Mockito初级学习

标签:mockito


使用Mockito来Mock对象

有两种方法来mock对象,1)使用静态方法: mock();2) 使用注解:@Mock

  • mock()
@Test
public void givenCountMethodMocked_WhenCountInvoked_ThenMockedValueReturned() {
    UserRepository localMockRepository = Mockito.mock(UserRepository.class);
    Mockito.when(localMockRepository.count()).thenReturn(111L);
 
    long userCount = localMockRepository.count();
 
    Assert.assertEquals(111L, userCount);
    Mockito.verify(localMockRepository).count();
}
  • @Mock
    • 采用@Rule注解来调用MockitoAnnotations.initMocks(this)
    • 采用@RunWith(MockitoJUnitRunner.class)
//实例1
import static org.mockito.Mockito.*;

public class MockitoTest  {

    @Mock
    MyDatabase databaseMock; 


    @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); 


    @Test
    public void testQuery()  {
        ClassToTest t  = new ClassToTest(databaseMock); 

        boolean check = t.query("* from t"); 

        assertTrue(check); 

        verify(databaseMock).query("* from t"); 

    }
}
//实例2
@RunWith(MockitoJUnitRunner.class)
public class MockAnnotationUnitTest {
     
    @Mock
    UserRepository mockRepository;
     
    @Test
    public void givenCountMethodMocked_WhenCountInvoked_ThenMockValueReturned() {
        Mockito.when(mockRepository.count()).thenReturn(123L);
 
        long userCount = mockRepository.count();
 
        Assert.assertEquals(123L, userCount);
        Mockito.verify(mockRepository).count();
    }
}

Configuring mocks

  • "when thenReturn"和"when thenThrow
  • "doReturn when"和"doThrow when"

The doReturn(…​).when(…​).methodCall call chain works similar to when(…​.).thenReturn(…​.). It is useful for mocking methods which give an exception during a call.

使用Spy来包装java对象

@spy或者spy()方法被用来包装一个真实的对象。

  • spy()
@Test
public void whenNotUseSpyAnnotation_thenCorrect() {
    List<String> spyList = Mockito.spy(new ArrayList<String>());
     
    spyList.add("one");
    spyList.add("two");
 
    Mockito.verify(spyList).add("one");
    Mockito.verify(spyList).add("two");
 
    assertEquals(2, spyList.size());
 
    Mockito.doReturn(100).when(spyList).size();
    assertEquals(100, spyList.size());
}
  • @spy
@Spy
List<String> spiedList = new ArrayList<String>();
 
@Test
public void whenUseSpyAnnotation_thenSpyIsInjectedCorrectly() {
    spiedList.add("one");
    spiedList.add("two");
 
    Mockito.verify(spiedList).add("one");
    Mockito.verify(spiedList).add("two");
 
    assertEquals(2, spiedList.size());
 
    Mockito.doReturn(100).when(spiedList).size();
    assertEquals(100, spiedList.size());
}

在这个例子中,使用真实的方法spiedList.add()来给spiedList增加元素。

verify的用途

import static org.mockito.Mockito.*;

@Test
public void testVerify()  {
    // create and configure mock
    MyClass test = Mockito.mock(MyClass.class);
    when(test.getUniqueId()).thenReturn(43);


    // call method testing on the mock with parameter 12
    test.testing(12);
    test.getUniqueId();
    test.getUniqueId();


    // now check if method testing was called with the parameter 12
    verify(test).testing(ArgumentMatchers.eq(12));

    // was the method called twice?
    verify(test, times(2)).getUniqueId();

    // other alternatives for verifiying the number of method calls for a method
    verify(test, never()).someMethod("never called");
    verify(test, atLeastOnce()).someMethod("called at least once");
    verify(test, atLeast(2)).someMethod("called at least twice");
    verify(test, times(5)).someMethod("called five times");
    verify(test, atMost(3)).someMethod("called at most 3 times");
    // This let's you check that no other methods where called on this object.
    // You call it after you have verified the expected method calls.
    verifyNoMoreInteractions(test);
}

@injectMock

若某个class中有依赖的属性或者方法,在创建此class实例时,使用@InjectMocks来将mock注入。例如,我们有这样一个类:

public class ArticleManager {
    private User user;
    private ArticleDatabase database;

