Detailed SpringBoot unit test (Mockito, MockBean)

A test method mainly includes three parts:

1)setup

2) perform an operation

3) validation results

public class CalculatorTest {
    Calculator mCalculator;

    @Before // setup
    public void setup() {
        mCalculator = new Calculator();
    }

    @Test //assert 部分可以帮助我们验证一个结果
    public void testAdd() throws Exception {
        int sum = mCalculator.add(1, 2);
        assertEquals(3, sum);  //为了简洁,往往会static import Assert里面的所有方法。
    }

    @Test
    @Ignore("not implemented yet") // 测试时忽略该方法
    public void testMultiply() throws Exception {
    }

    // 表示验证这个测试方法将抛出 IllegalArgumentException 异常,若没抛出,则测试失败
    @Test(expected = IllegalArgumentException.class)
    public void test() {
        mCalculator.divide(4, 0);
    }
}

Junit basic introduction notes

  • @BeforeClass Executed once before the execution of all test methods, which are generally written in a whole initialization code.
  • @AfterClass After all perform a test method, which is generally in the code written on the destruction and release of resources.
// 注意这两个都是静态方法
@BeforeClass
public static void test(){
    
}
@AfterClass
public static void test(){
}
  • @Before When executed before each test method is generally used to initialize method (for example, we tested other methods, classes shared with other test methods value has been changed, in order to ensure the validity of the test results, we will comment @Before reset process data)
  • @After After each test method execution, in the method of execution is completed to do.
  • @Test(timeout = 1000) Test method of performing more than 1,000 ms after time out, the test will fail.
  • @Test(expected = Exception.class) Exception class test methods expect, if there is no method to perform the specified exception is thrown, the test fails.
  • @Ignore("not ready yet") This method will be ignored when performing the test, if used to modify the class, the whole class is ignored.
  • @Test Usually written test cases.
  • @RunWith There are a number of in Runner Junit, they responsible for calling your test code, each Runner has its own special features, you choose different Runner to run your test code as needed.

If we simply do ordinary Java test that does not involve Spring Web project, you can omit @RunWithcomments, you have to choose a different Runner to run your test code as needed.

Test method execution order

By design, Junit test method the execution order is not specified.

  • @FixMethodOrder(MethodSorters.JVM): Reserved test method execution order for the return of order JVM. The order of execution of each test might be different club.
  • @FixMethodOrder(MethodSorters.NAME_ASCENDING): According to the test method name sorting method, according to the dictionary collation (ASC, from small to large increments).

Failure is a test fails, Error is a program error.

Test method naming convention

Maven is not in itself a unit testing framework, it is only when building execution to a specific stage of the life cycle to execute JUnit or TestNG test cases through plug-ins. This plug-in is the maven-surefire-plugin, can also be referred to as test runner (Test Runner), it is compatible with JUnit 3, JUnit 4 and TestNG.

By default, test target maven-surefire-plugin automatically perform a test source path (by default src / test / java /) The test classes comply with a set mode is named. This set of patterns is:

  • * / Test .java: any Java class to a subfolder named Test all switches.
  • * / Test.java: any Java class named Test all ending in a subdirectory.
  • * / TestCase.java: any Java class TestCase all named after ending subdirectory.

Based on unit tests written in the Spring

First of all our projects are generally MVC hierarchical, and unit testing is carried out mainly written in Dao layer and Service layer. From the project structure is, Service layer is dependent Dao layer, but from the perspective of unit test, carried out on a Service unit of time, all he should be dependent class Mock. Dao layer unit while relatively simple tests, dependent only data in the database.

Mockito

Mockito is mocking framework, it allows you to do the test in simple API. And Mockito easy to learn, it readable and concise syntax validation.
Mockito unit test is a simulation framework for Java, and it is similar to the jMock EasyMock, it is to simplify the test unit during testing context (or referred to as a test driver functions and stubs) to build tools developed

Relative to EasyMock and jMock, Mockito advantage is that by what has been called upon to perform a function check, eliminating the need for desired behavior (expectations) of. Other mocking libraries need to record the desired behavior (expectations) prior to execution, which led to ugly initialization code.

SpringBoot the pom.xmlfile you need to add dependencies:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

