安装
使用maven安装
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> |
参数化测试
参数化测试可以用不同的参数多次运行测试。它们和普通的@Test方法一样声明,但是使用@ParameterizedTest注解。另外,您必须声明至少一个将为每次调用提供参数的来源(source)。
@ParameterizedTest @ValueSource(strings = { "racecar", "radar", "able was I ere I saw elba" }) void palindromes(String candidate) { assertTrue(isPalindrome(candidate)); } |
测试类
测试类一般都在test文件夹下,而且包结构与被测试类相同。
import static org.junit.jupiter.api.Assertions.fail; import ... //@SpringBootTest //如果要使用在SpringBoot中托管,就要加上这个注解 class StandardTests { @BeforeAll static void initAll() { } @BeforeEach void init() { } @Test void succeedingTest() { } @Test void failingTest() { fail("a failing test"); } @Test @Disabled("for demonstration purposes") void skippedTest() { // not executed } @AfterEach void tearDown() { } @AfterAll static void tearDownAll() { } } |
其中,测试类方法中经常使用断言来判断测试结果。
断言
JUnit提供了许多断言方法,是org.junit.jupiter.Assertions中的静态方法。以下列举一些常用方法。
Mockito|Mockito 中文文档
Mockito库能够Mock对象、验证结果以及打桩(stubbing)。Mockito经常与JUnit搭配使用。
Mock的作用
Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。
如果我想对A进行单元测试,那A依赖于BC,而C又有自己的依赖链,如果要构建A,就需要吧BCDE都构建出来,这样就失去了单元测试的意义。
而mock可以将这些依赖解耦,实现真正的单元测试。
创建Mock
Mock
可以用mock(class)方法创建一个mock对象。
Spy
可以用spy(class)方法创建一个监控对象。当你使用这个spy对象时真实的对象也会也调用,除非它的函数被stub了。
即由spy创建的对象,在其方法被插桩之前,都是调用原函数的方法。
注解创建Mock
@Mock
同mock方法
@Spy
同spy方法
@InjectMocks
可以把mock或spy注入被注解的对象的@Autowired成员中。因为在单元测试中,无法把mock或spy的对象进行注入,所以可以使用@InjectMocks进行注入。使用方法见实例。
验证行为
verify(对象[, times(执行次数)]).方法(参数...)
一旦mock对象被创建了,mock对象会记住所有的交互。
测试桩
测试桩有两种风格。
when...then
when(对象.方法(...)).[then,thenAnwser,thenReturn,thenThrow](返回);
// 你可以mock具体的类型,不仅只是接口 LinkedList mockedList = mock(LinkedList.class); // 测试桩 when(mockedList.get(0)).thenReturn("first"); when(mockedList.get(1)).thenThrow(new RuntimeException()); when(mockedList.show(anyString)).then(new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { return "mock show: "+invocationOnMock.getArgument(0); }); // 输出“first” System.out.println(mockedList.get(0)); // 抛出异常 System.out.println(mockedList.get(1)); // 返回"mock show: test" System.out.println(mockedList.show("test"); // 因为get(999) 没有打桩,因此输出null System.out.println(mockedList.get(999)); // 验证get(0)被调用的次数 verify(mockedList).get(0); |
do...when
[doReturn,doThrow,doAnswer,doNothing,doCallRealMethod](返回).when(对象).方法(参数)。
区别
如果是spy对象,要使用do...when来插桩,如果使用when...then,会调用原对象的方法,而do...when则是直接返回,不会调用原方法。
参数匹配器
Mockito以自然的java风格来验证参数值: 使用equals()函数。
当为mock对象做测试桩时,可以为有参数的方法指定参数类型而不仅仅是一个值。
// 使用内置的anyInt()参数匹配器 when(mockedList.get(anyInt())).thenReturn("element"); // 使用自定义的参数匹配器( 在isValid()函数中返回你自己的匹配器实现 ) //ArgumentMatcher<T>实现了isValid()接口 when(mockedList.contains(argThat(isValid()))).thenReturn("element"); // 输出element System.out.println(mockedList.get(999)); // 你也可以验证参数匹配器 verify(mockedList).get(anyInt()); |
实例
package com.demo.JUnit; public class User { @Autowired public Phone phone; private Long id; private String username; private String password; public String callPhoneNum(String num){ return this.phone.call(num); } public String usePhone(){ return this.phone.use(); } public String callPhoneNum(String num){ return this.phone.call(num); } /**Getter and Setter**/ } package com.demo.JUnit; public class Phone { public String use(){ return "use phone."; } public String call(String num){ return "call: "+num; } public String show(Phone phone){ return phone.toString(); } } package com.demo.JUnit; inport ... @SpringBootTest @RunWith(SpringRunner.class) class UserTest { @Spy Phone phone; @InjectMocks User user; @BeforeEach public void init(){ user.setId(1L); //对方法进行回调插桩,回调函数为一个实现了Anwser的类 doAnswer(new Answer() { @Override public Object answer(InvocationOnMock invocationOnMock) throws Throwable { return "mock call: "+invocationOnMock.getArgument(0); } }).when(phone).call(anyString()); //自定义参数类型 doReturn("mock show").when(phone).show(argThat(new ArgumentMatcher<Phone>() { @Override public boolean matches(Phone phone) { //返回true就返回插桩的方法,返回false就使用原类的方法或返回null return true; } })); //调用一次usePhone() user.usePhone(); } @Test @DisplayName("??") public void call() { assertAll("测试call", ()->assertEquals("call: 12345",user.callPhoneNum("12345")),//失败,原方法被覆盖 ()->assertEquals("mock call: 12345",user.callPhoneNum("12345"))//通过 ); } @Test void getId() { assertEquals(1,user.getId());//通过 } @Test void usePhone(){ assertEquals("use phone.",user.phone.use());//通过,使用了spy,调用了原类中的方法 } @Test void usePhoneTimes(){ assertAll("使用手机次数", ()->verify(phone).use(), ()->verify(phone,times(1)).use(),//通过 ()->verify(phone,times(2)).use()//失败,phone.use()只执行了一次 ); } @Test void testShow(){ assertEquals("mock show",this.phone.show(phone));//通过 } } |
本文内容不用于商业目的,如涉及知识产权问题,请权利人联系51Testing小编(021-64471599-8017),我们将立即处理