Mockito雞尾酒第一杯 Java單測Mock

雞尾酒

Mockito是Java的單元測試Mock框架。html

它的logo是一杯古巴最著名的雞尾酒Mojito,java

Mojito雞尾酒,源自古巴的哈瓦那,帶有濃厚的加勒比海風情。數據庫

並不濃烈,可是喝一杯下去,臉上會泛起紅暈,象少女的羞澀。味道很清新,有一點青澀、有點甜蜜。框架

logo_副本

巧的是,我才發現周董的新歌,也叫《Mojito》。哈哈哈。單元測試

Stub & Mock

Stub和Mock是Test Double類型中的2種。Test Double一共有5種類型,Dummy,Stub,Spy,Mock,Fake。測試

img

Test Double是測試複製品,用來統稱模擬真實對象的假對象。因使用場景有略微不一樣,而有這5種類型。code

  • Dummy,一般只用來填充參數列表。有多是null對象引用,或Object類實例等。
  • Fake,是簡化版的實現,好比基於內存實現的數據庫,不會真的去作數據庫操做,用簡單的HashMap來存放數據。
  • Stub,Stub用來替代SUT(System Under Test)依賴的組件,可是隻模擬一個外部依賴,不作斷言。
  • Spy,介於Stub和Mock之間。若是真實對象沒有被打樁,當調用Spy對象時,真實對象也會被調用。
  • Mock,能夠理解爲Stub+Verification,既模擬外部依賴,也會定義預期結果。

無論你有沒有懵逼,反正我是懵逼了。不着急,慢慢來,先搞懂Stub和Mock。xml

看一個實例,發送郵件,htm

public interface MailService {
    public void send(Message msg);
}

先寫個Stub,對象

public class MailServiceStub implements MailService {
    private List<Message> messages = new ArrayList<Message>();

    public void send(Message msg) {
        messages.add(msg);
    }

    public int numberSent() {
        return messages.size();
    }
}

實現Stub的狀態驗證,

class OrderStateTester...

public void testOrderSendsMailIfUnfilled() {
    Order order = new Order(TALISKER, 51);
    MailServiceStub mailer = new MailServiceStub();
    order.setMailer(mailer);
    order.fill(warehouse);
    assertEquals(1, mailer.numberSent());
}

只作了簡單的測試,斷言發出了1封郵件。沒有測試是否發送給了對的收件人,或者郵件正文是否正確。不過不影響跟Mock比較。

若是用Mock,會怎麼寫呢?

class OrderInteractionTester...

public void testOrderSendsMailIfUnfilled() {
    Order order = new Order(TALISKER, 51);
    Mock warehouse = mock(Warehouse.class);
    Mock mailer = mock(MailService.class);
    order.setMailer((MailService) mailer.proxy());

    mailer.expects(once()).method("send");
    warehouse.expects(once()).method("hasInventory")
        .withAnyArguments()
        .will(returnValue(false));

    order.fill((Warehouse) warehouse.proxy());
}

結合一張圖,就一下全明白了,

2020-08-21_172121_副本

怎麼喝Mockito?

添加Maven dependency,

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.3.3</version>
    <scope>test</scope>
</dependency>

如今能夠開始Mock了,先Mock一個List Interface試試,(示例只是玩語法,實際應使用instance)

//Let's import Mockito statically so that the code looks clearer
import static org.mockito.Mockito.*;

// mock creation
List mockedList = mock(List.class);

// using mock object
mockedList.add("one");
mockedList.clear();

// verification
verify(mockedList).add("one");
verify(mockedList).clear();

接着用Mockito來作Stub,淡定,我知道你在懷疑什麼。咱們在實際使用的時候,不要被理論的概念限制了。Mockito爲何不能Stub,Stub不就是模擬外部依賴嘛,模擬了不就是Stub了嘛。

// You can mock concrete classes, not just interfaces
LinkedList mockedList = mock(LinkedList.class);

// stubbing
when(mockedList.get(0)).thenReturn("first");
when(mockedList.get(1)).thenThrow(new RuntimeException());

// following prints "first"
System.out.println(mockedList.get(0));

// following throws runtime exception
System.out.println(mockedList.get(1));

// following prints "null" because get(999) was not stubbed
System.out.println(mockedList.get(999));

// Although it is possible to verify a stubbed invocation, usually it's just redundant
// If your code cares what get(0) returns, then something else breaks 
// (often even before verify() gets executed).
// If your code doesn't care what get(0) returns, then it should not be stubbed.
verify(mockedList).get(0);

mock默認會返回null,或原始值,或空集合。如int/Integer返回0,boolean/Boolean返回false。

第一杯先打個樣,喝完這杯,還有「三」杯。

參考資料
https://javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html

https://martinfowler.com/articles/mocksArentStubs.html

[http://xunitpatterns.com/Test Double.html](http://xunitpatterns.com/Test Double.html)


專一測試,堅持原創,只作精品。歡迎關注公衆號『東方er』

版權申明:本文爲博主原創文章,轉載請保留原文連接及做者。

相關文章
相關標籤/搜索