TDD
测试驱动开发的基本思想就是在开发功能代码之前,先编写测试代码。也就是说在明确要开发某个功能后,首先思考如何对这个功能进行测试,并完成测试代码的编写,然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能,直到完全部功能的开发。
测试驱动开发的基本过程如下:
1) 明确当前要完成的功能。可以记录成一个 TODO 列表。
2) 快速完成针对此功能的测试用例编写。
3) 测试代码编译或执行不通过。
4) 编写对应的功能代码。
5) 测试通过。
6) 对代码进行重构,并保证测试通过。
7) 循环完成所有功能的开发。
TODO
假设在实际开发中有一个用户模块,该模块包括了用户的添加、查询、列表展示等功能。针对待开发的功能先做一个todo列表。
1)点击添加按钮,根据输入的信息添加一个用户信息,用户名存在提示异常,否则添加成功;
2)根据用户名,查询用户信息,返回用户名,用户昵称,用户职业,年龄与性别;
3)管理员权限的用户可以查看所有的用户信息;
Modules
为了实现上述功能点,首先选择开发框架。本次演示用基础开发框架为Spring Boot。项目管理使用的为maven。项目中用到模块之间的依赖关系为tdd-web->tdd-service->tdd-repo->tdd-domain->tdd-api。
TDD-Test Case
在设计测试用例时,用到的测试框架为spring-boot-starter-test。根据之前的todo列表。笔者设计了三个测试用例。分别用来测试UserController的add、query、list三个方法。写到这里,已经完成了tdd里面的第二步,根据需求设计测试用例。用例设计完成后,我们先执行,看看效果。三个用例都执行失败因为此时笔者还没有实现任何逻辑代码。
package com.mc.tdd.web.web.user;
import com.mc.tdd.web.TddApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.awt.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author M.C
* @description UserControllerTest
* @date 2019-03-10 10:45
**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TddApplication.class)
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
MockMvc mockMvc;
@Test
public void testUserAdd() throws Exception {
MvcResult mvcResult =mockMvc.perform(post("/user/add")
.param("userName","tdd")
.param("userNickname","mc")
.param("gender","male")
.param("age","25")
.param("occupation","spring boot developer")
).andExpect(status().isCreated()).andReturn();
}
@Test
public void testUserQuery() throws Exception {
mockMvc.perform(get("/user/get/{userName}","tdd")
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.userName").isNotEmpty());
}
@Test
public void testUserList() throws Exception {
mockMvc.perform(get("/user/list")
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.users").exists())
.andExpect(MockMvcResultMatchers.jsonPath("$.users[*].userName").isNotEmpty());
}
}
TDD-Web
此时我们简单的浏览一下tdd-web模块里面的内容。TddApplication 为Spring Boot的入口类。UserControllerTest为之UserController的测试类。接下来,笔者将分别实现用户的添加,查询和列表显示功能在web模块中的代码以及依赖接口的空实现。
UserDto类,主要用来接收web端传入的参数。
package com.mc.tdd.domain.user;
/**
* @author M.C
* @description UserDto
* @date 2019-03-10 10:40
**/
public class UserDto {
private String userName;
private String userNickName;
private Integer age;
private String gender;
private String occupation;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getUserNickName() {
return userNickName;
}
public void setUserNickName(String userNickName) {
this.userNickName = userNickName;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public String getOccupation() {
return occupation;
}
public void setOccupation(String occupation) {
this.occupation = occupation;
}
}
Response类,用于给web端进行交互使用。
package com.mc.tdd.domain.response;
import java.io.Serializable;
/**
* @author M.C
* @description Response
* @date 2019-03-10 12:43
**/
public class Response implements Serializable {
private int code ;
private String message;
private Object data;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
UserService接口,定义相关的操作。
package com.mc.tdd.api.user;
import com.mc.tdd.domain.user.UserDto;
import java.util.List;
import java.util.Optional;
/**
* @author M.C
* @description UserService
* @date 2019-03-10 9:02
**/
public interface UserService {
public Optional<UserDto> add(UserDto userDto);
public Optional<UserDto> query(String userName);
public Optional<List<UserDto>> list();
}
UserServiceImpl的接口实现类。目前都是接口的空实现。
package com.mc.tdd.service.user.impl;
import com.mc.tdd.api.user.UserService;
import com.mc.tdd.domain.user.UserDto;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;
/**
* @author M.C
* @description UserServiceImpl
* @date 2019-03-10 9:14
**/
@Service
public class UserServiceImpl implements UserService {
@Override
public Optional<UserDto> add(UserDto userDto) {
return null;
}
@Override
public Optional<UserDto> query(String userName) {
return null;
}
@Override
public Optional<List<UserDto>> list() {
return null;
}
}
UserController的实现代码
package com.mc.tdd.web.controller;
import com.mc.tdd.api.user.UserService;
import com.mc.tdd.domain.response.Response;
import com.mc.tdd.domain.user.UserDto;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;
/**
* @author M.C
* @description UserController
* @date 2019-03-10 12:14
**/
@Controller
@RequestMapping("/user")
public class UserController {
@Resource
UserService userService;
@PostMapping("/add")
@ResponseBody
public Response add(UserDto userDto){
Response response = new Response();
Optional<UserDto> result = userService.add(userDto);
response.setData(result.get());
return response;
}
@GetMapping("/get")
@ResponseBody
public Response query(@PathVariable("userName") String userName){
Response response = new Response();
Optional<UserDto> result = userService.query(userName);
response.setData(result.get());
return response;
}
@GetMapping("/list")
@ResponseBody
public Response list(){
Response response = new Response();
Optional<List<UserDto>> result =userService.list();
response.setData(result.get());
return response;
}
}
当controller代码设计完毕后,再次执行之前的测试用例。结果还是失败。因为UserController依赖service模块的UserService接口。所以如果测试该controller的话,需要将UserService接口进行mock处理。
Mock Service
由于UserController需要使用UserService,不过此时UserService目前还未实现。因此在进行UserController测试时,需要将UserService做mock,使用到的注解为@MockBean。
改造后的UserControllerTest类实现如下
package com.mc.tdd.web.web.user;
import com.mc.tdd.api.user.UserService;
import com.mc.tdd.domain.user.UserDto;
import com.mc.tdd.web.TddApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import java.util.List;
import java.util.ArrayList;
import java.util.Optional;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author M.C
* @description UserControllerTest
* @date 2019-03-10 10:45
**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TddApplication.class)
@AutoConfigureMockMvc
public class UserControllerTest {
@Autowired
MockMvc mockMvc;
@MockBean
UserService userService;
@Test
public void testUserAdd() throws Exception {
UserDto userDto = new UserDto();
userDto.setUserName("tdd");
userDto.setUserNickName("mc");
userDto.setGender("male");
userDto.setAge(29);
userDto.setOccupation("spring boot developer");
Optional<UserDto> mockUserDto = Optional.of(userDto);
Mockito.when(userService.add(Mockito.any(UserDto.class))).thenReturn(mockUserDto);
MvcResult mvcResult = mockMvc.perform(post("/user/add")
.param("userName","tdd")
.param("userNickname","mc")
.param("gender","male")
.param("age","25")
.param("occupation","spring boot developer"))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("0"))
.andExpect(MockMvcResultMatchers.jsonPath("$.data.userName").value("tdd"))
.andReturn();
}
@Test
public void testUserQuery() throws Exception {
UserDto userDto = new UserDto();
userDto.setUserName("tdd");
userDto.setUserNickName("mc");
userDto.setGender("male");
userDto.setAge(29);
userDto.setOccupation("spring boot developer");
Optional<UserDto> mockUserDto = Optional.of(userDto);
Mockito.when(userService.query(Mockito.anyString())).thenReturn(mockUserDto);
MvcResult mvcResult = mockMvc.perform(get("/user/query/{userName}","tdd"))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("0"))
.andExpect(MockMvcResultMatchers.jsonPath("$.data.userName").value("tdd"))
.andReturn();
}
@Test
public void testUserList() throws Exception {
UserDto userDto = new UserDto();
userDto.setUserName("tdd");
userDto.setUserNickName("mc");
userDto.setGender("male");
userDto.setAge(29);
userDto.setOccupation("spring boot developer");
List<UserDto> userDtoList = new ArrayList<>();
userDtoList.add(userDto);
Optional<List<UserDto>> mockUserDtoList = Optional.of(userDtoList);
Mockito.when(userService.list()).thenReturn(mockUserDtoList);
MvcResult mvcResult = mockMvc.perform(get("/user/list")
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("0"))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[0].userName").value("tdd"))
.andReturn();
}
}
执行该测试类,检查实现的功能是否符合预期。其中两个用例执行成功,一个失败。失败的原因为404及请求的url未找到。这时候我们具体排查一下失败的原因:请求的url为get("/user/query/{userName}。但是UserController里面定义方法的@GetMapping("/query")。所以这次失败的原因为代码实现的bug。修改代码为@GetMapping("/query/{userName}")。解决该bug后重新执行测试用例。
到此,我们的UserController中的功能就算基本开发完成了。看到这里是不是觉得TDD根本没有想象中的那么难呢?
Mock-JPA
接下来我们开发UserService中定义的接口。因为我们采用的是TDD的思想。所以第一步还是创建测试用例。由于该接口依赖于UserRepo,所以在该测试类中添加mock。将未做实现的UserRepo做mock处理。
在做mock之前,先浏览一下UserRepo中的接口定义。
package com.mc.tdd.repo.user;
import com.mc.tdd.domain.user.User;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.List;
/**
* @author M.C
* @description UserRepo
* @date 2019-03-10 15:36
**/
public interface UserRepo extends JpaRepository<User,Long> {
public User saveDto(User user);
public User queryDto(String userName);
public List<User> listDto();
}
结合UserRepo的接口定义与UserService的功能,最终设计的测试用例如下。
package com.mc.tdd.api.user;
import com.mc.tdd.domain.user.User;
import com.mc.tdd.domain.user.UserDto;
import com.mc.tdd.repo.user.UserRepo;
import com.mc.tdd.web.TddApplication;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* @author M.C
* @description UserServiceTest
* @date 2019-03-10 15:19
**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TddApplication.class)
public class UserServiceTest {
@Resource
UserService userService;
@MockBean
UserRepo userRepo;
@Test
public void testUserAdd(){
User user = new User();
user.setUserName("tdd");
user.setUserNickName("mc");
user.setGender("male");
user.setAge(29);
user.setOccupation("spring boot developer");
Mockito.when(userRepo.saveDto(Mockito.any(User.class))).thenReturn(user);
UserDto userDto = new UserDto();
userDto.setUserName("tdd");
userDto.setUserNickName("mc");
userDto.setGender("male");
userDto.setAge(29);
userDto.setOccupation("spring boot developer");
Optional<UserDto> result = userService.add(userDto);
Assert.assertEquals(userDto.getUserName(),result.get().getUserName());
}
@Test
public void testUserQuery(){
User user = new User();
user.setUserName("tdd");
user.setUserNickName("mc");
user.setGender("male");
user.setAge(29);
user.setOccupation("spring boot developer");
Mockito.when(userRepo.queryDto(Mockito.anyString())).thenReturn(user);
Optional<UserDto> result = userService.query("tdd");
Assert.assertEquals("tdd",result.get().getUserName());
}
@Test
public void testUserList(){
User user = new User();
user.setUserName("tdd");
user.setUserNickName("mc");
user.setGender("male");
user.setAge(29);
user.setOccupation("spring boot developer");
List<User> userList = new ArrayList<>();
userList.add(user);
Mockito.when(userRepo.listDto()).thenReturn(userList);
Optional<List<UserDto>> result = userService.list();
Assert.assertEquals(Boolean.TRUE.booleanValue(),result.get().size()>0);
}
}
执行完毕测试用例后,笔者发现其中一个测试用例执行失败。userService.query("tdd"); 期望结果中的userName为tdd,但是接口实际返回的却是mc。其中mc这个值为userNickName。排查代码发现是因为给userName赋值时错误的将userNickName赋给了userName。userDtoTemp.setUserName(result.getUserNickName()); 解决完该bug后,重新执行测试用例。
为了方便代码的阅读,此处将UserServiceImpl最终的实现代码粘贴出来。
package com.mc.tdd.service.user.impl;
import com.mc.tdd.api.user.UserService;
import com.mc.tdd.domain.user.User;
import com.mc.tdd.domain.user.UserDto;
import com.mc.tdd.repo.user.UserRepo;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
/**
* @author M.C
* @description UserServiceImpl
* @date 2019-03-10 9:14
**/
@Service
public class UserServiceImpl implements UserService {
@Resource
UserRepo userRepo;
@Override
public Optional<UserDto> add(UserDto userDto) {
User user = new User();
user.setUserName(userDto.getUserName());
user.setUserNickName(userDto.getUserNickName());
user.setAge(userDto.getAge());
user.setGender(userDto.getGender());
user.setOccupation(userDto.getOccupation());
User result = userRepo.saveDto(user);
UserDto userDtoTemp = new UserDto();
userDtoTemp.setUserName(result.getUserName());
userDtoTemp.setUserNickName(result.getUserNickName());
userDtoTemp.setAge(result.getAge());
userDtoTemp.setGender(result.getGender());
userDtoTemp.setOccupation(result.getOccupation());
return Optional.of(userDtoTemp);
}
@Override
public Optional<UserDto> query(String userName) {
User result = userRepo.queryDto(userName);
UserDto userDtoTemp = new UserDto();
userDtoTemp.setUserName(result.getUserName());
userDtoTemp.setUserNickName(result.getUserNickName());
userDtoTemp.setAge(result.getAge());
userDtoTemp.setGender(result.getGender());
userDtoTemp.setOccupation(result.getOccupation());
return Optional.of(userDtoTemp);
}
@Override
public Optional<List<UserDto>> list() {
List<User> userList = userRepo.listDto();
List<UserDto> userDtoList = new ArrayList<>();
userList.forEach( result -> {
UserDto userDtoTemp = new UserDto();
userDtoTemp.setUserName(result.getUserName());
userDtoTemp.setUserNickName(result.getUserNickName());
userDtoTemp.setAge(result.getAge());
userDtoTemp.setGender(result.getGender());
userDtoTemp.setOccupation(result.getOccupation());
userDtoList.add(userDtoTemp);
});
return Optional.of(userDtoList);
}
}
JPA-Test
目前就剩下UserRepo的类未实现。所以本章节重点介绍JPA相关的实现与测试。根据之前的思路。笔者还是先设计测试用例。
package com.mc.tdd.repo.user;
import com.mc.tdd.domain.user.User;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;
import javax.annotation.Resource;
import java.util.List;
/**
* @author M.C
* @description UserRepoTest
* @date 2019-03-11 16:31
**/
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepoTest {
@Autowired
private TestEntityManager entityManager;
@Resource
UserRepo userRepo;
@Test
public void testUserAdd(){
User user = new User();
user.setUserName("tdd");
user.setUserNickName("mc");
user.setGender("male");
user.setAge(29);
user.setOccupation("spring boot developer");
User result = userRepo.saveDto(user.getUserName(),user.getUserNickName(),user.getAge(),user.getGender(),user.getOccupation());
Assert.assertEquals("tdd",result.getUserName());
}
@Test
public void testUserQuery(){
User user = new User();
user.setUserName("tdd");
user.setUserNickName("mc");
user.setGender("male");
user.setAge(29);
user.setOccupation("spring boot developer");
entityManager.persist(user);
User result = userRepo.queryDto("tdd");
Assert.assertEquals("tdd",result.getUserName());
}
@Test
public void testUserList(){
User user = new User();
user.setUserName("tdd");
user.setUserNickName("mc");
user.setGender("male");
user.setAge(29);
user.setOccupation("spring boot developer");
entityManager.persist(user);
List<User> result = userRepo.listDto();
Assert.assertEquals(Boolean.TRUE.booleanValue(),result.size()>0);
}
}
执行之后查看失败原因。因为目前位置演示用的JPA类中还没有做任何的实现。同时在接口定义时,没有使用JPA默认的方法命名规则。所以jpa会认为我们设计的接口是错误的。根据错误提示。实现具体的jpa功能。
实现后的jpa类
package com.mc.tdd.repo.user;
import com.mc.tdd.domain.user.User;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.List;
/**
* @author M.C
* @description UserRepo
* @date 2019-03-10 15:36
**/
public interface UserRepo extends JpaRepository<User,Long> {
@Query(value = "insert into tdd_user(user_name,user_nick_name,user_age,user_gender,user_occupation) value (:userName,:userNickName,:age,:gender,:occupation)",nativeQuery = true)
public User saveDto(@Param("userName") String userName,@Param("userNickName") String userNickName,@Param("age") Integer age,@Param("gender") String gender,@Param("occupation") String occupation);
@Query(value = "select * from tdd_user as tddUser where tddUser.user_name = :userName",nativeQuery = true)
public User queryDto(@Param("userName") String userName);
@Query(value = "select * from tdd_user",nativeQuery = true)
public List<User> listDto();
}
再次执行之前的测试用例,发现有一个用例执行失败,失败原因为sql异常。排查失败原因,是因为笔者将 sql的 values错误的写成了value。少些了一个s。再次执行查看结果发现还是有异常Method is only allowed for a query。只要有异常就继续排查。失败原因是为insert或update操作需要显示声明为@Modify。
调试到这里,读者是不是在想总算可以松口气了。因为所有的单元测试用例都已经pass了。笔者认为,目前只是完成了一些基本的单元测试。接下来要做的是集成测试。把之前实现的模块集成到一起进行测试。
Integration Testing
TDD不仅适用于单元测试阶段,集成测试阶段也同样适用。这时候的测试用例可以充分参考之前的设计场景来进行测试。在测试用例设计过程中,需要注意的是,接口之间的调用就要用实际的接口而不能使用mock接口了。
我们的集成测试用例设计如下。
package com.mc.tdd.web.web.user;
import com.mc.tdd.web.TddApplication;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
/**
* @author M.C
* @description UserControllerTest
* @date 2019-03-10 10:45
**/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = TddApplication.class)
@AutoConfigureMockMvc
public class UserControllerIntegrationTest {
@Autowired
MockMvc mockMvc;
@Test
public void testUserAdd() throws Exception {
MvcResult mvcResult = mockMvc.perform(post("/user/add")
.param("userName","tdd")
.param("userNickname","mc")
.param("gender","male")
.param("age","25")
.param("occupation","spring boot developer"))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("0"))
.andExpect(MockMvcResultMatchers.jsonPath("$.data").value("1"))
.andReturn();
}
@Test
public void testUserQuery() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/user/query/{userName}","tdd"))
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("0"))
.andExpect(MockMvcResultMatchers.jsonPath("$.data.userName").value("tdd"))
.andReturn();
}
@Test
public void testUserList() throws Exception {
MvcResult mvcResult = mockMvc.perform(get("/user/list")
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk())
.andExpect(MockMvcResultMatchers.jsonPath("$.code").value("0"))
.andExpect(MockMvcResultMatchers.jsonPath("$.data[0].userName").value("tdd"))
.andReturn();
}
}
针对执行过程中遇到的异常逐个分析排查。UserRepo注入异常,是因为spring boot 在扫描时,没有将UserRepo进行初始化并交给Spring容器来托管。问题修复方法添加 @EnableJpaRepositories(basePackages={"com.mc.tdd.repo"})。
Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query。失败原因是update或delete或insert相关的操作需要添加事物声明。
TDD
关于测试驱动开发的相关理论知识,感觉这篇文章写的还是不错的。浅谈测试驱动开发(TDD)。如果读者感兴趣可以查看该文章进一步了解。本文的侧重点是从实战的角度来进一步的体会什么是TDD。以及在实际项目中如何运用TDD。如有不足之处还请各位读者指正。