本文主要总结自己近期在项目中对MVC集成测试的实践及理解,因为先前对这块未实践过。主要参考了官方文档《11.3.6

Spring MVC Test Framework》这一章节内容,涉及到Spring

TestContext Framework、TestNG和Mockito这3个测试框架,完全基于Spring自动装配注解(@Autowired)实现,不需要定义额外的setter或构造器来注入bean,也不需要通过Mockito的@Mock和MockitoAnnotations.initMocks(this)代码方式实现实例化,而是通过静态工厂方法Mockito.mock(...)在XML中实现bean实例初始化。

废话不多说了,看一下需要几步就能搞定MVC Controller与Service层的集成测试。(如果你现在也正好使用Spring

Test框架,可以看看下面对TestNG基类封装的代码,我觉得自己写得还可以。小小赞美一下啦~)

1. 定义底层Service接口及实现

Java代码 

public interface UserService {

User getUserInfo(long id);

int updateUserInfo(User user);

}

Java代码 

public interface UserQueryService {

String getUserName(long userId);

int updateUserName(long userId, String userName);

}

Java代码 

@Service

public class UserQueryServiceImpl implements UserQueryService {

@Autowired

private UserService userService;

@Override

public String getUserName(long userId) {

User user = this.userService.getUserInfo(userId);

return user != null ? user.getName() : "";

}

@Override

public int updateUserName(long userId, String userName) {

User user = new User(userId, userName);

int udpateResult = this.userService.updateUserInfo(user);

return udpateResult;

}

}

2. 为 Controller 层的每一接口定义一对

Request与Response(可重用的,就别多定义啦!~\(≧▽≦)/~)

Java代码 

@JsonIgnoreProperties(ignoreUnknown = true) // 忽略多传的参数

public class BaseRequest {

}

Java代码 

public class UserIDRequest extends BaseRequest {

@JsonProperty("id")

@NotNull(message = "id param is null")

@Min(value = 1, message = "id param must be great or equal than \\{{value}\\}") // 4.3. Message interpolation -《JSR 303: Bean Validation》

protected long id;

public long getId() {

return id;

}

public void setId(long id) {

this.id = id;

}

@Override

public String toString() {

return "UserIDRequest [id=" + id + "]";

}

}

Java代码 

public class UserNameResponse {

@JsonProperty("name")

protected String name = "";

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

@Override

public String toString() {

return "UserNameResponse [name=" + name + "]";

}

}

Java代码 

public class UserInfoRequest extends UserIDRequest {

@JsonProperty("name")

@NotNull(message = "name param is null")

@Size(min = 1, message = "name param is empty")

protected String userName; // 变量名与请求参数名不一样,在@RequestBody中用到

public String getUserName() {

return userName;

}

public void setUserName(String userName) {

this.userName = userName;

}

@Override

public String toString() {

return "UserInfoRequest [userName=" + userName + ", id=" + id + "]";

}

}

Java代码 

public class UserResultResponse {

@JsonProperty("ret")

protected int result;

@JsonProperty("ret_msg")

protected String resultMessage;

public UserResultResponse() {

this.result = 0;

this.resultMessage = "ok";

}

public int getResult() {

return result;

}

public void setResult(int result) {

this.result = result;

}

public String getResultMessage() {

return resultMessage;

}

public void setResultMessage(String resultMessage) {

this.resultMessage = resultMessage;

}

@Override

public String toString() {

return "UserResultResponse [result=" + result + ", resultMessage="

+ resultMessage + "]";

}

}

3. 实现 Controller 层逻辑

Java代码 

@Controller

@RequestMapping(value = "/user")

public class UserController {

@Autowired

private UserQueryService userQueryService;

@RequestMapping(value = "/get_user_name", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)

@ResponseBody

public UserNameResponse getUserName(@Valid UserIDRequest userIDRequest) {

long userId = userIDRequest.getId();

String userName = this.userQueryService.getUserName(userId);

UserNameResponse response = new UserNameResponse();

if (!StringUtils.isEmpty(userName)) {

response.setName(userName);

}

return response;

}

@RequestMapping(value = "/update_user_name", method = RequestMethod.POST,

consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)

@ResponseBody

public UserResultResponse updateUserName(@Valid @RequestBody UserInfoRequest userInfoRequest) { // JSON request body map

UserResultResponse response = new UserResultResponse();

long userId = userInfoRequest.getId();

String userName = userInfoRequest.getUserName();

int result = this.userQueryService.updateUserName(userId, userName);

if (result

response.setResult(result);

response.setResultMessage("update operation is fail");

}

return response;

}

}

4. 实现一个Service与Controller层的抽象测试基类(用于集成TestNG与MVC

Test框架,且自动加载配置文件)

Java代码 

