精华内容
下载资源
问答
  • Mockito

    2021-05-24 16:29:18
    Mockito Dan North, the originator of Behavior-Driven Development wrote this back in 2008: “We decided during the main conference that we should use JUnit 4 and Mockito because we think they are the ...

    Mockito

    Dan North, the originator of Behavior-Driven Development wrote this back in 2008:
    “We decided during the main conference that we should use JUnit 4 and Mockito because we think they are the future of TDD and mocking in Java”


    说明
    本文章中的示例使用的开发环境为JDK1.8!
    Mockito的版本信息如下

    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>1.10.19</version>
    

    文章中的示例部分来自Mockito Dzone Reference Card

    Warning:文章中的示例只是为了展示Mockito的语法和特性,在实际的测试编码中,我们可能不会使用示例的用法(测试逻辑)!
    官方原文:

    Warning: Note that the examples in this Refcardwere created to demonstrate behaviors of Mockito in a specific context. Of course, when writing the test for your codebase, there is no need to ensure that mocks are stubbed correctly.

    鄙人在Github上修改了部分示例,并且写了一些其他的示例(实际测试代码中用法)!传送门



    简介

    Mockito是一个模拟测试框架。在一个被测试的对象(功能)A中,它通常需要与其他的对象(功能)B进行一些交互,我们把A称作被测试对象(tested object),把B称作协作者(collaborators)。那么在测试环境中,这些协作者都需要被创建,以便被测试对象可以使用它们。为了使测试代码简化以及满足上下文执行环境,我们通常使用测试替身(test double)来代替这些协作者,测试替身看上去和原始的协作者一样,但是却不依赖其他对象,而且可以执行预期行为,记录他们的交互行为(interactions,可以理解成方法调用)


    核心元素

    TDD : Test-Driven Development

    BDD : Behavior-Driven Development

    Mock : 模拟对象,可以理解成Mockito框架帮我们自动生成的数据。通过mock产生的对象有以下的能力:1.可以通过编程产生预期行为;2.在对象的生命周期内可以校验它的交互行为(方法调用);

    原文参考:Mock - an object with the ability to a) have a programmed expected behavior, and b) verify the interactions occurring in its lifetime (this object is usually created with the help of mocking framework)

    Stub : 存根?桩?可以理解成通过硬编码的方式预期定义行为(方法),将会产生特定的结果,屏蔽原本行为(方法)的实现。

    /**举例,伪代码描述*/
    
    //define a method
    public string sayABC() {
        return "ABC";
    }
    
    //stubbing
    define when call sayABC the return "CBA"
    
    //act
    sayABC();   //call then we got "CBA" not "ABC"
    
    

    原文参考:Stub - an object with hardcoded behavior suitable for a given test (or a group of tests)

    Spy : mock的代理对象,当方法被stub的时候,调用stub的定义行为(方法实现被忽略);当方法没有被stub时,调用真实对象的行为(调用真实方法的逻辑实现)。此对象一般不由mock生成,而是自己编码new,再通过spy包装成mock

    原文参考:Spy - a mock created as a proxy to an existing real object; some methods can be stubbed, while the un-stubbed ones are forwarded to the covered object

    Test Doubles:测试替身,包括5个类型stub,mock,spy,fake,dummy。


    地心历险(待研究)

    揭示底层的原理


    实战

    1. 概述

    编写测试用例,一般分为三个阶段:

    Section nameResponsibility
    arrange (given)SUT and mocks initialization and configuration
    act (when)An operation which is a subject to testing;preferably only one operation on an SUT
    assert (then)The assertion and verification phase

    arrange-act-assert模式对应的语法为:when().thenXXX()
    given-when-then模式(对应BDD形式)对应的语法为:given().willXXX()
    官方大部分DEMO使用given-when-then模式,而且也推荐使用这种模式

    官方原文: given-when-then comments make intentions of tests clearer.

    2. simple example

    import org.testng.annotations.Test;
    import static org.mockito.Mockito.*;
    import static org.testng.Assert.assertEquals;
    
    public class SimpleStubbingTest {
        public static final int TEST_NUMBER_OF_LEAFS = 5;
        
        @Test
        public void shouldReturnGivenValue() {
            // arrange
            Flower flowerMock = mock(Flower.class);
            when(flowerMock.getNumberOfLeafs()).thenReturn(TEST_NUMBER_OF_LEAFS);
        
            // act
            int numberOfLeafs = flowerMock.getNumberOfLeafs();
            
            // assert
            assertEquals(numberOfLeafs, TEST_NUMBER_OF_LEAFS);
        }
    
    }
    
    import static org.mockito.Mockito.*;
    import static org.mockito.BDDMockito.*;
    import static org.junit.Assert.*; 
    
    public class SimpleStubbingTest {
        public static final int TEST_NUMBER_OF_LEAFS = 5;
        
        @Test
        public void shouldReturnGivenValueUsingBDDSemantics() {
            //given
            Flower flowerMock = mock(Flower.class);
            given(flowerMock.getNumberOfLeafs()).willReturn(TEST_NUMBER_OF_LEAFS);
        
            //when
            int numberOfLeafs = flowerMock.getNumberOfLeafs();
            
            //then
            assertEquals(numberOfLeafs, TEST_NUMBER_OF_LEAFS);
        }
    }
    

    3. 参数匹配(Argument Matching)

    Mockito默认使用equals()来匹配参数。通常我们需要一个宽范围的参数匹配,Mockito的org.mockito.Matchers类中提供了一套内置的匹配器(Matcher)。
    代码示例:

    given(plantSearcherMock.smellyMethod(anyInt(), contains("asparag"), eq("red"))).willReturn(true);
    
    //given(plantSearcherMock.smellyMethod(anyInt(), contains("asparag"), "red")).willReturn(true);
    
    //incorrect - would throw an exception
    

    如果有一个参数使用了匹配器,则所有的参数必须都使用匹配器,否则将会抛出异常!示例代码中注释掉的部分会抛出异常!

    自定义的参数匹配器需要继承org.mockito.ArgumentMatcher抽象类,并且实现matches方法。然后调用argThat(org.hamcrest.Matcher matcher)方法。
    代码示例:

    given(schedulerMock.getNumberOfPlantsScheduledOnDate(
        argThat(haveHourFieldEqualTo(7)))).willReturn(1);
    
    //with the util method to create a matcher
    private ArgumentMatcher haveHourFieldEqualTo(final int hour) {
        return new ArgumentMatcher() {
        
            @Override
            public boolean matches(Object argument) {
                return ((Date) argument).getHours() == hour;
            }
        };
    
    }
    

    4. Stubbing Void Methods

    void Methods 应该使用 willXXX…given 或者 doXXX…when.来进行stubbing
    代码示例:

    @Test(expectedExceptions = WaterException.class)
    public void shouldStubVoidMethod() {
    
        WaterSource waterSourceMock = mock(WaterSource.class);
        doThrow(WaterException.class).when(waterSourceMock).doSelfCheck();
    
        //the same with BDD semantics
        //willThrow(WaterException.class).given(waterSourceMock).doSelfCheck();
    
        waterSourceMock.doSelfCheck();
    
        //exception expected
    
    }
    

    5. Stubbing With a Custom Answer

    极少的情况会使用自己的处理逻辑来指定预期行为的结果(也就是given…willReturn(Custom Answer)中的Answer).Mockito还是提供了org.mockito.stubbing.Answer的接口来实现这个功能,你只需实现该接口中的answer方法。

    Warning:如果需要使用Custom Answer,可能预示着被测代码太复杂,需要重构!
    官方原文:

    Warning: The need to use a custom answer may indicate that tested code is too complicated and should be re-factored.

    6. 检验行为(Verifying Behavior)

    在一个mock对象的生命周期内,它会记住本身所有的操作。在被测系统(SUT)中,这些操作应该可以被轻易校验。Mockito中可以使用Mockito.verify(T mock)这个基础形式来进行校验
    代码示例:

    WaterSourcewaterSourceMock = mock(WaterSource.class);
    
    waterSourceMock.doSelfCheck();
    
    verify(waterSourceMock).doSelfCheck();      //默认校验一次调用
    

    Mockito提供了一些有意义的校验模式,你也可以创建自定义校验模式

    verify(waterSourceMock,never()).doSelfCheck();
    
    verify(waterSourceMock,times(2)).getWaterPressure();
    
    verify(waterSourceMock,atLeast(1)).getWaterTemperature();
    

    7. 校验调用顺序(Verifying Call Order)

    Mockito可以让你校验调用的顺序,你可以创建一组mocks,然后在组内校验所有的调用顺序。
    代码示例:

    @Test
    public void shouldVerifyInOrderThroughDifferentMocks(){
    
        WaterSourcewaterSource1=mock(WaterSource.class);
        WaterSourcewaterSource2=mock(WaterSource.class);
    
        waterSource1.doSelfCheck();
        waterSource2.getWaterPressure();
        waterSource1.getWaterTemperature();
    
        InOrderinOrder=inOrder(waterSource1,waterSource2);
        inOrder.verify(waterSource1).doSelfCheck();
        inOrder.verify(waterSource2).getWaterPressure();
        inOrder.verify(waterSource1).getWaterTemperature();
    
    }
    

    8. Spying on Real Objects

    在Mockito中,你可以使用真实的对象来代替mock,从而使部分方法可以stubbed。通常我们不需要使用spy一个真实的对象,这可能是代码异味(code smell)的信号。但在一些特殊的情况下(比如使用了遗留代码,或者IOC容器),纯mock对象可能不能进行测试。
    代码示例:

    @Test
    public void shouldStubMethodAndCallRealNotStubbedMethod() {
    
        Flower realFlower = new Flower();
        realFlower.setNumberOfLeafs(ORIGINAL_NUMBER_OF_LEAFS);
        FlowerflowerSpy=spy(realFlower);
        willDoNothing().given(flowerSpy).setNumberOfLeafs(anyInt());
    
        flowerSpy.setNumberOfLeafs(NEW_NUMBER_OF_LEAFS);  //stubbed,and should do nothing
    
        verify(flowerSpy).setNumberOfLeafs(NEW_NUMBER_OF_LEAFS);
        assertEquals(flowerSpy.getNumberOfLeafs(),ORIGINAL_NUMBER_OF_LEAFS);       //value was not changed
    
    }
    

    当使用spy时,必须使用willXXX…given/ doXXX…的形式来stubbing,它可以在stub期间防止真实方法方法被调用

    Warning:当使用spy时,Mockito创建了真实对象的一份拷贝,因此所有的交互行为应该被传递到被创建的spy对象上
    官方原文:

    Warning: While spying, Mockito creates a copy of a real object, and therefore all interactions should be passed using the created spy.

    9. Annotations

    Mockito提供三个注解来简化用静态方法创建mock对象的工作 – @Mock, @Spy, @Captor;注解@InjectMocks可以简化mock和spy对象的注入,它可以通过构造器注入,setter方法注入,field赋值注入。

    使用注解的功能要调用MockitoAnnotations.initMocks(testClass)(通常在@Before的方法中调用),或者使用MockitoJUnit4Runner来作为junit runner

    代码示例:

    //with constructor: PlantWaterer(WaterSource waterSource,
    // WateringScheduler wateringScheduler) {...}
    
    public class MockInjectingTest {
    
        @Mock
        private WaterSource waterSourceMock;
    
        @Spy
        private WateringScheduler wateringSchedulerSpy;
    
        @InjectMocks
        private PlantWaterer plantWaterer;
    
        @BeforeMethod
        public void init() {
            MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void shouldInjectMocks() {
            assertNotNull(plantWaterer.getWaterSource());
            assertNotNull(plantWaterer.getWateringScheduler());
        }
    
    }
    
    AnnotationResponsibility
    @MockCreates a mock of a given type
    @SpyCreates a spy of a given object
    @CaptorCreates an argument captor of a given type
    @InjectMocksCreates an object of a given type and injects mocks and spies existing in a test

    10. 改变mock默认的返回值(Changing the Mock Default Return Value)

    Mockito使我们可以选择生成mock对象的默认值

    Default AnswerDescription
    RETURNS_DEFAULTSReturns a default “empty” value (e.g., null, 0, false, empty collection) - used by default
    RETURNS_SMART_NULLSCreates a spy of a given object
    RETURNS_MOCKSReturns a default “empty” value, but a mock instead of null
    RETURNS_DEEP_STUBSAllows for a simple deep stubbing (e.g., Given(ourMock.getObject().getValue()).willReturn(s))
    CALLS_REAL_METHODSCall a real method of spied object

    mock(clazz, Mockito.${Default Answer})

    Tips

    依照本人的理解,在真正编写测试代码时,我们应该分清当前单元测试的目的,以及它所依赖的方法调用。然后在given阶段,mock各种依赖的对象,并且stub各种依赖对象的预期行为;when阶段进行测试目的的执行,也就是测试对象真实的调用行为;then阶段进行verify和assert。


    局限性

    引用官方说明:
    Limitations

    Mockito has a few limitations worth remembering. They are generally technical restrictions, but Mockito authors believe using hacks to work around them would encourage people to write poorly testable code. Mockito cannot :

    • mock final classes
    • mock enums
    • mock final methods
    • mock static methods
    • mock private methods
    • mock hashCode() and equals()

    Nevertheless, when working with a badly written legacy code, remember that some of the mentioned limitations can be mitigated using the PowerMock or JMockit libraries.

    如果需要使用上面的mock特性也可以参考我的另一篇文章PowerMock


    参考资料

    鄙人编写的代码示例(部分来自官网示例,适量修改,并添加额外的示例)

    Mockito官网
    Mockito On Github
    Mockito Dzone Reference Card
    Introduction to mockito


    TOP

    展开全文
  • mockito

    2020-05-07 19:29:13
    @RunWith(MockitoJUnitRunner.class) public class UserServiceImplTest { @InjectMocks UserServiceImpl userService; @Mock UserMapperImpl userMapper; @BeforeMethod ... Mockito...

    对业务bean进行模拟

    package com.amap.aos.api.transports.service;
    
    import com.fasterxml.jackson.databind.ObjectMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    /**
     * 描述:
     *
     * @author
     * @date 2020-05-07 17:49
     */
    @Service
    public class UserServiceImpl implements UserService {
    
    	private ObjectMapper objectMapper = new ObjectMapper();
    
    	@Autowired
    	private UserMapper userMapper;
    
    	@Override
    	public User getUser() {
    		System.out.println("给我注入数据我开始执行了");
    		System.out.println(userMapper.getClass());
    		System.out.println(objectMapper.getClass());
    		return userMapper.get(1);
    
    	}
    }
    

    依赖userMapper

    package com.amap.aos.api.transports.service;
    
    import java.util.List;
    
    /**
     * 描述:
     *
     * @author lch
     * @date 2020-05-07 17:47
     */
    public interface UserMapper {
    
    	public List<User> list();
    
    	public User get(Integer id);
    
    	public void add(User user);
    
    	public void update(User user);
    
    	public void delete(Integer id);
    
    }
    
    

    对他们依赖关系进行mock

    @RunWith(MockitoJUnitRunner.class)
    public class UserServiceImplTest {
    
    
    	@InjectMocks
    	UserServiceImpl userService;
    
    	@Mock
    	UserMapperImpl userMapper;
    
    	@BeforeMethod
    	public void setUp() {
    		MockitoAnnotations.initMocks(this);
    	}
    
    	//
    	@Test
    	public void testGetUser() {
    		User user = new User();
    		user.setId(1);
    		user.setName("lichaohui");
    		//mock  替换掉被测试方法的下层依赖
    		Mockito.when(userMapper.get(Mockito.anyInt())).thenReturn(user);
    		//依赖自己的业务方法进行测试  
    		System.out.println(userService.getUser());
    	}
    }
    
    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 17,660
精华内容 7,064
关键字:

mockito