在日常接口测试的工作中,经常需要依赖其他系统的API,但是联调不常有,只能自己通过mock完成数据依赖。

对于Java栈的mock工具,ThoughtWorks的前工程师郑烨编写了Moco工具,并开源在GitHub,Moco repo:https://github.com/dreamhead/moco。

Moco的优点:

  1. 支持http、https、socket
  2. 支持设置headers、cookies、statuscode
  3. 支持GET、POST、PUT、DELETE请求
  4. 只需JRE环境即可运行
  5. 支持热更新
  6. 支持json、xml、text、file数据格式
  7. 能与JUnit、Maven、Gradle集成

Moco的缺点:

Moco的使用很简单,配置也很方便。但也仅仅是能Stub出接口,模拟出简单的场景。

如果接收到请求后需要做一些处理,如需查询数据库、运算、或者一些复杂的操作,就无能为力了。

但是在工作中,还是可以作为一个稳定的mock工具长期部署,应用于测试中的。

一、独立使用(standalone)

只需要根据mock要求,完成mock.json的配置,启动即可。



vim mock.json

[
    {
        "response": {
            "text": "Hello Moco"
        }
    }
]



启动Moco,java -jar moco-runner-0.12.0-standalone.jar http -p 8080 -c mock.json

TIPS:更多的启动参数,参看GitHub文档。

A、设定URI

JSON中无法添加注释,利用description字段可以清楚的说明接口的功能。



[
    {
        "description": "设定URI",
        "request": {
            "uri": "/uri"
        },
        "response": {
            "text": "Hello Moco GET URI"
        }
    }
]



B、设定请求方式

带参数GET请求,http://localhost:8088/getparam?id=1&status=100,参数通过queries设置k-v对。



[
    {
        "description": "无参GET请求",
        "request": {
            "uri": "/get",
            "method": "get"
        },
        "response": {
            "text": "无参GET请求返回-success"
        }
    },
    {
        "description": "带参GET请求",
        "request": {
            "uri": "/getparam",
            "method": "get",
            "queries": {
                "id": "1",
                "status": "100"
            }
        },
        "response": {
            "text": "带参GET请求返回-success"
        }
    }
]



带参数POST请求,参数通过forms设置k-v对,需要通过工具发送POST请求。



[
    {
        "description": "带参数POST请求",
        "request": {
            "uri": "/postparam",
            "method": "post",
            "forms": {
                "id": "1",
                "status": "100"
            }
        },
        "response": {
            "text": "带参POST请求返回-success",
            "headers": {
                "Content-Type": "text/html;charset=gbk"
            }
        }
    }
]



C、设定Headers及请求返回为JSON



[
    {
        "description": "带headers信息POST请求",
        "request": {
            "uri": "/post/headers",
            "method": "post",
            "headers": {
                "content-type": "application/json"
            },
            "json": {
                "name": "isisiwish",
                "sex": "male"
            }
        },
        "response": {
            "json": {
                "id": "1",
                "status": "100"
            }
        }
    }
]



D、设定Cookies

如果不获取cookies,直接访问http://localhost:8088/get/with/cookies,则返回失败;如果首先访问http://localhost:8088/getcookies,获取到服务器cookies,再访问http://localhost:8088/get/with/cookies,不用额外设定cookies也可以获得正确返回。



[
    {
        "description": "返回cookies信息的GET请求",
        "request": {
            "uri": "/getcookies",
            "method": "get"
        },
        "response": {
            "cookies": {
                "key": "63a9f0ea7bb98050796b649e85481845"
            },
            "text": "get cookies success"
        }
    },
    {
        "description": "携带cookies信息的GET请求",
        "request": {
            "uri": "/get/with/cookies",
            "method": "get",
            "cookies": {
                "key": "63a9f0ea7bb98050796b649e85481845"
            }
        },
        "response": {
            "text": "the cookies is okay"
        }
    },
    {
        "description": "携带cookies信息的POST请求",
        "request": {
            "uri": "/post/with/cookies",
            "method": "post",
            "cookies": {
                "key": "63a9f0ea7bb98050796b649e85481845"
            },
            "json": {
                "name": "isisiwish",
                "age": "30"
            }
        },
        "response": {
            "status": 200,
            "json": {
                "id": "1",
                "status": "100"
            }
        }
    }
]



E、重定向



[
    {
        "description": "绝对路径重定向",
        "request": {
            "uri": "/redirect"
        },
        "redirectTo": "腾讯首页"
    },
    {
        "description": "相对重定向",
        "request": {
            "uri": "/redirect/top"
        },
        "redirectTo": "/redirect/max"
    },
    {
        "description": "被重定向的请求",
        "request": {
            "uri": "/redirect/max"
        },
        "response": {
            "text": "top == max"
        }
    }
]



F、返回类型为file内容

对于比较大的JSON,可以通过文件方式返回。



[
    {
        "request": {
            "uri": "/file"
        },
        "response": {
            "file": "data.json"
        }
    }
]



G、HTTPS

