使用 Moq 框架模拟 Web 服务方法的请求和响应

问题描述 投票:0回答:1

总结:我希望能够模拟调用远程服务器的 SOAP Web 方法的请求和响应。我希望能够测试此 SOAP Web 方法 CustomerCareResult 的整个请求和响应功能,而无需进行远程服务器调用。这意味着我将不得不使用mocking。我想使用 C#.NET 中的 Moq 框架来模拟传入请求和 CustomerCareResult 的响应输出。我对用 C# 编写模拟测试完全陌生,弄清楚这一点对我来说非常令人沮丧。任何帮助将不胜感激。

注意:这里有一些伪代码,因为整体代码非常复杂,我只想给出它正在做什么的一般想法,而不添加不必要的细节。

我有一个包含 SOAP WebMethod 的 C#.NET 类和另外 2 个方法,如下所示:

public class TransactionService : System.Web.Services.WebService, 
TransactionServiceNS.ITransactionServiceSoap
{
    //This is a SOAP webservice method
    [WebMethod()]
    public CustomerPutResponse CustomerCareResult(CustomerRequest request)
    {
         //The incoming request parameter of type 'CustomerRequest' 
         //contains the fields: Name, Address, City, State, and AccountNumber
         
         //Call the ApiCreateRequestObj method, passing in the request parameter. This makes 
         //a call to a remote server:
         var apiRequestObj = ApiCreateRequestObj(request);

         //Call the ApiCreateResponseObj method, passing in apiRequestObj, this makes
         //a call to a remote server:
         var apiResponseObj = ApiCreateResponseObj(apiRequestObj );
         return apiResponseObj;
    }

    private CustomerPutRequest ApiCreateRequestObj(CustomerRequest req)
    {
        // PSEUDOCODE:
        //Makes a remote server call, does some processing on the 
       //CustomerRequest req parameter, and returns a "CustomerPutRequest" 
      //object containing all the fields of the req object
        
        //So after processing:
        return new CustomerPutRequest{Name = _req.Name, Address = _req.Address,
        City = _req.City, State = _req.State}
    }

    private CustomerPutResponse ApiCreateResponseObj(CustomerPutRequest cpReq)
    {
        // PSEUDOCODE:
        //Makes a remote server call, does some processing on the cpReq object and returns a 
        //"CustomerPutResponse" object containing the response data
        

        //So after processing:
        return new CustomerPutResponse{/*contains response data fields*/}
    }
}

如您所见,SOAP Web 方法 CustomerCareResult 接受“CustomerRequest”参数,然后调用一个进行远程数据库调用的方法 ApiCreateRequestObject()。这会进行一些处理并返回一个“CustomerPutRequest”对象,其中包含传入的 CustomerRequest 对象的字段。

然后,我将此 CustomerPutRequest 对象传递给 ApiCreateResponseObject() 方法,该方法又进行远程服务器调用,进行自己的一些处理,然后返回包含响应数据的“CustomerPutResponse”对象,该对象由 Web 方法 CustomerCareResult 返回。

我想使用 C#.NET 中的 Moq 框架来模拟 Web 方法 CustomerCareResult 的传入请求和响应输出。我对用 C# 编写模拟测试完全陌生,弄清楚这一点对我来说非常令人沮丧。任何帮助将不胜感激。

c# soap request response moq
1个回答
0
投票

为了测试 TransactionService 类而不实际调用外部服务,请确保您使用的是接口:

public class TransactionService
{
    private readonly IDatabaseService _databaseService;

    public TransactionService(IDatabaseService databaseService)
    {
        _databaseService = databaseService;
    }

    public CustomerPutResponse CustomerCareResult(CustomerRequest request)
    {
        var apiRequestObj = ApiCreateRequestObj(request);

        var apiResponseObj = ApiCreateResponseObj(apiRequestObj);
        
        return apiResponseObj;
    }

    private CustomerPutRequest ApiCreateRequestObj(CustomerRequest req)
    {
        return _databaseService.createPutRequest(req);
    }

    private CustomerPutResponse ApiCreateResponseObj(CustomerPutRequest resp)
    {
        return _databaseService.createPutResponse(resp);
    }
}

如果您想测试 TransactionService 内部的逻辑,而不是外部 DatabaseService 内部的逻辑,您应该使用 TransactionService 中的 IDatabaseService,并从测试中模拟 IDatabaseService 接口。真正的 DatabaseService 实现只会在您的应用程序中运行,但您的测试不会通过它:

public interface IDatabaseService
{
    public CustomerPutRequest createPutRequest(CustomerRequest request);

    public CustomerPutResponse createPutResponse(CustomerPutRequest putRequest); 
}

public class DatabaseService : IDatabaseService
{
    public CustomerPutRequest createPutRequest(CustomerRequest request)
    {
        //PSEUDOCODE: Real code for your real service that returns a customerPutRequest
    }

    public CustomerPutResponse createPutResponse(CustomerPutRequest putRequest)
    {
        //PSEUDOCODE: Real code for your real service that returns a customerPutResponse
    }
}

最后,在您的测试类中(在此示例中使用 xUnit),您应该测试类的公共方法,在本例中为方法 CustomerCareResult:

public class TransactionServiceTests
{
    [Fact]
    public void CustomerCareResult_UponCalled_ReturnsExpectedResponse()
    {
        // Arrange (instantiate input parameters, mocks and the object to test (sut))
        var customerRequest = new CustomerRequest
        {
            Name = "Joe Biden",
            Address = "White House",
            City = "Washington DC",
            State = "Columbia",
            AccountNumber = "666"
        };

        //Create the mock for the external service we don't really want to call
        var dataServiceMock = new Mock<IDatabaseService>();

        //Set up the mock to behave as we would expect the real class to behave when calling its methods.
        //In this case, to return the expected value whenever the external IDatabaseService.createPutRequest is called
        dataServiceMock .Setup(m => m.createPutRequest(It.IsAny<CustomerRequest>()))
                        .Returns(new CustomerPutRequest
                        {
                            Name = "Joe Biden",
                            Address = "White House",
                            City = "Washington DC",
                            State = "Columbia"
                        });

        dataServiceMock.Setup(m => m.createPutResponse(It.IsAny<CustomerPutRequest>()))
                        .Returns(new CustomerPutResponse
                        {
                            Data = "this is the mocked data I expect my real external service to return"
                        });

        var sut = new TransactionService(dataServiceMock.Object);

        // Act (call a method on the sut)
        sut.CustomerCareResult(customerRequest);

        // Assert (verify that each of the 2 the methods in the IDatabaseService mock have been called exactly once)
        dataServiceMock.Verify(m => m.createPutRequest(It.IsAny<CustomerRequest>()), Times.Once());
        dataServiceMock.Verify(m => m.createPutResponse(It.IsAny<CustomerPutRequest>()), Times.Once());
    }
}

在测试方法中,使用 Moq 为 IDatabaseService 服务创建 2 个模拟。在设置部分,我们告诉 Moq,当事务服务因调用公共方法 CustomerCareResult 而在后台调用 _databaseService.createPutRequest(req) 和 _databaseService.createPutResponse(resp) 时,我们希望 Moq 如何响应。

在测试的最后一部分,我们断言每个方法都被调用了一次。当然,如果您在 CustomerCareResult 内部有更复杂的逻辑,根据您请求的信息,您可能会为同一个方法 CustomerCareResult 编写更多具有不同断言的测试

© www.soinside.com 2019 - 2024. All rights reserved.