• Mockito鸡尾酒第一杯 Java单测Mock


    鸡尾酒

    Mockito是Java的单元测试Mock框架。

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

    Mojito鸡尾酒,源自古巴的哈瓦那,带有浓厚的加勒比海风情。

    并不浓烈,但是喝一杯下去,脸上会泛起红晕,象少女的羞涩。味道很清新,有一点青涩、有点甜蜜。

    logo_副本

    巧的是,我才发现周董的新歌,也叫《Mojito》。哈哈哈。

    Stub & Mock

    Stub和Mock是Test Double类型中的2种。Test Double一共有5种类型,Dummy,Stub,Spy,Mock,Fake。

    img

    Test Double是测试复制品,用来统称模拟真实对象的假对象。因使用场景有略微不同,而有这5种类型。

    • Dummy,通常只用来填充参数列表。有可能是null对象引用,或Object类实例等。
    • Fake,是简化版的实现,比如基于内存实现的数据库,不会真的去做数据库操作,用简单的HashMap来存放数据。
    • Stub,Stub用来替代SUT(System Under Test)依赖的组件,但是只模拟一个外部依赖,不做断言。
    • Spy,介于Stub和Mock之间。如果真实对象没有被打桩,当调用Spy对象时,真实对象也会被调用。
    • Mock,可以理解为Stub+Verification,既模拟外部依赖,也会定义预期结果。

    不管你有没有懵逼,反正我是懵逼了。不着急,慢慢来,先搞懂Stub和Mock。

    看一个实例,发送邮件,

    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)


    所有文章公众号【测试开发刚哥】首发!

    版权申明:本文为博主原创文章,转载请保留原文链接及作者。
  • 相关阅读:
    数据准备2 数据清洗
    数据准备1 数据导入、导出
    数据分析基本流程 Python基本数据类型 Python各种括号的使用方式
    fineBI 学习成果展示1
    未确认融资收益的计算
    合同现金流量
    公允价值持续计算的金额
    发放股票股利
    权益法未实现内部交易损益的调整
    营业外收入入不入损益
  • 原文地址:https://www.cnblogs.com/df888/p/13543471.html
Copyright © 2020-2023  润新知