@ContextConfiguration("classpath:META-INF/spring/test-context.xml") // 集成应用上下文并加载默认的beans XML配置

public abstract class AbstractTestNGTest extends AbstractTestNGSpringContextTests { // 集成TestNG

@BeforeSuite(alwaysRun = true)

public void init() {

// MockitoAnnotations.initMocks(this); // 基于Spring自动装配注解,这里不再需要初始化

}

}

Java代码 

@WebAppConfiguration("src/test/java") // 集成Web应用上下文

public abstract class AbstractControllerTestNGTest extends AbstractTestNGTest {

protected MockMvc mockMvc;

protected abstract Object getController();

@BeforeClass(alwaysRun = true)

public void setup() {

this.mockMvc = MockMvcBuilders.standaloneSetup(this.getController()).build();

}

protected void getMock(String url, Object[] params, String expectedContent) throws Exception {

// 2. 构造GET请求

MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders

.get(url, params);

this.jsonRequestMock(requestBuilder, expectedContent);

}

protected void postMock(String url, String paramsJson, String expectedContent) throws Exception {

// 2. 构造POST请求

MockHttpServletRequestBuilder requestBuilder = MockMvcRequestBuilders

.post(url)

.content(paramsJson) // 设置request body请求体,服务于"@RequestBody"

;

this.jsonRequestMock(requestBuilder, expectedContent);

}

private void jsonRequestMock(MockHttpServletRequestBuilder requestBuilder, String expectedContent) throws Exception {

// 2. 设置HTTP请求属性

requestBuilder.contentType(MediaType.APPLICATION_JSON)

.accept(MediaType.APPLICATION_JSON)

.characterEncoding(CharEncoding.UTF_8)

;

// 3. 定义期望响应行为

this.mockMvc.perform(requestBuilder)

.andDo(print()) // 打印整个请求与响应细节

.andExpect(status().isOk())

.andExpect(content().contentType(MediaType.APPLICATION_JSON))

.andExpect(content().string(expectedContent)) // 校验是否是期望的结果

;

}

}

5. 实现Controller与Service层的测试逻辑

Java代码 

public class UserControllerTest extends AbstractControllerTestNGTest {

// tested controller

@Autowired

private UserController userControllerTest;

// mocked service (被依赖的服务)

@Autowired

private UserQueryService userQueryService;

@Test(dataProvider = "getUserName")

public void getUserName(Object[] params, String userName, String expectedContent) throws Exception {

// 1. 定义"被依赖的服务"的方法行为

when(this.userQueryService.getUserName(anyLong())).thenReturn(userName);

this.getMock("/user/get_user_name?id={id}", params, expectedContent);

}

@DataProvider(name = "getUserName")

protected static final Object[][] getUserNameTestData() {

Object[][] testData = new Object[][] {

{ new Object[] { "23" }, "Bert Lee", "{"name":"Bert Lee"}" },

};

return testData;

}

@Test(dataProvider = "updateUserName")

public void updateUserName(String paramsJson, Integer result, String expectedContent) throws Exception {

// 1. 定义"被依赖的服务"的方法行为

when(this.userQueryService.updateUserName(anyLong(), anyString())).thenReturn(result);

this.postMock("/user/update_user_name", paramsJson, expectedContent);

}

@DataProvider(name = "updateUserName")

protected static final Object[][] updateUserNameTestData() {

Object[][] testData = new Object[][] {

{ "{"id":23,"name":"Bert Lee"}", 0, "{"ret":0,"ret_msg":"ok"}" },

};

return testData;

}

@Override

public Object getController() {

return this.userControllerTest;

}

}

Java代码 

public class UserQueryServiceTest extends AbstractTestNGTest {

// tested service

@Autowired

private UserQueryService userQueryServiceTest;

// mocked service (被依赖的服务)

@Autowired

private UserService userService;

@Test(dataProvider = "getUserName")

public void getUserName(User user, String expected) {

// 1. 定义"被依赖的服务"的方法行为

when(userService.getUserInfo(anyLong())).thenReturn(user);

String userName = this.userQueryServiceTest.getUserName(3L);

assertEquals(userName, expected);

}

@DataProvider(name = "getUserName")

protected static final Object[][] getUserNameTestData() {

Object[][] testData = new Object[][] {

{ null, "" },

{ new User(3L, ""), "" },

{ new User(10L, "Edward Lee"), "Edward Lee" },

{ new User(23L, "李华刚@!~#$%^&"), "李华刚@!~#$%^&" },

};

return testData;

}

}

6. 定义XML bean配置文件,实现测试对象及被依赖的服务的自动注入

Xml代码 

分享:

喜欢

0

赠金笔

加载中,请稍候......

评论加载中,请稍候...

发评论

