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

本文介绍了如何使用TestNG、Mockito和Spring MVC Test Framework进行集成测试。通过自动装配注解(@Autowired),无需手动注入bean或使用Mockito的实例化方式。文章通过一个具体的UserService和UserController的例子,展示了如何进行Controller与Service层的测试,包括定义请求响应类、创建Controller逻辑和测试基类,以及如何设置和验证预期响应。
摘要由CSDN通过智能技术生成

本文主要总结自己近期在项目中对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代码 a4c26d1e5885305701be709a3d33442f.png

public interface UserService {

User getUserInfo(long id);

int updateUserInfo(User user);

}

Java代码 a4c26d1e5885305701be709a3d33442f.png

public interface UserQueryService {

String getUserName(long userId);

int updateUserName(long userId, String userName);

}

Java代码 a4c26d1e5885305701be709a3d33442f.png

@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代码 a4c26d1e5885305701be709a3d33442f.png

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

public class BaseRequest {

}

Java代码 a4c26d1e5885305701be709a3d33442f.png

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代码 a4c26d1e5885305701be709a3d33442f.png

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代码 a4c26d1e5885305701be709a3d33442f.png

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代码 a4c26d1e5885305701be709a3d33442f.png

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代码 a4c26d1e5885305701be709a3d33442f.png

@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代码 a4c26d1e5885305701be709a3d33442f.png

@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代码 a4c26d1e5885305701be709a3d33442f.png

@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代码 a4c26d1e5885305701be709a3d33442f.png

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代码 a4c26d1e5885305701be709a3d33442f.png

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代码 a4c26d1e5885305701be709a3d33442f.png

分享:

a4c26d1e5885305701be709a3d33442f.png喜欢

0

a4c26d1e5885305701be709a3d33442f.png赠金笔

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

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

发评论

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

昵   称:

发评论

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值