Entering spring-boot-starter-test-2.1.3.RELEASE.pomcan be seen most of the dependencies have been dependent on the required unit tests, such as:

  • junit
  • mockito
  • hamcrest

If other spring items, you need to add your own Junit and mockito project.

Mockito commonly used methods:

Method name description
Mockito.mock(classToMock) Mock objects
Mockito.verify(mock) Verify whether the conduct occurred
Mockito.when(methodCall).thenReturn(value1).thenReturn(value2) The first returns value1 when triggered, the n-th return value2
Mockito.doThrow(toBeThrown).when(mock).[method] Analog thrown.
Mockito.mock(classToMock,defaultAnswer) Use the default Answer mock objects
Mockito.when(methodCall).thenReturn(value) Parameter matching
Mockito.doReturn(toBeReturned).when(mock).[method] Matching parameters (do not direct determination)
Mockito.when(methodCall).thenAnswer(answer)) Callback interface is expected to generate expectations
Mockito.doAnswer(answer).when(methodCall).[method] Callback interface is expected to generate a desired value (do not direct determination)
Mockito.spy(Object) With real spy monitor object, set the behavior of real objects
Mockito.doNothing().when(mock).[method] Without any return
Mockito.doCallRealMethod().when(mock).[method] //等价于Mockito.when(mock.[method]).thenCallRealMethod(); Call the real way
reset(mock) Reset mock

Example:

  • Verify whether the conduct occurred
//模拟创建一个List对象
List<Integer> mock =  Mockito.mock(List.class);
//调用mock对象的方法
mock.add(1);
mock.clear();
//验证方法是否执行
Mockito.verify(mock).add(1);
Mockito.verify(mock).clear();
  • Multi-Trigger return different values
//mock一个Iterator类
Iterator iterator = mock(Iterator.class);
//预设当iterator调用next()时第一次返回hello,第n次都返回world
Mockito.when(iterator.next()).thenReturn("hello").thenReturn("world");
//使用mock的对象
String result = iterator.next() + " " + iterator.next() + " " + iterator.next();
//验证结果
Assert.assertEquals("hello world world",result);
  • Analog thrown
@Test(expected = IOException.class)//期望报IO异常
public void when_thenThrow() throws IOException{
      OutputStream mock = Mockito.mock(OutputStream.class);
      //预设当流关闭时抛出异常
      Mockito.doThrow(new IOException()).when(mock).close();
      mock.close();
  }
  • Use the default Answer mock objects

RETURNS_DEEP_STUBS is one of the alternate parameter when creating mock objects
以下方法deepstubsTest和deepstubsTest2是等价的

  @Test
  public void deepstubsTest(){
      A a=Mockito.mock(A.class,Mockito.RETURNS_DEEP_STUBS);
      Mockito.when(a.getB().getName()).thenReturn("Beijing");
      Assert.assertEquals("Beijing",a.getB().getName());
  }

  @Test
  public void deepstubsTest2(){
      A a=Mockito.mock(A.class);
      B b=Mockito.mock(B.class);
      Mockito.when(a.getB()).thenReturn(b);
      Mockito.when(b.getName()).thenReturn("Beijing");
      Assert.assertEquals("Beijing",a.getB().getName());
  }
  class A{
      private B b;
      public B getB(){
          return b;
      }
      public void setB(B b){
          this.b=b;
      }
  }
  class B{
      private String name;
      public String getName(){
          return name;
      }
      public void setName(String name){
          this.name = name;
      }
      public String getSex(Integer sex){
          if(sex==1){
              return "man";
          }else{
              return "woman";
          }
      }
  }
  • Parameter matching
@Test
public void with_arguments(){
    B b = Mockito.mock(B.class);
    //预设根据不同的参数返回不同的结果
    Mockito.when(b.getSex(1)).thenReturn("男");
    Mockito.when(b.getSex(2)).thenReturn("女");
    Assert.assertEquals("男", b.getSex(1));
    Assert.assertEquals("女", b.getSex(2));
    //对于没有预设的情况会返回默认值
    Assert.assertEquals(null, b.getSex(0));
}
class B{
    private String name;
    public String getName(){
        return name;
    }
    public void setName(String name){
        this.name = name;
    }
    public String getSex(Integer sex){
        if(sex==1){
            return "man";
        }else{
            return "woman";
        }
    }
}
  • Match any parameters

