Shan
Shan

Reputation: 2832

How to mock a SOAP Service class in C#

I want to fake the getCustomerName Service call here and mock it with fake Data

My class is "CustomerName" which calls a SOAPService call which returns the CustomerName for an CustomerNumber.I want to fake the SOAPService call to return some fakedata.

CustomerName class:

using (WebService.WebServiceClient CustomerData = new WebService.WebServiceClient ())
{
      WebServiceClient.TestResponse resp = CustomerData.getCustomerName(customerNumber);
      resp.CustomerName;
}

I tried doing these

var FakeWebService = A.Fake<WebServiceClient>();
var FakeCustomerName=A.Fake<CustomerName>();

Then I faked the Calls here and Fake response is some value

    A.CallTo(WebServiceClient.CustomerNumber).WithNonVoidReturnType().Returns(FakeResponse); 
fakeCustomerName = FakeCustomerData.GetCustomerName(CustomerNumber);

The problem I face is it's getting the data from the actual endpoint instead of fakedata?

I am not clear on how to do it..

Upvotes: 4

Views: 4403

Answers (2)

Blair Conrad
Blair Conrad

Reputation: 241980

There are several problems with your approach. For starters, you can only fake virtual methods (or abstract ones, or methods defined on interfaces). I assume that WebService.WebServiceClient.getCustomerName is not virtual, so here's an alternative to @Micael's answer that will get you around that. I've used it many times in the past:

Like Micael says, create an interface instead which holds the functionality the webservice provides.

public interface ICustomerData
{
    CustomerName GetCustomerName(CustomerNumber number);
}

Then you can make your production collaborator CustomerData as he did, or if WebService.WebServiceClient is not sealed, you can do this:

public class CustomerData: WebService.WebServiceClient, ICustomerData
{}

You will need to find a way to supply an implementation of ICustomerData to your production code so that you can use the actual implementation during production and a fake during the tests, which I talk about right now:

The second problem is that in your test, you're attempting to fake calls to WebServiceClient.CustomerNumber, which feels like a type to me, not an actual object that you're dealing with. Following along from our step above, you want to fake the ICustomerData interface:

var fakeCustomerData = A.Fake<ICustomerData>();
var someCustomerName = getACustomerNameSomehow();

A.CallTo(() => fakeCustomerData.GetCustomerName(A<CustomerNumber>.Ignored)
        .Returns(someCustomerName);

This will make sure your fake returns someCustomerName whenever GetCustomerName is called. I changed someCustomerName from a fake because you may not need it to be a fake - if it's easy to create a CustomerName object to return from your fake service, I'd just do that. Only if you need to change its behaviour, or it's almost impossible to create, would I use a fake.

After the fake is configured, you should call some method on your production class (which you said was called CustomerName, just like the type returned from GetCustomerName?), that will eventually call the ICustomerData collaborator. In your original code, you're calling a method on the fake directly, which only tests the fake, not your own code. So instead you might have something like

var customerNameService = new CustomerName(fakeCustomerData);

var foundCustomerName = customerNameService.GetACustomerNameFromANumber(someCustomerNumber);
// in my imagination, GetACustomerNameFromANumber calls ICustomerData.GetCustomerName

// now do something to check if the foundCustomerName is right, or whatever

Upvotes: 1

Micael
Micael

Reputation: 155

Instead of refering to the webservice directly and hence try to mock the webservice itself, create an interface instead which holds the functionality the webservice provides.

public interface ICustomerData
{
    CustomerName GetCustomerName(CustomerNumber number);
}

Then, make two implementations. One which calls the actual webservice, and one which you can use for testing:

public class CustomerData : ICustomerData
{
    CustomerName GetCustomerName(CustomerNumber number)
    {
        return CustomerData.GetCustomerName(number);
    }

    public CustomerData()
    {
        CustomerData = new WebService.WebServiceClient ();
    }
    private WebService.WebServiceClient CustomerData;
}


public class DelegatedCustomerData : ICustomerData
{
    public Func<CustomerNumber,CustomerName> GetCustomerName {get;set;}


    CustomerName ICustomerData.GetCustomerName(CustomerNumber number) => GetCustomerName(number);

}

The latter delegated class is just an example on how to mock the class, but which i personally prefer.

Upvotes: 2

Related Questions