4

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..

Shan
  • 2,822
  • 9
  • 40
  • 61

2 Answers2

2

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.

Micael
  • 155
  • 6
1

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
Blair Conrad
  • 233,004
  • 25
  • 132
  • 111
  • The FakeCustomerData is calling an WebService..I am little confused here..I want to mock the WebService that is being declared in the interface "ICustomerData" As the example I have written here is almost a different from the original one..I am so confused (As far as I can see I dont have an interface where I can force it return production and fake data separately – Shan Jun 08 '17 at 06:22
  • I don't entirely understand your comment, but let me try to shed some light. The common way to do what you want/need is to declare a concrete class (which you'll be testing) that accepts the injected collaborator (often via constructor or property injection) in the form of an interface. Then in the production code, you inject the concrete implementation of the collaborator (the web service), and in the test, you inject the faked interface. I hope this helps. I am confused by the sample snippets and your variable and class names, or I'd try to provide a full example. – Blair Conrad Jun 09 '17 at 01:20
  • It wont work anyway because the Service I am calling is not virtual it's a private way I tried with mock,Fake and nothing works! – Shan Jun 20 '17 at 07:02
  • @Shan, if you don't have the option of making it non-private, and you really want to mock it, you could use TypeMock Isolator or a similar product. Although a common alternative is to create an interface to use in your code, and have a very very slim class implement the interface and delegate to the collaborator that you can't mock. – Blair Conrad Jun 20 '17 at 22:32
  • Thanks, I will try with TypeMock Isolator and let you know – Shan Jun 21 '17 at 11:06
  • 1
    +4 years @BlairConrad well i did try and your solution worked perfectly. – Linda Lawton - DaImTo Jun 25 '20 at 08:45