这里就写一些关键代码。
service是最需要关注的地方,重要的逻辑一般都在这里;同时又有诸多的外部依赖,用这一层做实际mock的实例是最合适的。
/**
* @author zhangpeng34
* Created on 2019/1/18 下午9:15
**/
@Service
public class UserServiceImpl implements UserService {
@Autowired
UserDao userDao;
@Override
public User findUserById(Long id) {
return userDao.findById(id).orElse(null);
}
@Override
public User addUser(String name) {
if(StringUtils.isEmpty(name)){
return null;
}
User user = new User();
user.setName(name);
return userDao.save(user);
}
}
这个service很简单,这里针对里面的addUser方法写一些对应的单测:
/**
*
* 单元测试,测试的目的是对java代码逻辑进行测试。
* 单纯的逻辑测试,不应该加载外部依赖,所有的外部依赖应该mock掉,只关注本身逻辑。
* 例如,需要测试service层时,所依赖的dao等,应提前mock掉,设置好测试需要的输入和输出即可。
* dao层的逻辑应由dao层的测试保证,service层默认dao层是正确的。
**/
@RunWith(MockitoJUnitRunner.class)
public class UserServiceTests {
//mock注解创建一个被mock的实例
@Mock
UserDao userDao;
//InjectMocks代表创建一个实例,其他带mock注解的示例将被注入到该实例用。
//可以用该注解创建要被测试的实例,将实例所需的依赖用mock注解创建,即可mock掉依赖
@InjectMocks
UserServiceImpl UserServiceImpl;
String addUserName = "testAddUser";
/**
* 初始化时设置一些需要mock的方法和返回值
* 这里的设置表示碰到userDao的save方法,且参数为任一User类的实例时,返回提前预设的值
*/
@Before
public void init(){
User user =new User();
user.setId(1L);
user.setName(addUserName);
Mockito.when(userDao.save(any(User.class)))
.thenReturn(user);
}
//正向流程
@Test
public void testAddUser(){
User user = UserServiceImpl.addUser(addUserName);
Assert.assertEquals(addUserName,user.getName());
Assert.assertEquals(1L,user.getId().longValue());
}
//异常分支,name为null
@Test
public void testAddUserNull(){
User user = UserServiceImpl.addUser(null);
Assert.assertEquals(null,user);
}
//将各个分支都写出test和assert
//............
}
集成测试
上面所说的都是单元测试,但是实际开发中,我们往往不光需要单元测试(甚至不需要单元测试。。。。),还需要有集成测试,来测试我们程序的整体运行情况。
集成测试并不是联调,集成测试用依然可以mock第三方依赖。
在我们的工程里,一般只要实际启动整个spring容器的测试代码,都是集成测试。
Controller层是较为适合集成测试的地方,这里用Controller层来做集成测试的示例。
@RestController
public class UserController {
@Autowired
UserService userService;
@PostMapping(value = "/user")
public Object register(String name) {
return userService.addUser(name);
}
@GetMapping(value = "/user/{userId}")
public Object getUserInfo(@PathVariable Long userId) {
User user = userService.findUserById(userId);
if (user != null) {
return user;
}
return "fail";
}
@PostMapping(value = "/user/login")
public Object login(Long id, String pwd) {
User user = userService.findUserById(id);
if(user!=null){
return user;
}
return "fail";
}
}
下面写一个这次集成测试用的spring测试文件,位置如下:
测试代码:
配置文件如下:
spring.profiles=it
server.port=9898
spring.h2.console.enabled=true
spring.h2.console.path=/h2
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
spring.datasource.max-wait=10000
spring.datasource.max-active=5
spring.datasource.test-on-borrow=true
spring.datasource.test-while-idle = true
spring.datasource.validation-query = SELECT 1
# jpa
spring.jpa.hibernate.ddl-auto=update
#spring.jpa.hibernate.ddl-auto= create-drop
spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.show-sql=true
spring.jpa.generate-ddl=true
这个配置文件的主要作用就是将程序连接的DB换成一个内存数据库h2,这样就不用在测试时受DB的掣肘了。
@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("it")
@AutoConfigureMockMvc
public class UserControllerTests {
@Autowired
private MockMvc mvc;
@Autowired
UserDao userDao;
Long userId;
@Before
public void init(){
User user = new User();
user.setName("111");
userId = userDao.save(user).getId();
System.out.println(userId);
}
/**
* 测试/user/{userId}
* @throws Exception
*/
@Test
public void testGetUser() throws Exception {
//success
this.mvc.perform(get("/user/"+userId)).andExpect(status().isOk())
.andExpect(content().json("{ \"id\": "+userId+", \"name\": \"111\" }"));
//fail
this.mvc.perform(get("/user/"+(userId+100))).andExpect(status().isOk())
.andExpect(content().string("fail"));
}
/**
* 测试login
* @throws Exception
*/
@Test
public void exampleTest2() throws Exception {
//success
this.mvc.perform(post("/user/login").param("id",userId.toString()).param("pwd","11"))
.andExpect(status().isOk())
.andExpect(content().json("{ \"id\": "+userId+", \"name\": \"111\" }"));
//fail
this.mvc.perform(post("/user/login").param("id",userId.toString()+"11").param("pwd","11"))
.andExpect(status().isOk())
.andExpect(content().string("fail"));
}
}
@ActiveProfiles("it") 这个注解就是制定本测试代码加载的配置文件,”it“指 文件名 application-XX.properties 中间的xx,spring会自动根据名称去加载对应的配置文件。
init()?方法就是在内存数据库中构造自己需要的数据,这是集成测试最常见的步骤。
后面的测试代码就不需要解释太多了。
现在我也找了很多测试的朋友,做了一个分享技术的交流群,共享了很多我们收集的技术文档和视频教程。
如果你不想再体验自学时找不到资源,没人解答问题,坚持几天便放弃的感受
可以加入我们一起交流。而且还有很多在自动化,性能,安全,测试开发等等方面有一定建树的技术大牛
分享他们的经验,还会分享很多直播讲座和技术沙龙
可以免费学习!划重点!开源的!!!
qq群号:485187702【暗号:csdn11】
最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走! 希望能帮助到你!【100%无套路免费领取】