簡介
為什麼要學習 Mock
- 提高測試深度
- 提高測試效率
- 降低成本
- 測試股票軟件,模擬當天股票全部上漲
- 測試股票軟件,模擬當天股票全部下跌
- 測試股票軟件,模擬部分股票漲幅10%
Test Double 測試替身
- Dummy 佔位對象 對象被傳遞但從未實際使用過。通常它們僅用於填充參數列表。
- Fake 假對象 對象實際上有工作實現,但通常採取一些捷徑,這使得它們不適合生產(內存數據庫就是一個很好的例子)。
- Stubs 樁對象 為測試期間調用提供預設答案,通常根本不響應任何超出測試程序的內容。
- Spies 間諜對象 它們還根據調用方式記錄一些信息。其中一種形式可能是電子郵件服務,它記錄發送了多少消息。
- Mocks 模擬對象 是我們在這裏談論的:預先編程的對象,這些期望形成了它們期望接收的調用的規範。
測試替身關鍵概念的區別
一個真實的技術架構例子
- dummy 只要端口開着就行
- fake 內存數據庫
- spy UI 界面後端請求記錄
- stub 假的登錄後端服務
- hook 新用户判斷方法修改
- proxy 代理轉發機制
- mock 模擬對象
Fake 假對象 定義
Fake objects actually have working implementations, but usually take some shortcut which makes them not suitable for production (an InMemoryTestDatabase is a good example).
假對象實際上有工作實現,但通常採取一些捷徑,這使得它們不適合生產(純內存數據庫就是一個很好的例子)。
Fake 應⽤場景
Stub 樁定義
Stubs provide canned answers to calls made during the test, usually not responding at all to anything outside what's programmed in for the test.
為測試期間調用提供預設答案,通常根本不響應任何超出測試程序的內容。
Stub 應⽤場景 Swagger
Setup Stub Server
java -jar moco-runner-1.2.0-standalone.jar http -p 12345 -c foo.json
{
"request" :
{
"uri" : "/foo",
"queries" :
{
"param" : "blah"
}
},
"response" :
{
"text" : "bar"
}
}
Mock 模擬對象定義
Mocks are pre-programmed with expectations which form a specification of the calls they are expected to receive. They can throw an exception if they receive a call they don't expect and are checked during verification to ensure they got all the calls they were expecting.
模擬預編程了期望,這些期望形成了他們期望接收的調用的規範。如果他們收到了他們不期望的調用,他們可以拋出異常,並在驗證過程中進行檢查以確保他們得到了他們期望的所有調用。
Mock 兩種應用場景
- mock on stub:按需返回期望數據
- mock on proxy:按需返回真實數據的修改副本
常⽤的 Mock 工具
- Charles 測試工程師常用
- BurpSuite 黑客常用
- Fiddler 只能 Windows 上使用
- Nginx 服務器反向代理與修改
- Mitmproxy 代理工具 可編程
- Wiremock 代理工具 可編程
mitmproxy
mitmproxy is a set of tools that provide an interactive, SSL/TLS-capable intercepting proxy for HTTP/1, HTTP/2, and WebSockets.
mitmproxy mock on proxy
python3 -m http.server
mitmdump -p 8001 -m reverse:127.0.0.1:8000 --flow-detail 4 -B '/~bs .*Directory.*/Directory/ceshiren.com mock'
mitmproxy 強大的插件機制 Addons
- dns
- tcp
- cert
- http/https
- websocket
adb mock 案例
import sys
from mitmproxy import ctx
from mitmproxy import tcp
from mitmproxy.utils import strutils
from mitmproxy.tools.main import mitmdump
def tcp_message(flow: tcp.TCPFlow):
message = flow.messages[-1]
old_content = message.content
message.content = old_content.replace(
b":0;localabstract:webview_devtools_remote_",
b": 0;localabstract:xweb_devtools_remote_"
)
ctx.log.info(
"[tcp_message{}] from {} to {}:\n{}".format(
" (modified)" if message.content != old_content else "",
"client" if message.from_client else "server",
"server" if message.from_client else "client",
strutils.bytes_to_escaped_str(message.content))
)
if __name__ == '__main__':
sys.argv = ["", "-p", "5038", "--rawtcp", "--mode", "reverse:http://localhost:5037/", "-s", sys.argv[0], "-vv"]
mitmdump()
WireMock
The flexible tool for building mock APIs.
Create stable development environments, isolate yourself from flakey 3rd parties and simulate APIs that don't exist yet.
wiremock stub
java -jar wiremock-jre8-standalone-2.33.2.jar
{ "request": { "method": "GET", "url": "/wiremock" },
"response": { "status": 200, "body": "Easy!" }}
mock on stub
@Test
public void exactUrlOnly() {
stubFor(get(urlEqualTo("/some/thing"))
.willReturn(aResponse()
.withHeader("Content-Type", "text/plain")
.withBody("Hello world!")));
assertThat(testClient.get("/some/thing").statusCode(), is(200));
assertThat(testClient.get("/some/thing/else").statusCode(), is(404));
}
mock on proxy
wm.stubFor(get(urlPathEqualTo("/templated"))
.willReturn(aResponse()
.proxiedFrom("$!request.headers.X-WM-Proxy-Url")
.withTransformers("response-template")));
-
public class ExampleTransformer extends ResponseDefinitionTransformer {
@Override
public ResponseDefinition transform(Request request, ResponseDefinition responseDefinition, FileSource files, Parameters parameters) {
String content=responseDefinition.getTextBody().replace("霍格沃茲", "mock");
System.out.println(responseDefinition.getTextBody());
System.out.println(content);
return new ResponseDefinitionBuilder()
.withHeader("MyHeader", "Transformed")
.withStatus(200)
.withBody(content)
.build();
}
@Override
public String getName() {
return "hogwarts_mock";
}
}
總結
- 瞭解測試替身的實現方式,統一溝通概念
- 瞭解 Mock 的常見應用場景