Mockito.anyInt()Any int value;
Mockito.anyLong()any long value;
Mockito.anyString()any String value;

Mockito.any(XXX.class) Any value XXX type, and so on.

@Test
public void with_unspecified_arguments(){
    List list = Mockito.mock(List.class);
    //匹配任意参数
    Mockito.when(list.get(Mockito.anyInt())).thenReturn(1);
    Mockito.when(list.contains(Mockito.argThat(new IsValid()))).thenReturn(true);
    Assert.assertEquals(1,list.get(1));
    Assert.assertEquals(1,list.get(999));
    Assert.assertTrue(list.contains(1));
    Assert.assertTrue(!list.contains(3));
}
class IsValid extends ArgumentMatcher<List>{
    @Override
    public boolean matches(Object obj) {
        return obj.equals(1) || obj.equals(2);
    }
}

Note: Use the parameters match, then all parameters must be matched by matchers
Mockito inheritance Matchers, anyInt () method, etc. are Matchers
when two arguments, one of the parameters when using any parameter that specifies the parameters needed to compare matchers

Comparator comparator = mock(Comparator.class);
comparator.compare("nihao","hello");
//如果你使用了参数匹配,那么所有的参数都必须通过matchers来匹配
Mockito.verify(comparator).compare(Mockito.anyString(),Mockito.eq("hello"));
//下面的为无效的参数匹配使用
//verify(comparator).compare(anyString(),"hello");
  • Custom parameters match
@Test
public void argumentMatchersTest(){
   //创建mock对象
   List<String> mock = mock(List.class);
   //argThat(Matches<T> matcher)方法用来应用自定义的规则,可以传入任何实现Matcher接口的实现类。
   Mockito.when(mock.addAll(Mockito.argThat(new IsListofTwoElements()))).thenReturn(true);
   Assert.assertTrue(mock.addAll(Arrays.asList("one","two","three")));
}

class IsListofTwoElements extends ArgumentMatcher<List>
{
   public boolean matches(Object list)
   {
       return((List)list).size()==3;
   }
}
  • Callback interface is expected to generate expectations
@Test
public void answerTest(){
      List mockList = Mockito.mock(List.class);
      //使用方法预期回调接口生成期望值(Answer结构)
      Mockito.when(mockList.get(Mockito.anyInt())).thenAnswer(new CustomAnswer());
      Assert.assertEquals("hello world:0",mockList.get(0));
      Assert.assertEquals("hello world:999",mockList.get(999));
  }
  private class CustomAnswer implements Answer<String> {
      @Override
      public String answer(InvocationOnMock invocation) throws Throwable {
          Object[] args = invocation.getArguments();
          return "hello world:"+args[0];
      }
  }
等价于:(也可使用匿名内部类实现)
@Test
 public void answer_with_callback(){
      //使用Answer来生成我们我们期望的返回
      Mockito.when(mockList.get(Mockito.anyInt())).thenAnswer(new Answer<Object>() {
          @Override
          public Object answer(InvocationOnMock invocation) throws Throwable {
              Object[] args = invocation.getArguments();
              return "hello world:"+args[0];
          }
      });
      Assert.assertEquals("hello world:0",mockList.get(0));
     Assert. assertEquals("hello world:999",mockList.get(999));
  }
  • Callback interface is expected to generate the expected value (direct implementation)
@Test
public void testAnswer1(){
List<String> mock = Mockito.mock(List.class);  
      Mockito.doAnswer(new CustomAnswer()).when(mock).get(Mockito.anyInt());  
      Assert.assertEquals("大于三", mock.get(4));
      Assert.assertEquals("小于三", mock.get(2));
}
public class CustomAnswer implements Answer<String> {  
  public String answer(InvocationOnMock invocation) throws Throwable {  
      Object[] args = invocation.getArguments();  
      Integer num = (Integer)args[0];  
      if( num>3 ){  
          return "大于三";  
      } else {  
          return "小于三";   
      }  
  }
}
  • Modify the default expectations for returns are not preset call (specify a return value)
