Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mocking a ViewModel for unit testing with Moq?

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?

like image 542
Hardgraf Avatar asked Sep 28 '22 13:09

Hardgraf


1 Answers

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());
    }
}
like image 139
Thomas Lielacher Avatar answered Oct 16 '22 23:10

Thomas Lielacher