簡體   English   中英

如何在 finally 塊中使用 Response.OnCompleted 委托進行測試

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM