【问题标题】:How to mock two consecutive web requests如何模拟两个连续的网络请求
【发布时间】:2013-06-21 10:51:41
【问题描述】:

我正在尝试测试一种发出两个连续 Web 请求的方法。

到目前为止,我只使用 this solution using an object called TestWebRequest 测试了单个请求,这对于单个请求来说就像一个魅力。如果您不熟悉它,TestWebRequest 会拦截 Web 请求机制并在幕后工作以返回预期的响应。这样就可以在不使用模拟的情况下测试实际服务。

但是现在我的一个方法的逻辑已经变成了这样的东西(在伪代码中)

public void MakePayment(PaymentRequest request)
{
    var statusRequest = webService.GetPaymentStatus(new PaymentStatusRequest()
            {
                OrderId = request.OrderId
            });

    if (statusRequest.StatusId != 100)
    {
     var responseXml = webService.GetResponse(GetRequestXml(request));
     /*.. process response ..*/
    }
}

有没有办法扩展TestWebRequest 来处理两个请求,或者我可以使用替代方法来测试这个方法?

这是一个工作测试的例子:

 [Test]
 public void MakePayment()
 {
      var response = @"<XML>..</XML>";
      TestWebRequest request = TestWebRequestCreate.CreateTestRequest(response);
      var result = webService.MakePayment(1, new PaymentRequest());
      var expectedResponse = new PaymentResponse()
      {
          Status = 23
      };
      Assert.AreEqual(expectedResponse, result);  
 }

现在如果 MakePayment 突然发出两个请求,那么我们需要一种方法来模拟两个假响应,如下所示:

 [Test]
 public void MakePayment()
 {
      var response1 = @"<XML>..</XML>";
      var response2 =  @"<XML>..</XML>";
      TestWebRequest request1 = TestWebRequestCreate.CreateTestRequest(response1);
      TestWebRequest request2= TestWebRequestCreate.CreateTestRequest(response2);
      var result = webService.MakePayment(1, new PaymentRequest());
      var expectedResponse = new PaymentResponse()
      {
          Status = 23
      };
      Assert.AreEqual(expectedResponse, result);  
 }

这不起作用。

提前致谢

【问题讨论】:

  • 您能发布您要测试的实际代码而不是伪代码吗?我有一个答案给你,但我不得不做出太多假设,因为我在猜测你实际上在测试什么
  • 我刚刚发布了实际代码。我只是在同一个方法中发出两个 Web 请求。如果我做一个,那么测试通过,但如果我做两个,那么 TestWebRequest 返回相同的响应。我不能让它以特定的顺序返回两个不同的响应

标签: asp.net-mvc-3 unit-testing c#-4.0 httpwebrequest moq


【解决方案1】:

您是否测试了您提供的示例作为工作测试?这不会通过,因为expectedResponse 在您的测试代码中被实例化并且不能与从webService.MakePayment 调用返回的对象相同。您链接到的文章是测试 2 个字符串是否相等,而不是 2 个复杂类型。您实际上也没有使用您在测试中实例化的 TestWebRequest 对象。

也就是说,您可以通过控制创建并传递给 webRequest.MakePayment 的 2 个调用的 2 个请求或使用 Moq 的回调功能来完成您要测试的内容,例如(未经测试的代码):

webRequestMock.Setup(x => x.MakePayment(1, It.IsAny<PaymentRequest>()).Returns(It.IsAny<PaymentResponse>()).CallBack((PaymentRequest request, PaymentResponse response) => VerifyResponse(request, response));

private void VerifyResponse(PaymentRequest request, PaymentResponse response) 
{
    // do some checks to determine whether it's the first call or the second, based on something uniquely different between to 2 requests - I need more actual code from you before I can tidy this up
    // e.g. (pseudocode)
    if (request.SomeValue == firstRequestValue) 
    {
        response.ShouldEqual(firstReponse);
    }
    else 
    {
        response.ShouldEqual(secondReponse);
    }

}

我说这是未经测试的代码,因为我不确定是否可以捕获返回的响应并将其传递给回调(因为它是返回值)。

【讨论】:

  • webRequest 不是一个模拟,它是实际的服务,那是因为我正在测试它。 TestWebRequest 在 webRequest 服务内部使用,因此当它尝试发布 XML 时,TestWebRequest 将返回预期的 responseXML。这是测试 Web 服务的一种非常聪明的方法,但我无法将其扩展为处理多个请求
【解决方案2】:

我通过更新 TestWebReqestCreate 工厂方法来接受两个请求。

这是更新的代码。它不是很优雅,但适用于两个请求,您不必为单个请求更改所有现有测试。

我想它可以更新为链接任意数量的请求,但我现在不需要这样做,所以我想出了一些快速的方法。

我希望能帮助像我一样被困的人

internal class TestWebRequestCreate : IWebRequestCreate
    {
        static WebRequest nextRequest;
        static WebRequest nextRequest2;
        private static bool used = false;
        static object lockObject = new object();

        static public WebRequest NextRequest
        {
            get { return nextRequest; }
            set
            {
                lock (lockObject)
                {
                    nextRequest = value;
                }
            }
        }

        /// <summary>See <see cref="IWebRequestCreate.Create"/>.</summary>
        public WebRequest Create(Uri uri)
        {
            if (used)
            {
                return nextRequest2;
            }
            else
            {
                used = true;
                return nextRequest;
            }
        }

        /// <summary>Utility method for creating a TestWebRequest and setting
        /// it to be the next WebRequest to use. It can now accept a subsequent request</summary>
        /// <param name="response1">The response the TestWebRequest will return.</param>
        /// <param name="response2">The subsequent response the TestWebRequest will return.</param>
        public static TestWebRequest CreateTestRequest(string response1, string response2 = null)
        {
            TestWebRequest request1 = new TestWebRequest(response1);
            used = false;
            nextRequest = request1;

            if (!string.IsNullOrEmpty(response2))
            {
                TestWebRequest request2 = new TestWebRequest(response2);
                nextRequest2 = request2;
            }

            return request1;
        }
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-04
    • 2021-01-23
    • 1970-01-01
    相关资源
    最近更新 更多