Search code examples
c#unit-testing.net-coremoqsoap-client

How to mock SOAP client that was auto-generated by Visual Studio?


I have an auto-generated SOAP client. It was generated by visual studio wizard (add connected services -> Microsoft WCF Web Service Reference Provider). I would like to mock that client, so when a method is called, a predefined result is going to be returned in a format of the SOAP response. Unfortunately, I cannot get it to work - my result is either null (instead of defined in .Returns) or I get an exception.

I am trying to apply clean architecture in my design. So this SOAP client landed in my infrastructure layer, where I have created a repository for it. This repository creates DTOs, so they can be dispatched to my persistence. The repository receives the SOAP client through dependency injection. I would also like to have tests for the repository, just to validate that DTOs generation is correct. So, I would like to mock this SOAP service, so I can feed it to the repository and test the returned DTOs.

Auto-generated interface:

    public interface ApplicationSoap
    {
        [System.ServiceModel.OperationContractAttribute(Action = "http://Application/GetAppVersion", ReplyAction = "*")]
        Task<ExtApp.GetAppVersionResponse> GetAppVersionAsync(ExtApp.GetAppVersionRequest request);
    }

and auto-generated client class:

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.0.1-preview-30514-0828")]
    public partial class ApplicationSoapClient : System.ServiceModel.ClientBase<ExtApp.ApplicationSoap>, ExtApp.ApplicationSoap
    {

        static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);

        [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)]
        System.Threading.Tasks.Task<ExtApp.GetAppVersionResponse> ExtApp.ApplicationSoap.GetAppVersionAsync(ExtApp.GetAppVersionRequest request)
        {
            return base.Channel.GetAppVersionAsync(request);
        }

        public System.Threading.Tasks.Task<ExtApp.GetAppVersionResponse> GetAppVersionAsync(int appVer)
        {
            ExtApp.GetAppVersionRequest inValue = new ExtApp.GetAppVersionRequest();
            inValue.Body = new ExtApp.GetAppVersionRequestBody();
            inValue.Body.appVer = appVer;
            return ((ExtApp.ApplicationSoap)(this)).GetAppVersionAsync(inValue);
        }
    }

I want to mock ApplicationSoapClient and method GetApplicationVersionAsync. After different attempts I ended up the following in my test class:

private ExtApp.AppVersion[] _response =
    new ExtApp.AppVersion[]
        {
         new ExtApp.AppVersion
             {
              VersionNumber = 1,
              StartDate = DateTime.Parse("2010-01-01"),
              EndDate = DateTime.Parse("2015-12-31")
             },
        };

private Mock<ExtApp.ApplicationSoap> _client = new Mock<ExtApp.ApplicationSoap>();

public TestClass() 
{
    var body = new ExtApp.GetAppVersionRequestBody(It.IsAny<int>());
    var request = new ExtApp.GetAppVersionRequestRequest(body);
    _client
           .Setup(s => s.GetAppVersionAsync(request))                
           .Returns(
               Task.FromResult(
                   new ExtApp.GetAppVersionResponse(
                       new ExtApp.GetAppVersionResponseBody(_response)))
            );
}

[Fact]
public void TestAppVersionDownload()
{
    var request = new ExtApp.GetAppVersionRequest(
                      new ExtApp.GetAppVersionRequestBody(1));
    var result = _clientSoap.Object.GetAppVersionAsync(request)
                      .Result; //returns null instead of defined in Returns section
    Assert.True(result.Body.GetAppVersionResult.Length == 2);
}

It runs, but Result of the call is null. I am expecting to get back the object that would have a non-null Body property with the array inside. I am looking for advice how to make this thing work.


Solution

  • Your SOAP client contract is:

    public interface ApplicationSoap
    {
        [System.ServiceModel.OperationContractAttribute(Action = "http://Application/GetAppVersion", ReplyAction = "*")]
        Task<ExtApp.GetAppVersionResponse> GetAppVersionAsync(ExtApp.GetAppVersionRequest request);
    }
    

    You use this as a dependency in a repository that could look like this:

    public class Repository
    {
        private readonly IApplicationSoap _client;
    
        public Repository(IApplicationSoap client) { _client = client; }
    
        public async Task<AppVersion> GetAppVersionAsync(int version)
        {
            var request = new GetAppVersionRequest(new GetAppVersionRequestBody(version));
            var response = await _client.GetAppVersionAsync(request);
            return new AppVersion 
            {
                Version = response.Body.Version,
                StartDate = response.Body.StartDate,
                EndDate = response.Body.EndDate
            };
        }
    }
    

    In this case you may want to test the code that converts your input to a request and the code that converts the response to your DTO. This is the only code that is yours (as opposed to not being generated by the tools). To do so you need to mock (in fact stub) the SOAP client contract in your Repository test and have it return the response you want:

    [Fact]
    public async Task GetAppVersionAsync()
    {
        // arrange
        var client = new Mock<IApplicationSoap>(); // mock the interface, not the class!
        var result = new AppVersion
        { 
            Version = 1, 
            StartDate = DateTime.Parse("2010-01-01"),
            EndDate = DateTime.Parse("2015-12-31")
        };
        client.Setup(x => x.GetAppVersionAsync(It.IsAny<GetAppVersionRequest>))
              .Returns(Task.FromResult(new GetAppVersionResponse(new GetAppVersionResponseBody(result))));
        var repository = new Repository(soapApp);
    
        // act
        var dto = await repository.GetAppVersionAsync(1);
    
        // assert (verify the DTO state)
        Assert.Equal(1, dto.VersionNumber);
        Assert.Equal(new DateTime(2010, 1, 1), dto.StartDate);
        Assert.Equal(new DateTime(2015, 12, 31), dto.EndDate);
    }
    

    However... just because you can do this it does not mean that you should.