代码写到一半,之前的代码只需要完成功能且对其进行优化就完事了,就在这时,突然要我加上单元测试,崩溃啊……
我也只能把它啃了。
原谅我只做过简单Java程序的单元测试,是使用Junit去Test的。
现在要使用SpringBoot和Junit去弄,好吧,我也不太懂什么是单元测试,所以就看看吧:
摘自百度百科:
单元测试(模块测试)是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。
一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。
总的来说,就是为了后期做代码优化的时候,用来判断优化后的代码是否可以得到优化前的代码执行结果。如果可以得到,那就是代码优化的很好。如果得不到,那就是优化的代码存在问题,需要进一步的深入查找问题,并且解决。这就是进行单元测试的好处。
有点归总:
进行充分的单元测试,是提高软件质量,降低开发成本的必由之路。
对于程序员来说,如果养成了对自己写的代码进行单元测试的习惯,不但可以写出高质量的代码,而且还能提高编程水平。
要进行充分的单元测试,应专门编写测试代码,并与产品代码隔离。
1、它是一种验证行为。
程序中的每一项功能都是测试来验证它的正确性。它为以后的开发提供支援。就算是开发后期,我们也可以轻松的增加功能或更改程序结构,而不用担心这个过程中会破坏重要的东西。而且它为代码的重构提供了保障。这样,我们就可以更自由的对程序进行改进。
2、它是一种设计行为。
编写单元测试将使我们从调用者观察、思考。特别是先写测试(test-first),迫使我们把程序设计成易于调用和可测试的,即迫使我们解除软件中的耦合。
3、它是一种编写文档的行为。
单元测试是一种无价的文档,它是展示函数或类如何使用的最佳文档。这份文档是可编译、可运行的,并且它保持最新,永远与代码同步。
4、它具有回归性。
自动化的单元测试避免了代码出现回归,编写完成之后,可以随时随地的快速运行测试。
… …
使用的是spring boot + Junit;
下面我们来使用一个例子,查询用户信息:
@Service
public class Test1 {
@Autowired
private CheckCode checkCode;//数据校验
@Autowired
private Redis redis;//添加数据
@Autowired
private DaoService daoService;//dao层,,数据库访问层
//查询用户信息
public List<Object> query(String code){
boolean flag = checkCode.isFlag(code);//数据校验
List<Object> listData = new ArrayList<>();
if(flag){
listData = daoService.queryUserInfo();//访问数据库获取数据
}else{
//......
}
/**
*重要的代码逻辑
*/
//略
redis.set(JSON.toJSONString(listData));//添加数据到redis,无返回值
return listData;
}
}
我们正常情况下:运行query(String code)这个方法,都会调用到CheckCode、DaoService 这两个类或者接口的方法,对于数据校验和数据库访问,假如我们不用考虑其对代码的影响,那么我们的单元测试可以这么写
//忘了加单元测试的注解了
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = Application.class)
@WebAppConfiguration
public class Test2 {
@Autowired
private Test1 test;//这是我们一会要进行单元测试的类
//对两个不需要验证的类进行mock
@MockBean
private CheckCode checkCode;//数据校验
@MockBean
private DaoService daoService;//dao层,,数据库访问层
//对于不需要返回的任何值的类的所有方法,可以直接使用MockBean,直接忽略,如
@MockBean
private Redis redis;
//这样写,只要是使用到redis这个对象的地方,都不会执行。直接跳过
//第一种操作
//为方便使用,可以使用初始化形式mock
//公有的方法可以这样
@Before
public void init(){
//这句代码的意思是:当执行到checkCode.isFlag(Mockito.anyString())方法时,返回:true
//Mockito.anyString():任意一个String对象
//这中方式可以将返回的值和测试代码分开,我觉得比较美观
Mockito.when(checkCode.isFlag(Mockito.anyString())).thenReturn(true);
//也可以使用这种
//两种方式得到的结果是一样的
Mockito.when(checkCode.isFlag(Mockito.anyString())).thenAnswer(new Answer<boolean>() {
@Override
public boolean answer(InvocationOnMock invocation) throws Throwable {
//给出默认值
boolean flag = true;
//返回
return flag;
}});
//两种方法都是是的isFlag(String)这个方法返回默认的值:true。
//需要特别注意的是,不能带一个方法或者类mock两次及其以上
}
//单元测试的方法必须是公有的(public)、没有返回值的(void)、没有入参的()
@Test
public void test1(){
Mockito.when(daoService.queryUserInfo()).thenAnswer(new Answer<List<Object>>() {
@Override
public List<Object> answer(InvocationOnMock invocation) throws Throwable {
List<Object> listData = new ArrayList<>();
//对listData赋值
return listData;
}});
List<Object> data = test.query(anyString);//返回的值是上边的mock的默认值
Assert.assertEquals(arg1,arg2);//比较是否一致,为了判断和期望值是否一致
}
}
这样应该就可以完成一个简单的单元测试了,这些都只是伪代码,其实单元测试有很多的方法,也蛮有意思的,就是刚开始比较花时间。
其他的,后期有时间再写详细一点的。