通过Java的keytools生成密钥,具体生成方式参考tomcat-https,启动moco时指定密钥文件即可。



java -jar moco-runner-0.12.0-standalone.jar https -p 8080 -c mock.json --https tomcat.keystore --cert 123456 --keystore 123456





java mock接口失效 java mock 模拟接口_json


H、支持多重文件引入(类似nginx的conf)

Moco支持在配置文件中引入其他配置文件,对于按照URI分组的可以通过子JSON的方式进行mock的组织,更加便于阅读和管理。

启动参数,需要从-c修改为-g,如果只想引入多个配置,可以不适用context字段。

根JSON内容如下:


[
    {
        "context": "/",
        "include": "./json/tmall.json"
    },
    {
        "context": "/",
        "include": "./json/pdd.json"
    }
]


子JSON如下:


# cat tmall.json
[
    {
        "description": "tmall.trades.details.tmallAdmin.get.proxy.url",
        "request": {
            "uri": "/si-app-openapiPROXY/router",
            "method": "post"
        },
        "response": {
            "file": "/data01/moco/json/tmall/order.json"
        }
    },
    {
        "request": {
            "uri": "/prodstock/queryprodstock.xhtml",
            "method": "get"
        },
        "response": {
            "file": "/data01/moco/json/tmall/product_warehouse_stock.xml"
        }
    }
]

# cat pdd.json
[
    {
        "request": {
            "uri": "/api/router",
            "method": "post",
            "forms": {
                "type": "pdd.order.information.get"
            }
        },
        "response": {
            "json": {}
        }
    },
    {
        "request": {
            "uri": "/api/router",
            "method": "post",
            "forms": {
                "type": "pdd.invoice.application.query"
            }
        },
        "response": {
            "json": {}
        }
    }
]


I、正则表达式

支持正则方式对URI进行返回。


[
    {
        "request": {
            "uri": {
                "match": "/getUser/w+"
            }
        },
        "response": {
            "text": "isisiwish"
        }
    }
]


J、延迟返回


[
    {
        "request": {
            "uri": {
                "match": "/daily/w+"
            }
        },
        "response": {
            "latency": {
                "duration": 5,
                "unit": "second"
            },
            "text": "daily 5 second"
        }
    }
]


K、template

以上的请求参数的值和返回值都是固定的,从0.8版本之后,Moco提供了template功能,可以动态返回一些参数值。


[
    {
        "request": {
            "uri": "/getInfo"
        },
        "response": {
            "text": {
                "template": "${req.queries['name']}"
            }
        }
    }
]


template支持以下语法。


"template": "${req.version}"
"template": "${req.method}"
"template": "${req.content}"
"template": "${req.headers['foo']}"
"template": "${req.queries['foo']}"
"template": "${req.forms['foo']}"
"template": "${req.cookies['foo']}"


请求https://localhost:8088/getInfo?name=isisiwish,返回isisiwish。

更多request的设置,查看GitHub文档。

二、JUnit

Moco也支持API方式运行,可以轻松通过Maven和JUnit集成。


<dependencies>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
    </dependency>
    <dependency>
        <groupId>com.github.dreamhead</groupId>
        <artifactId>moco-core</artifactId>
        <version>0.12.0</version>
    </dependency>

    <!--other dependency-->
    <!--利用了fluent-hc客户端请求-->
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>fluent-hc</artifactId>
        <version>4.5</version>
    </dependency>
</dependencies>


代码:


@Test
public void should_response_as_expected() throws Exception
{
    // prepare a mock server
    HttpServer server = httpServer(8080);
    // mock.json
    server.response("mock") ;

    running(server, new Runnable()
    {
        public void run() throws IOException
        {
            Content content = Request.Get("http://localhost:8080").execute().returnContent();
            assertThat(content.asString(), is("foo"));
        }
    });
}


A、大量请求模拟

实际测试工作中,会有很多接口需要mock,需要引入新的jar包。


<dependency>
    <groupId>com.github.dreamhead</groupId>
    <artifactId>moco-junit</artifactId>
    <version>0.12.0</version>
</dependency>
<dependency>
    <groupId>com.github.dreamhead</groupId>
    <artifactId>moco-runner</artifactId>
    <version>0.12.0</version>
</dependency>


MocoJunitRunner加载resources下的JSON配置文件,然后自动构建一个mock的服务器。


public class MocoTest
{
    @Rule
    public MocoJunitRunner runner = MocoJunitRunner.jsonHttpRunner(8080, Moco.pathResource("mock.json"));

    @Test
    public void noUriTest() throws IOException
    {
        Content content = Request.Get("http://localhost:8080").execute().returnContent();
        assertThat(content.asString(), is("Hello Moco"));
    }

    @Test
    public void withParamAndHeaderTest() throws IOException
    {
        HttpResponse httpResponse = Request.Get("http://localhost:8080/hust?param=zw").execute().returnResponse();
        assertThat(httpResponse.getFirstHeader("SelfHeader").getValue(), is("SelfHeader"));
    }
}


更多和JUnit的结合,查看GitHub文档。