目录
- 第一章、快速了解JUnit单元测试
- 1.1)单元测试是什么
- 1.2)为什么使用JUnit单元测试
- 第二章、快速使用JUnit5框架
- 2.1)在pom文件中导入依赖
- 2.2)新建测试类
- 2.3)新建一个简单的测试方法
- 第三章、测试框架提供的注解和方法介绍
- 3.1)注解
- 3.1.1)注释测试类
- 3.1.2)注释成员变量
- 3.2)方法
- 3.2.1)Mockito方法
- 3.2.2)Assert结果断言、verify行为验证
- 3.3)
- 第四章、使用Mockito的实例
- 4.1)需要测试的方法
- 4.2)测试上面的方法
- 4.3)
友情提醒:
先看文章目录,大致了解文章知识点结构,点击文章目录可直接跳转到文章指定位置。
三连提问:单元测试是什么?springboot中怎么使用单元测试 ?工作中怎么写单元测试?
第一章、快速了解JUnit单元测试
1.1)单元测试是什么
单元测试:针对方法(最小功能单元)的测试,由程序员自己写。
JUnit是什么:一种帮助我们编写测试的标准测试框架。
1.2)为什么使用JUnit单元测试
不使用单元测试时,我们测试一个方法能否成功运行,我们会在主方法里调用这个方法,看看控制台的运行结果。
public class Test {
public static void main(String[] args) {
System.out.println(Test(1));
}
public static int Test(int i){
return ++i;
}
}
或者更直接的把需要测试代码放到主方法里,看能不能运行成功。
public class Test {
public static void main(String[] args) {
System.out.println("test");
}
}
局限性:但是使用这种方式测试代码局限性很大,比如只有一个main方法无法有组织有层次的测试不同的方法。比如测试的输出结果不够直观。所以我们需要专门的JUnit来编写单元测试
第二章、快速使用JUnit5框架
IDE工具为IDEA
2.1)在pom文件中导入依赖
SpringBoot的spring-boot-starter-test
包中,默认集成了JUnit5依赖包,Mockito: Java Mock框架依赖,AssertJ流式断言依赖包等,所以正常来说我们不需要再导入关于单元测试的依赖包了。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
版本关系如图:对静态类进行mock的时候:mockito-core的版本要在3.4以上。
2.2)新建测试类
①查看扫描路径是否正确(如果不正确需要手动添加):File-project structure–>moudles–>选中想要的模块–>Sources–>test下的java文件夹右击设置为tests
②选中需要测试的service包中类的类名,右键选择Generate
③然后选择Test
④勾选以下的选项:
1、选择JUnit5。
2、测试类的命名规范为BidInfoServiceImpl类名后面加Test也就是BidInfoServiceImplTest
。3、我们需要在test包下建立相同的路径
4、勾选setUp/@Before
5、勾选需要测试的方法
6、选择OK
⑤查看test下的路径,已经替我们生成了对应的测试类了
2.3)新建一个简单的测试方法
①我现在想要测试BidInfoServiceImpl类中的fact方法
public class BidInfoServiceImpl implements BidInfoService {
public int fact(int i){
int a=++i;
return a;
}
}
②在刚刚新建的测试类BidInfoServiceImplTest上面使用@SpringBootTest
注解,使用@InjectMock
注释在要测试的实现类上,使用@Test
注释在测试的方法上
package com.bjpowernode.money.service;
import com.bjpowernode.money.mapper.BidInfoMapper;
/**
* Tesr for {@link BidInfoServiceImpl}
* @author bms
* @since 1.0
*/
// 在junit4的时候使用@Runwith注解,在junit5的时候使用@SpringBootTest注解,
//作用是加载web Application Context并提供Mock Web Environment
@SpringBootTest
//@RunWith(MockitoJUnitRunner.class)
public class BidInfoServiceImplTest {
@InjectMocks
BidInfoServiceImpl bidInfoServiceImpl;
@Test
public void testFact(){
//第一个参数是预期结果:2。第二个参数是需要测试的方法,传入值1
assertEquals(2,bidInfoServiceImpl.fact(1));
assertEquals(4,bidInfoServiceImpl.fact(3));
}
}
③右键红框处,点击RUN运行,assertEquals()方法中第一个参数是预期结果2。第二个参数是需要测试的方法,传入参数1
④测试通过(即结果与预期相符),会出现如下提示:
第三章、测试框架提供的注解和方法介绍
3.1)注解
3.1.1)注释测试类
@SpringBootTest()
@RunWith(SpringRunner.class)
3.1.2)注释成员变量
方式一:
@InjectMock|@Mock:
@InjectMock:注释的是要测试的实现类,不是接口
@Mock:注释测试类serviceimpl用@Autowired引入的成员变量(mock后真实的方法不再调用)
但用Mockito.when(service.方法名(参数)).thenCallRealMethod();还是可以调真实的方法
@Spy:注释测试类serviceimpl用@Autowired引入的成员变量(spy真实的调用不调用要看写法)
但用Mockito.doReturn(“不执行此方法”).when(service).方法名(参数);还是可以不调用真实的方法
@Spy 会真实的执行对象的方法(如果方法报错,test直接报错)
@Mock 不会执行对象的方法(即使方法报错,test只会使用你设置的返回值,不影响流程)
方式二:
@Autowired| ReflectionTestUtils.setField();
@Autowired注释被注入mock的被测试的类
AopTestUtils.getTargetObject()、AopTestUtils.getUltimateTargetObject()
ReflectionTestUtils.setField();将其他的mock对象注入被测试的类中
@Autowired和@InjectMock最好不要一起使用,如果一起使用了
a、必须在test之前@Before中执行 MockitoAnnotations.openMocks(this);对mock初始化
b、测试的时候要用this.serviceimpl.方法名()测试 否则mock还会走真实的方法
3.2)方法
3.2.1)Mockito方法
Mockito.when(service.方法名(参数)).thenReturn(要返回的值);
Mockito.when(service.方法名(参数)).thenCallRealMethod();
Mockito.when(service.方法名(参数)).thenThrow(new RuntimeException());
Mockito.doNothing().when(service).方法名(参数);
Mockito.doReturn(“不执行此方法”).when(service).方法名(参数);
new 对象的:MockedConstruction
静态方法:MockedStatic
3.2.2)Assert结果断言、verify行为验证
assertEquals(expected, actual)是最常用的测试方法
assertTrue(): 期待结果为true
assertFalse(): 期待结果为false
assertNotNull(): 期待结果为非null
assertArrayEquals(): 期待结果为数组并与期望数组每个元素的值均相等
3.3)
第四章、使用Mockito的实例
4.1)需要测试的方法
需要进行测试的方法
@Component
@RequestMapping(path = "/BidInfoService")
public class BidInfoServiceImpl implements BidInfoService {
@Autowired
BidInfoMapper bidInfoMapper;
@Autowired(required = false)
RedisTemplate redisTemplate;
// 累计成交额:总金额
@Override
@GetMapping("/queryBidMoneySum")
@ResponseBody
public Double queryBidMoneySum() {
//通过工具类常量对应的值,获得展示值
Double bidMoneySum = (Double) redisTemplate.opsForValue().get(Constants.BID_MONEY_SUM);
if (bidMoneySum != null) {
return bidMoneySum;
}
if (bidMoneySum == null) {
//如果缓存中值不存在,访问数据库得到值
bidMoneySum = bidInfoMapper.selectBidMoneySum();
if (bidMoneySum != null) {
//将从数据库获得的值设置到缓存,有效时间42秒
redisTemplate.opsForValue().set(Constants.BID_MONEY_SUM, bidMoneySum, 42, TimeUnit.SECONDS);
} else {
redisTemplate.opsForValue().set(Constants.BID_MONEY_SUM, null, 42, TimeUnit.SECONDS);
}
}
return bidMoneySum;
}
4.2)测试上面的方法
每一行都详细写了注释,认真看就知道怎么用咯
package com.bjpowernode.money.service;
/**
* Tesr for {@link BidInfoServiceImpl}
* @author bms
* @since 1.0
*/
// 在junit4的时候使用@Runwith,在junit5的时候使用@SpringBootTest,
//作用是加载web Application Context并提供Mock Web Environment
@RunWith(MockitoJUnitRunner.class)
public class BidInfoServiceImplTest {
//把要测试的类通过mock注入
@InjectMocks
BidInfoServiceImpl bidInfoServiceImpl;
//在运行时,需要用到的对象,使用@mock造个假对象
@Mock
RedisTemplate redisTemplate;
//在运行时,需要用到的对象,使用@mock造个假对象
@Mock
BidInfoMapper bidInfoMapper;
@Test
public void testQueryBidMoneySum(){
//缓存不为空的情况:
//造一个假的值
ValueOperations value = mock(ValueOperations.class);
//redisTemplate调用opsForValue,获得(返回)这个mock值
when(redisTemplate.opsForValue()).thenReturn(value);
//获得这个mock值value后,调用get方法。得到返回值是5.345
when(value.get(Constants.BID_MONEY_SUM)).thenReturn(5.345);
//本类的对象调用需要测试的方法queryBidMoneySum,期望返回值是5.345
assertEquals(5.345, bidInfoServiceImpl.queryBidMoneySum());
//缓存为空的情况
when(redisTemplate.opsForValue()).thenReturn(value);
//获得这个mock值value后,调用get方法。得到返回值是null
when(value.get(Constants.BID_MONEY_SUM)).thenReturn(null);
//当返回值是null,执行selectBidMoneySum去数据库查询,得到返回值是8.88
when(bidInfoMapper.selectBidMoneySum()).thenReturn(8.88);
//将从数据库获得的值设置到缓存,有效时间42秒。 因为没有返回值所以使用doNothing().when(value)
doNothing().when(value).set(Constants.BID_MONEY_SUM, 8.88, 42, TimeUnit.SECONDS);
//本类的对象调用需要测试的方法queryBidMoneySum,期望返回值是8.88
assertEquals(8.88, bidInfoServiceImpl.queryBidMoneySum());
}