//mock对象使用Answer来对未预设的调用返回默认期望值
List mock = Mockito.mock(List.class,new Answer() {
     @Override
     public Object answer(InvocationOnMock invocation) throws Throwable {
         return 999;
     }
 });
 //下面的get(1)没有预设,通常情况下会返回NULL,但是使用了Answer改变了默认期望值
 Assert.assertEquals(999, mock.get(1));
 //下面的size()没有预设,通常情况下会返回0,但是使用了Answer改变了默认期望值
 Assert.assertEquals(999,mock.size());
  • With real spy monitor object, set the behavior of real objects
    @Test(expected = IndexOutOfBoundsException.class)
    public void spy_on_real_objects(){
        List list = new LinkedList();
        List spy = Mockito.spy(list);
        //下面预设的spy.get(0)会报错,因为会调用真实对象的get(0),所以会抛出越界异常
        //Mockito.when(spy.get(0)).thenReturn(3);

        //使用doReturn-when可以避免when-thenReturn调用真实对象api
        Mockito.doReturn(999).when(spy).get(999);
        //预设size()期望值
        Mockito.when(spy.size()).thenReturn(100);
        //调用真实对象的api
        spy.add(1);
        spy.add(2);
        Assert.assertEquals(100,spy.size());
        Assert.assertEquals(1,spy.get(0));
        Assert.assertEquals(2,spy.get(1));
        Assert.assertEquals(999,spy.get(999));
    }
  • Without any return
@Test
public void Test() {
    A a = Mockito.mock(A.class);
    //void 方法才能调用doNothing()
    Mockito.doNothing().when(a).setName(Mockito.anyString());
    a.setName("bb");
    Assert.assertEquals("bb",a.getName());
}
class A {
    private String name;
    private void setName(String name){
        this.name = name;
    }
    private String getName(){
        return name;
    }
}
  • Call the real way
@Test
public void Test() {
    A a = Mockito.mock(A.class);
    //void 方法才能调用doNothing()
    Mockito.when(a.getName()).thenReturn("bb");
    Assert.assertEquals("bb",a.getName());
    //等价于Mockito.when(a.getName()).thenCallRealMethod();
    Mockito.doCallRealMethod().when(a).getName();
    Assert.assertEquals("zhangsan",a.getName());
}
class A {
    public String getName(){
        return "zhangsan";
    }
}
  • Reset mock
    @Test
    public void reset_mock(){
        List list = mock(List.class);
        Mockito. when(list.size()).thenReturn(10);
        list.add(1);
        Assert.assertEquals(10,list.size());
        //重置mock,清除所有的互动和预设
        Mockito.reset(list);
        Assert.assertEquals(0,list.size());
    }
  • @Mock annotation
public class MockitoTest {
    @Mock
    private List mockList;
    //必须在基类中添加初始化mock的代码,否则报错mock的对象为NULL
    public MockitoTest(){
        MockitoAnnotations.initMocks(this);
    }
    @Test
    public void AnnoTest() {
            mockList.add(1);
        Mockito.verify(mockList).add(1);
    }
}
  • Specify the test class uses runner: MockitoJUnitRunner
@RunWith(MockitoJUnitRunner.class)
public class MockitoTest2 {
    @Mock
    private List mockList;

    @Test
    public void shorthand(){
        mockList.add(1);
        Mockito.verify(mockList).add(1);
    }
}

@MockBean

Use @MockBeancan solve some of the dependencies in a unit test, for example:

@RunWith(SpringRunner.class)
@SpringBootTest
public class ServiceWithMockBeanTest {
    @MockBean
    SampleDependencyA dependencyA;
    @Autowired
    SampleService sampleService;

    @Test
    public void testDependency() {
        when(dependencyA.getExternalValue(anyString())).thenReturn("mock val: A");
        assertEquals("mock val: A", sampleService.foo());
    }
}

@MockBean Only mock native code - or write your own code to be stored in the library and they are assembled to form the Bean class code powerless.

@SpyBeanSolve SpringBoot unit tests @MockBeancan not mock library autowiring Bean limitations (currently not demand, need their own access to information).

reference:

https://www.cnblogs.com/Ming8006/p/6297333.html#c3
https://www.vogella.com/tutorials/Mockito/article.html

Guess you like

Origin blog.csdn.net/weixin_34162228/article/details/90790615