差異處

這裏顯示兩個版本的差異處。

連向這個比對檢視

Both sides previous revision 前次修改
下次修改
前次修改
java:wiremock:record-disconnection-behavior [2021/08/07 22:51]
tony [Problem]
java:wiremock:record-disconnection-behavior [2023/06/25 09:48] (目前版本)
行 14: 行 14:
 {{:​java:​wiremock:​wm_test_connection_reset_and_get_500_error_code.png|}}\\ {{:​java:​wiremock:​wm_test_connection_reset_and_get_500_error_code.png|}}\\
 \\ \\
-這導致SUT接到非預期的500 status code,使工作的執行無法順利完成。本篇文章主要分享我的解決方法。+這導致SUT接到非預期的500 status code,使工作的執行無法順利完成。本篇文章主要分享我的解決方法。(code: [[https://​github.com/​frank007love/​wiremock-redfish|link]])
 ===== How to? ===== ===== How to? =====
 +(對嘗試過程沒興趣可以直接看方法2)
 +===== 方法1 =====
 +我的第一個方法,是直接找到造成500問題的地方([[https://​github.com/​tomakehurst/​wiremock/​blob/​1e7c07e9ff845b3253c8177159775ddd070c22a4/​src/​main/​java/​com/​github/​tomakehurst/​wiremock/​http/​ProxyResponseRenderer.java|link]] ProxyResponseRenderer),修改WireMock程式碼直接retry。這會讓工作能夠繼續執行並完成。最後我透過修改錄製出來的腳本,加上長時間的response delay強迫client timeout去模擬類似情境:​
 +<code json>
 +"​response":​ {
 +        "​status":​ 200,
 +        "​fixedDelayMilliseconds":​ 120000
 +}
 +</​code>​
 +Note. 嘗試過在HttpClientFactory加socket timeout是沒有幫助的,這受到作業系統設定限制。
 +===== 方法2 =====
 +後來研究了一下[[http://​wiremock.org/​docs/​extending-wiremock/​|Exteding WireMock]]內容後,覺得應該有機會可以透過擴充的方式去達到我的目的,因此有了方法2。
 +==== Extend ResponseTransformer ====
 +首先我看上了ResponseTransformer,它可以讓你對Response加工。在[[http://​wiremock.org/​docs/​simulating-faults/​|Simulating Faults]]內容有提及WireMock模擬Fault的一些方式,其中這部分內容激起了我的靈感:​
 +<code json>
 +{
 +    "​request":​ {
 +        "​method":​ "​GET",​
 +        "​url":​ "/​fault"​
 +    },
 +    "​response":​ {
 +        "​fault":​ "​MALFORMED_RESPONSE_CHUNK"​
 +    }
 +}
 +</​code>​
 +我想應該可以根據connection timed out這種特定錯誤情境,讓WireMock產生Fault的Response,在這裡我使用了Fault.CONNECTION_RESET_BY_PEER:​
 +<code java>
 +public class SocketTimedOutResponseTransformer extends ResponseTransformer {
 +
 +    @Override
 +    public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
 +    if(response.getStatus()==500&&​response.getBodyAsString().contains("​Network failure"​)) {
 +    return Response.Builder.like(response)
 +    .body(String.valueOf(Fault.CONNECTION_RESET_BY_PEER))
 +    .fault(Fault.CONNECTION_RESET_BY_PEER).build();​
 +    }
 +        return response;
 +    }
 +
 +    @Override
 +    public String getName() {
 +        return "​SocketTimedOutResponseTransformer";​
 +    }
 +}
 +</​code>​
 +response我除了模擬fault以外,也在body塞Fault.CONNECTION_RESET_BY_PEER;這是另一段故事,請讓我在下一段做說明。
 +==== Extend StubMappingTransformer ====
 +在套用上面方法後,工作是能夠如下圖執行完畢:​\\
 +{{:​java:​wiremock:​wm_simulate_connection_reset.png|}}\\
 +但產生出來的腳本並不包含fault動作,也代表著無法重現整個流程。我Trace了一下WireMock程式碼,發現LoggedResponse轉為ResponseDefinition時,並沒有取用Fault欄位,這導致了資料的遺失:​\\
 +{{:​java:​wiremock:​wm_src_apply_loggedresponse.png|}}\\
 +因此我將腦筋動到了另外一個Extension StubMappingTransformer上。與前一個extension成對,當收到500且有Fault的ResponseDefinition時,就會把body內的字串轉為Fault並產生新的ResponseDefinition,最後塞到StubMapping讓它有辦法產生我們預期的內容:​
 +<code java>
 +public class FaultStubMappingTransformer extends StubMappingTransformer {
 +
 +
 +    @Override
 +    public String getName() {
 +        return "​FaultStubMappingTransformer";​
 +    }
 +
 +    private Fault getFault(ResponseDefinition responseDef) {
 +    String body = Objects.toString(new String(responseDef.getByteBody()),​ ""​);​
 +    try {
 + return Fault.valueOf(body);​
 +    } catch (IllegalArgumentException e) {
 + return null;
 + }
 +    }
 +    ​
 +    private void setupFaultResponseDef(StubMapping stubMapping) {
 +    ResponseDefinition responseDef = stubMapping.getResponse();​
 +    Fault fault = getFault(responseDef);​
 +    if( fault == null )
 +    return;
 +   
 +    ResponseDefinition faultResponseDef = ResponseDefinitionBuilder.like(responseDef).withFault(fault).build();​
 + stubMapping.setResponse(faultResponseDef);​
 +    }
 +    ​
 + @Override
 + public StubMapping transform(StubMapping stubMapping,​ FileSource files, Parameters parameters) {
 + ResponseDefinition responseDef = stubMapping.getResponse();​
 +
 + if( responseDef.getStatus() == 500) {
 + setupFaultResponseDef(stubMapping);​
 + }
 + return stubMapping;​
 + }
 +}
 +</​code>​
 +最後產生的mapping file終於有fault的字眼:​
 +<code json>
 + "​response"​ : {
 +    "​status"​ : 500,
 +    "​base64Body"​ : "​Q09OTkVDVElPTl9SRVNFVF9CWV9QRUVS",​
 +    "​fault"​ : "​CONNECTION_RESET_BY_PEER"​
 +  }
 +</​code>​
  
 =====    ===== =====    =====