5

New to unit testing. I have a WPF client app hooked into a WCF service via basicHttpbinding. Everything works great. I'm using simple constructor Dependency Injection in my viewModel, passing in an IServiceChannel which I then call me service methods on e.g:

IMyserviceChannel = MyService;

public MyViewModel(IMyServiceChannel myService)
{
   this.MyService = myService;  
}

Private void GetPerson()
{
  var selectedPerson = MyService.GetSelectedPerson();
}

I have then added an MS Test project in the client app and I'm trying to use Moq to mock my service:

  [TestMethod]
    public void GetArticleBody_Test_Valid()
    {
        // Create channel mock
        Mock<IIsesServiceChannel> channelMock = new Mock<IIsesServiceChannel>(MockBehavior.Strict);    

        // setup the mock to expect the Reverse method to be called
        channelMock.Setup(c => c.GetArticleBody(1010000008)).Returns("110,956 bo/d, 1.42 Bcfg/d and 4,900 bc/d. ");

        // create string helper and invoke the Reverse method
        ArticleDataGridViewModel articleDataGridViewModel = new ArticleDataGridViewModel(channelMock.Object);
        string result = channelMock.GetArticleBody(1010000008);
        //Assert.AreEqual("cba", result);

        //verify that the method was called on the mock
        channelMock.Verify(c => c.GetArticleBody(1010000008), Times.Once());
    }

The test is failing with a System.NullReferenceException. Object reference not set to an instance of an object. at the method invocation here:

 string result = articleDataGridViewModel.IsesService.GetArticleBody(1010000008);

so I'm wandering whether this is the best way to approach or am I better somehow mocking an isolated part of the viewModel which is applicable to the test?

Hardgraf
  • 2,566
  • 4
  • 44
  • 77
  • What do you mean by "exception is caught further into the view model"? – Thomas Lielacher Mar 09 '15 at 13:41
  • Sorry, question edited. I'm getting 'Object reference not set to an instance of an object' on the method invocation in the test. I've just new'ed up a viewModel object. Obviously all the members of the object hold null values as I haven't initialised any of them from the constructor but not sure why this exception is thrown? – Hardgraf Mar 09 '15 at 13:43
  • Where is your viewmodel code**? – C Bauer Mar 09 '15 at 14:20
  • In the question. I included the constructor & method at hand – Hardgraf Mar 09 '15 at 14:47

3 Answers3

6

The NullReferenceException is mybe thrown because you use MockBehavior.Strict. The documentation says:

Causes this mock to always throw an exception for invocations that don't have a corresponding setup.

Maybe the constructor of ArticleDataGridViewModel calls other methods of the service which you haven't set up. Another issue is, that you are calling the mocked method directly. Instead you should call a method of your view model, which calls this method.

[TestMethod]
public void GetArticleBody_Test_Valid()
{
    // Create channel mock
    Mock<IIsesServiceChannel> channelMock = new Mock<IIsesServiceChannel>();    

    // setup the mock to expect the Reverse method to be called
    channelMock.Setup(c => c.GetArticleBody(1010000008)).Returns("110,956 bo/d, 1.42 Bcfg/d and 4,900 bc/d. ");

    // create string helper and invoke the Reverse method
    ArticleDataGridViewModel articleDataGridViewModel = new ArticleDataGridViewModel(channelMock.Object);
    string result = articleDataGridViewModel.MethodThatCallsService();
    //Assert.AreEqual("cba", result);

    //verify that the method was called on the mock
    channelMock.Verify(c => c.GetArticleBody(1010000008), Times.Once());
}

Besides that I think there is no problem with your approach. Maybe the view model violates the single responsibility principle and does more than it should, but that's hard to tell on the basis of your code example.

EDIT: Here's a full example of how you could test something like this:

public interface IMyService
{
    int GetData();
}

public class MyViewModel
{
    private readonly IMyService myService;

    public MyViewModel(IMyService myService)
    {
        if (myService == null)
        {
            throw new ArgumentNullException("myService");
        }

        this.myService = myService;
    }

    public string ShowSomething()
    {
        return "Just a test " + this.myService.GetData();
    }
}

class TestClass
{
    [TestMethod]
    public void TestMethod()
    {
        var serviceMock = new Mock<IMyService>();
        var objectUnderTest = new MyViewModel(serviceMock.Object);

        serviceMock.Setup(x => x.GetData()).Returns(42);

        var result = objectUnderTest.ShowSomething();

        Assert.AreEqual("Just a test 42", result);
        serviceMock.Verify(c => c.GetData(), Times.Once());
    }
}
Thomas Lielacher
  • 1,037
  • 9
  • 20
  • Great, thanks. The viewModel had a property taking a return value from the method that was set to null in the ViewModel. I've written a ViewModel constructor which only takes a service interface & doesn't implement anything. This is just to simplify the viewmodel construction during unit testing and will only ever be hit by running the tests. Is this normal procedure? – Hardgraf Mar 09 '15 at 14:24
  • Generally you shouldn't have to add things like an additional constructor to make the code testable. Maybe there are some other issues in the view model. Eg.: why isn't the service injected all the time? – Thomas Lielacher Mar 09 '15 at 14:32
  • The service is injected all the time. I have another constructor which takes more parameters & calls other methods so didn't want to hard-code in constructor values when newing my ViewModel object in the test class. If I use this constructor, I get null exception errors because values are set in property setters etc. – Hardgraf Mar 09 '15 at 14:34
0

Without access to your viewmodel, there's only so much help that we can provide you.

However, this code:

Mock<IIsesServiceChannel> channelMock = new Mock<IIsesServiceChannel>(MockBehavior.Strict);
...
ArticleDataGridViewModel articleDataGridViewModel = new ArticleDataGridViewModel(channelMock.Object);
...
string result = articleDataGridViewModel.IsesService.GetArticleBody(1010000008);

Does not set up your IsesService. If it is not set up in your constructor, that means the IsesService is a null reference. You can't call a method on a null object.

C Bauer
  • 5,003
  • 4
  • 33
  • 62
  • Using constructor dependency injection so a service interface object is passed into the viewModel constructor. Turned out to be a property in the viewModel which was being set to take the return string from the method I was testing. I hadn't initialised it in the ViewModel constructor so therefore was null at runtime. – Hardgraf Mar 09 '15 at 14:27
-1

Consider mocking out at a higher level of abstraction then the tight coupling you have with the tool your using.

Perhaps your view-model should rely on services and not a detail of the tool that your using (i.e. IIsesServiceChannel).

Here's an example:

Construct testable business layer logic

Community
  • 1
  • 1
Scott Nimrod
  • 11,206
  • 11
  • 54
  • 118