    public ArticleManager(User user, ArticleDatabase database) {
        super();
        this.user = user;
        this.database = database;
    }

    public void initialize() {
        database.addListener(new ArticleListener());
    }
}

这个类可通过Mockito来被创建,它的依赖也能被mock填充。

@RunWith(MockitoJUnitRunner.class)
public class ArticleManagerTest  {

       @Mock ArticleCalculator calculator;
       @Mock ArticleDatabase database;
       @Mock User user;

       @Spy private UserProvider userProvider = new ConsumerUserProvider();

       @InjectMocks private ArticleManager manager; 

       @Test public void shouldDoSomething() {
           // calls addListener with an instance of ArticleListener
           manager.initialize();

           // validate that addListener was called
           verify(database).addListener(any(ArticleListener.class));
       }
}

捕获参数

ArgumentCaptor类允许在验证期间访问方法调用的参数。采用@Captor来实现。

import static org.hamcrest.Matchers.hasItem;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;

import java.util.Arrays;
import java.util.List;

import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;

public class MockitoTests {
    
    @Rule
    public MockitoRule rule = MockitoJUnit.rule();
    
    @Captor
    private ArgumentCaptor<List<String>> captor;
    
    @Test
    public final void shouldContainCertainListItem() {
        List<String> asList = Arrays.asList("someElement_test", "someElement");
        final List<String> mockedList = mock(List.class);
        mockedList.addAll(asList);
    
        verify(mockedList).addAll(captor.capture());
        final List<String> capturedArgument = captor.getValue();
        assertThat(capturedArgument, hasItem("someElement"));
    }
}

上述代码首先创建了一个参数列表,然后mock了一个List对象通过调用addAll函数来将参数添加到mockedList中。使用verify来验证mockedList调用addAll函数时,利用captor捕捉参数。captor是一个ArgumentCaptor实例,存储的是List<String>类型的数据。最后,通过captor.getValue()来获得捕获的参数。

mock final类

java中的final关键字可以用来修饰变量、方法和类。final修饰变量,此变量是不能被修改的;final修饰方法,此方法不能被重写的(override);final修饰类后,此类不能被继承。下面给出一个例子来说明如何mock final类:

final class FinalClassDemo {
    public final String finalMethod() {
        return "something";
    }
}

@Test
public final void mockFinalClassTest() {
    FinalClassDemo instance = new FinalClassDemo();
    
    FinalClassDemo mock = mock(FinalClassDemo.class);
    when(mock.finalMethod()).thenReturn("other things");
    
    assertNotEquals(mock.finalMethod(), instance.finalMethod());
}

注意到,在对final类进行测试时,测试方法也是final的。

以前遇到过使用mock的实例

TWU期间的SpingBoot的Workshop,我们在Spring项目中用到类Mockito。但是,对于Spring项目,我们使用@MockBean注解来给Spring应用添加mock对象。下面的例子是Spring应用中,对于controller层进行测试的测试代码。由于controller层和service层存在依赖,因此需要mock service层的对象来进行测试。

@RunWith(SpringRunner.class)
@WebMvcTest(ProductController.class)
public class ProductControllerTest {
    @Autowired
    private MockMvc mockMvc;

    @MockBean
    private ProductService productService;

    @Test
    public void should_return_201_and_location_when_creating_product() throws Exception {
        //given
        Product product = new Product();
        product.setName("ice peak");

        Product createdProdcut = new Product();
        createdProdcut.setId("id1");
        createdProdcut.setName("ice peak");

        when(productService.create(refEq(product))).thenReturn(createdProdcut);

        mockMvc.perform(post("/products")
                .accept(MediaType.APPLICATION_JSON)
                .param("name","ice peak"))
                .andExpect(status().isCreated())
                .andExpect(header().string("location",containsString("/products/id1")));
    }
}

可以看到,在spring中我们使用注解@RunWith(SpringRunner.class)。对于service层的mock,使用的注解是@MockBean
注意到,我们使用mockMvc来进行测试。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,816评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,729评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,300评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,780评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,890评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,084评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,151评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,912评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,355评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,666评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,809评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,504评论 4 334
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,150评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,882评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,121评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,628评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,724评论 2 351

推荐阅读更多精彩内容