zoukankan      html  css  js  c++  java
  • 四、使用Mock对象

    很多情况下,代码需要与外部依赖打交道,如一个REST地址,数据库链接、外部IO等;这些依赖有些速度过慢、有些不够稳定,不符合单元测试要求的快速、可重复等原则性要求,因此引入了Mock对象这一概念。与Mock相关的还有Stub这个单词。
    • stub 桩,它针对指定的输入缓存了行为
    • mock 模拟对象,增加了对输入条件校验、注入等功能,简单来说,它保证在收到预期参数时表现出预定义的行为,常用的有两个框架
      • mockito 较为易用
      • powermock 功能更加强大,能够对静态方法和私有函数进行Mock
    一般来说,在编写stub之后,需要将其注入依赖对象中,也即依赖注入(DI),框架上有Spring DI和Google Guice等。

    修改代码结构使其更具可测性

    为了使得测试更加容易,有时需要修改代码,如将依赖以成员变量的形式传入被测类中,如:
    public class AddressRetriever{
        private Http http; //将外部依赖以构造函数的方式引入,对单元测试更加友好
     
        public AddressRetriever(Http http){
            this.http = http;
        }
     
        public Address retrieve(double latitude, double longitude) throws IOException, ParseException{
            String params = String.format("lat=%.6flon=%.6f", latitude, longitude);
            String response = http.get("http://open.mapquestapi.com/nominatim/v1/reverse?format=json&"+params);
     
            JSONObject obj = (JSONObject)new JSONParse().parse(reponse);
            //...
        }
    }
     
    但不仅限于构造函数,还可以通过set方法或其他依赖注入框架实现。

    为Stub增加一点智能

    如这个桩:
    Http http = new Http(){
        @Override
        public String get(String uri) throws IOException{
            return "{"address":{" + ""house_number":"324"," + ""road":"North Tejon Street","
            }
    }
    这个桩接受任何uri即可返回对应的结果,没有对输入进行判断,我们期望的是:在收到预期参数时提供预期的输入,可以通过在get()方法中加入判断实现,这样的通用功能引入Mock工具。

    使用Mock工具简化测试

    public class AddressRetrieverTest {
        @Test
        public void answersAppropriateAddressForValidCoordinates() throws IOException, ParseException {
            Http http = mock(Http.class);
            when(http.get(contains("lat=38.000000&lon=-104.000000"))).thenReturn(
                "{"address":{" + ""house_number":"324","
                // ...
                + "}");
     
            AddressRetriever retriever = new AddressRetriever(http);
            Address address = retriever.retrieve(38.0,-104.0);
     
            assertThat(address.houseNumber, equalTo("324"));
        }
    when().thenReturn()模式就是Mockito设置的常用方式。

    介绍一种DI工具

    DI工具有很多,如Spring DI和Google Guice,但是moctito内建的DI工具也能满足绝大部分的需要,步骤如下:
    • 使用@Mock注解创建一个模拟对象
    • 使用@InjectMocks注解声明一个目标对象
    • 在目标对象初始化完毕后,调用MockitoAnnotations.initMocks(this)方法完成注入
    下面是示例代码:
    public class AddressRetrieverTest{
        @Mock
        private Http http;
     
        @InjectMocks
        private AddressRetriever retriever;
     
        @Before
        public void createRetriever(){
            retriever = new AddressRetriever();
            MockitoAnnotations.initMocks(this);
        }
     
        @Test
        public void answersAppropriateAddressForValidCoordinates() throws IOException, ParseException{
            when(http.get(contains("lat=38.000000&lon=-104.000000")))
                .thenReturn("{"address":{" 
                    + ""house_number":"324","
                          //...
               }
     }
    最后需要注意的是,如果使用了Mock,那不是直接测试生产代码,而是在于生产代码中加了鸿沟,单元测试的正确性依赖于被Mock对象的正确性,因此单元测试需要配合端到端的集成测试。
  • 相关阅读:
    Oracle 备份与恢复介绍
    Oracle 监听器
    ORA-01041: 内部错误,hostdef 扩展名不存在
    NIO读写文件并加锁
    ActiveMQ消息生产消费流程
    金额,有效值等保留小数位处理
    JVM
    Linux架构分布式集群之基础篇
    Vue.js 开发实践:实现精巧的无限加载与分页功能
    Mysql 查看连接数,状态
  • 原文地址:https://www.cnblogs.com/jiyuqi/p/13841602.html
Copyright © 2011-2022 走看看