简体   繁体   中英

How to mock web service call with Moq?

The using below hits an external resource that I do not want to actually hit. I want to test someResult and the code that uses it, but every time I run my unit test, this code still tries to hit the real web service. How do I use moq to fake the real call to the web service, but not mock the rest of the code within the using?

public IMyInterface.SomeMethod()
{    
     // hits a web service
     using ( mySoapClient client = new mySoapClient() )
     {
          var someResult = client.DoSomething();
       ...
       ...
     }
}


[TestMethod()]
public void SomeMethodTest()
{
    IMyInterface target = new MyInterface();
    target.SomeMethod();

    // Assert....
}

You need to decouple the web service implementation from the consumer

public class ClassIWantToTest
{
      public ClassIWantToTest(IServiceIWantToCall service) {}

      public void SomeMethod()
      {
           var results = service.DoSomething();
           //Rest of the logic here
      }
}

Now you can use Moq to mock the IServiceIWantToCall in order to test the logic of SomeMethod

To add to pickles' answer, I created an interface for my current service calls named IService . I then created a ServiceMock class that inherits the interface and added a global variable named _service . In the constructor I instantiate the mock service and set up all the methods of the interface as such:

public class ServiceMock : IService
{
    Mock<IService> _serviceMock;

    public ServiceMock()
    {
        _serviceMock = new Mock<IService>();

        _serviceMock.Setup(x => x.GetString()).Returns("Default String");

        SomeClass someClass = new SomeClass();
        someClass.Property1= "Default";
        someClass.Property2= Guid.NewGuid().ToString();

        _serviceMock.Setup(x => x.GetSomeClass()).Returns(someClass);
    }

    public string GetString()
    {
        return _serviceMock.Object.GetString();
    }

    public License GetSomeClass()
    {
        return _serviceMock.Object.GetSomeClass();
    }
}

You then inject this class into your code instead of the actual web service. It will return the values you set it up to return. You can now test without depending on your web service.

You first have to be able to inject the web service. Creating a new one inside SomeMethod() "tightly couples" the method to the production code; you can't dynamically tell it to create something other than a mySoapClient.

Since you want to create and destroy them, might I suggest that the code you want to test accept a Func<IMySoapClient> as a method parameter or as a constructor parameter. It would look something like this:

public IMyInterface.SomeMethod(Func<IMySoapClient> clientFactory)
{    
     // hits a web service
     using ( mySoapClient client = clientFactory() )
     {
          var someResult = client.DoSomething();
       ...
       ...
     }
}

... or:

public class MyClass:IMyInterface
{
   private Func<IMySoapClient> MySoapClientFactoryMethod;

   public MyClass(Func<IMySoapClient> clientFactoryMethod)
   {
      MySoapClientFactoryMethod = clientFactoryMethod;
   }

   ...

   public IMyInterface.SomeMethod()
   {    
        // hits a web service
        using ( mySoapClient client = MySoapClientFactoryMethod() )
        {
             var someResult = client.DoSomething();
          ...
          ...
        }
   }
}

Now, when you create the object you are trying to test, you define a function that generates the appropriate Moq mock of the Soap service, which has the behavior you would expect from the real client without the side effects (including being able to tell that the code Dispose()d of the client), and pass that function into the class or method that you're testing. In production, you could simply define the function as ()=>new mySoapClient() , or you could set up an IoC framework and register mySoapClient as an IMySoapClient, then also register MyClass; most IoC frameworks are smart enough to see the delegate as a parameter and generate the method that injects the registered dependency.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM