Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use Moq to test a method with no return value?

This is my first question so please be kind! :)

What I am trying to do is write some tests for a manager class that during construction adds many new instances of a single item class to a list. When the UpdateAllItems is called in this manager class the intention is to iterate the list and call Increment on each single item.

The manager class is my code, but the single item class is not so I can't modify it.

I use NUnit for a testing framework and am starting to work with Moq. Because the manager class uses the single item class I would think I need to use a Moq so I am testing only the manager, not the single item.

How do I write tests for my UpdateAllItems method? (Technically I should be writing the tests first I know).

Here is a some sample code that gives a general idea of what I am working with...

public class SingleItem_CodeCantBeModified
{
    public int CurrentValue { get; private set; }

    public SingleItem_CodeCantBeModified(int startValue)
    {
        CurrentValue = startValue;
    }

    public void Increment()
    {
        CurrentValue++;
    }
}

public class SingleItemManager
{
    List<SingleItem_CodeCantBeModified> items = new List<SingleItem_CodeCantBeModified>();

    public SingleItemManager()
    {
        items.Add(new SingleItem_CodeCantBeModified(100));
        items.Add(new SingleItem_CodeCantBeModified(200));
    }

    public void UpdateAllItems()
    {
        items.ForEach(item => item.Increment());
    }
}

Thanks in advance for all the help!

like image 883
Frank Trocchia Avatar asked Sep 02 '11 12:09

Frank Trocchia


People also ask

Can you mock a private method Moq?

Moq supports mocking protected methods. Changing the methods to protected , instead of private , would allow you to mock their implementation.

Can you mock a class with Moq?

You can use Moq to create mock objects that simulate or mimic a real object. Moq can be used to mock both classes and interfaces. However, there are a few limitations you should be aware of. The classes to be mocked can't be static or sealed, and the method being mocked should be marked as virtual.

What can be mocked with Moq?

Unit testing is a powerful way to ensure that your code works as intended. It's a great way to combat the common “works on my machine” problem. Using Moq, you can mock out dependencies and make sure that you are testing the code in isolation. Moq is a mock object framework for .


2 Answers

The simple answer is, you can't. The method that UpdateAllItems calls (Increment()) is non-virtual, so you won't be able to mock it.

Your options, as I see it, are:

  • Don't test UpdateAllItems at all. Its implementation is trivial, so this is an option to consider (though not ideal).
  • Create real SingleItem_CodeCantBeModified instances in your test. Purists would say that you no longer have a unit test at this point, but it could still be a useful test.
  • Add an ISingleItem interface, and an SingleItemAdapter : ISingleItem class that holds onto a reference to a SingleItem_CodeCantBeModified and forwards the calls. Then you can write SingleItemManager to operate on ISingleItems, and you'll be free to pass in mock ISingleItems in your tests. (Depending on how your system is set up, you might even be able to descend from SingleItem_CodeCantBeModified, implement the interface on your descendant, and use those objects instead of writing an adapter.)

That last option gives you the most options, but at the cost of some complexity. Choose the option that's best suited for what you're trying to accomplish.

like image 155
Joe White Avatar answered Oct 14 '22 10:10

Joe White


Your Manager is too dependent on Item (in List<Item>). Can you extract list population into separate class to be able to mock it? e.g.:

public SingleItemManager()
{
    items.Add(ItemRepository.Get(100));
    items.Add(ItemRepository.Get(200));
}

Testing (some code omitted):

int i = 0;

var itemMock = new Mock<Item>();
itemMock.Setup(i => i.Increment()).Callback(() => i++);

var repositoryMock = new Moc<ItemRepository>();
repositoryMock.Setup(r => r.Get(It.IsAny<int>()).Returns(itemMock.Object);

var manager = new SingleItemManager();
manager.UpdateAllItems();

Assert.AreEqual(i, 1);
like image 40
abatishchev Avatar answered Oct 14 '22 11:10

abatishchev