登录名: 密码: 找回密码 注册记住登录状态

昵   称:

发评论

以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

java集成测试_基于TestNG+Mockito及自动装配注解的Spring MVC集成测试相关推荐

  1. java照片墙_基于jQuery实现照片墙自动播放特效

    一个动态展示图片的页面: 功能:定时从后台取数据,进行页面图片追加.对已经在页面中的图片,进行放大缩小动画展示.目前我们用于微信新关注用户头像展示. 抽奖 body{ background-color ...

  2. Spring注解驱动开发学习总结8:自动装配注解@Autowire、@Resource、@Inject

    Spring注解驱动开发学习总结8:自动装配注解@Autowire.@Resource.@Inject 1.自动装配@Autowire.@Resource.@Inject 1.1 构建bookDao ...

  3. Spring自动装配注解

    Spring自动装配注解 注解 来源 特点与区别 备注 @Autowired Spring定义 Spring提供自动装配注解 @Autowired详解 @Resource(JSR250) java注解 ...

  4. Spring MVC集成测试

    在Spring MVC中对控制器进行集成测试的一种方法是使用Spring提供的集成测试支持. 对于Junit4,此支持包括一个名为SpringJunit4ClassRunner的自定义Junit Ru ...

  5. Spring自动装配----注解装配----Spring自带的@Autowired注解

    Spring自动装配----注解装配----Spring自带的@Autowired注解 父类 package cn.ychx;public interface Person {public void ...

  6. 用java写ods系统_基于数据库的代码自动生成工具,生成JavaBean、生成数据库文档、生成前后端代码等(TableGo v7.0.0版)...

    TableGo是基于数据库的代码自动生成工具,低代码编程技术的实现,可以零代码自动生成SpringBoot项目工程.生成JavaBean.生成前后端分离的CRUD代码.生成MyBaits的Mapper ...

  7. java生成iso9660工具_基于数据库的代码自动生成工具,生成JavaBean、生成数据库文档、生成前后端代码等(TableGo v7.0.0版)...

    TableGo_20210212 v7.0.0 正式版发布,此次版本更新如下: 1.新增对DB2数据库的支持 2.新增按字段生成文件,支持把字段.JSON.XML数据转换成任何代码 3.新增大量新的自 ...

  8. java如何实现计费软件_基于jsp的网吧自动计费收费管理系统-JavaEE实现网吧自动计费收费管理系统 - java项目源码...

    基于jsp+servlet+pojo+mysql实现一个javaee/javaweb的网吧自动计费收费管理系统, 该项目可用各类java课程设计大作业中, 网吧自动计费收费管理系统的系统架构分为前后台 ...

  9. mockito模拟依赖注入_使用Mockito模拟自动装配的字段

    mockito模拟依赖注入 依赖注入是诸如Spring和EJB之类的Control容器反转的非常强大的功能. 将注入的值封装到私有字段中总是一个好主意. 但是,自动连线字段的封装会降低可测试性. 我喜 ...

最新文章

  1. UNICODE 码和字符的相互转换
  2. <文献阅读>用转移熵通过微阵列的时间序列推断基因调控网络(inferring gene regulatory networks from microarray time series data
  3. 在CentOS上安装Python3的三种方法
  4. SEO网站优化团队官网模板
  5. Java(19)JDBC
  6. 【java】visualvm 插件 visual gc 使用介绍
  7. ajax live search,AJAX Live Search
  8. 高度等于动态宽度(CSS流体布局)
  9. C语言计算总成绩和平均成绩
  10. Vue系列:通过vue-router如何传递参数
  11. 神器!Alibaba Sentinel,功能真心强大!
  12. 线性代数学习之行列式
  13. python链家数据分析_练习—利用Python对链家网广州二手房进行数据分析
  14. win10键盘失效问题
  15. 常用计量单位及其换算
  16. oppo手机禁止自动打开应用商店
  17. WIFI芯片调试APP Wif网络精灵
  18. fastqc检验时不能执行java_fastqc对原始测序reads质控
  19. 【WLAN】【测试】WLAN相关测试软件一览
  20. linux系统getopt函数详解

热门文章

  1. 电竞高性能主机可以改云服务器吗,无缘主机平台 NBA 2K League电竞联赛将改用高性能PC...
  2. pb调用键盘钩子的例子_搞不动Vue3.0的源码,先做个API调用师也行(新人踩坑初试)...
  3. 拖放获取文件信息的bat代码
  4. manacher(马拉车)算法详解
  5. 纯JS前端分页方法(JS分页)
  6. 值类型和引用类型及其区别!
  7. [BZOJ3676][Apio2014]回文串
  8. pdf转换成word教程
  9. 如何在excel 单元格中增加换行
  10. Google认证的SketchUp模型网站