[英]How to test with Response.OnCompleted delegate in a finally block
我有以下 netcore 2.2 controller 方法,我正在嘗試為其編寫 xUnit 集成測試:
private readonly ISoapSvc _soapSvc;
private readonly IRepositorySvc _repositorySvc;
public SnowConnectorController(ISoapSvc soapSvc, IRepositorySvc repositorySvc)
{
_soapSvc = soapSvc;
_repositorySvc = repositorySvc;
}
[Route("accept")]
[HttpPost]
[Produces("text/xml")]
public async Task<IActionResult> Accept([FromBody] XDocument soapRequest)
{
try
{
var response = new CreateRes
{
Body = new Body
{
Response = new Response
{
Status = "Accepted"
}
}
};
return Ok(response);
}
finally
{
// After the first API call completes
Response.OnCompleted(async () =>
{
// Run the close method
await Close(soapRequest);
});
}
}
catch塊運行並做它需要做的事情,然后finally塊運行並在 catch 中的請求完成每個設計后做它需要做的事情。
Close 一直是一個私有方法。 它以公共 controller 方法開始,但我不需要為 function 公開它,因此將其移至私有方法狀態。
這是我開始的一個集成測試,目的是測試代碼的 try 部分:
[Fact]
public async Task AlwaysReturnAcceptedResponse()
{
// Arrange------
// Build mocks so that we can inject them in our system under tests constructor
var mockSoapSvc = new Mock<ISoapSvc>();
var mockRepositorySvc = new Mock<IRepositorySvc>();
// Build system under test(sut)
var sut = new SnowConnectorController(mockSoapSvc.Object, mockRepositorySvc.Object);
var mockRequest = XDocument.Load("..\\..\\..\\mockRequest.xml");
// Act------
// Form and send test request to test system
var actualResult = await sut.Accept(mockRequest);
var actualValue = actualResult.GetType().GetProperty("Value").GetValue(actualResult);
// Assert------
// The returned object from the method call should be of type CreateRes
Assert.IsType<CreateRes>(actualValue);
}
我是測試的超級新手......我一直在編寫測試並在解決問題時摸索着自己的方式。 我從輸入 controller 方法開始,並不知道它會在哪里 go。 測試通過 try 方法進行,然后一旦遇到finally塊中的委托,就會引發異常。
看起來我的測試將不得不運行到finally塊的結果,除非有辦法告訴它停止執行catch塊?
很好,我正在學習,但是現在這種方法對我來說的問題是,當我的測試運行時,最終塊中的 HttpResponse 的Response.OnCompleted委托返回 null 並且我還沒有成功弄清楚我能做什么不要讓它成為 null - 因為它是 null,所以當我的單元測試正在執行時它會拋出這個 -
System.NullReferenceException: 'Object reference not set to an instance of an object.'
*發生的一個想法是,如果我要使私有 Close 方法成為公共 controller 方法,然后使 Accept 方法沒有finally塊,我可以創建第三個 controller 方法,通過運行這兩個方法來執行 try finally 操作controller 方法,然后只測試與第三種方法串聯在一起的各個 controller 方法。 但是,這感覺不對,因為我只是為了單元測試而公開方法,而我不需要 Close 來公開。
如果上述想法不是正確的方法,我想知道是什么,如果我只需要端到端測試,我將如何克服 null httpresponse?
任何想法,將不勝感激。 謝謝你,社區!
編輯 -在接受的答案實施后更新的測試。 謝謝!
[Fact]
public async Task AlwaysReturnAcceptedResponse()
{
// Arrange------
// Build mocks so that we can inject them in our system under tests constructor
var mockSoapSvc = new Mock<ISoapSvc>();
var mockRepositorySvc = new Mock<IRepositorySvc>();
// Build system under test(sut)
var sut = new SnowConnectorController(mockSoapSvc.Object, mockRepositorySvc.Object)
{
// Supply mocked ControllerContext and HttpContext so that finally block doesnt fail test
ControllerContext = new ControllerContext
{
HttpContext = new DefaultHttpContext()
}
};
var mockRequest = XDocument.Load("..\\..\\..\\mockRequest.xml");
// Act------
// Form and send test request to test system
var actualResult = await sut.Accept(mockRequest);
var actualValue = actualResult.GetType().GetProperty("Value").GetValue(actualResult);
// Assert------
// The returned object from the method call should be of type CreateRes
Assert.IsType<CreateRes>(actualValue);
}
好奇您在 Close 方法中針對輸入參數執行的操作。 是否必須在發送響應后發生? 它可能並不總是像您期望的那樣發生,請參見此處。
無論如何,在運行時 asp.net 核心運行時在 controller 上設置了很多屬性,包括 ControllerContext、HttpContext、請求、響應等。但是這些在單元測試中將不可用,因為沒有 Z1848E732244CCE460F29B 運行時。 如果您真的想對此進行測試,則必須模擬它們。 這是ControllerBase 源代碼。
正如我們所見, ControllerBase.Response
只是返回ControllerBase.HttpContext.Response
,而ControllerBase.HttpContext
是來自ControllerBase.ControllerContext
的 getter。 這意味着您必須模擬一個 ControllerContext(以及嵌套的 HttpContext 和 HttpResponse),並在設置階段將其分配給您的 controller。
此外,在單元測試中也不會調用 OnCompleted 回調。 如果您想對該部分進行單元測試,則必須手動觸發它。
就我個人而言,我認為除了我上面提到的開放錯誤之外,這太麻煩了。
我建議您將關閉邏輯(如果確實有必要)移動到 IDisposable 范圍服務並在 Dispose 中處理它 - 假設它不是會影響響應延遲的計算繁重的操作。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.