Reputation: 86
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!
Upvotes: 6
Views: 4786
Reputation: 10552
Instead of hard-coding your additional concrete items, have the SingleItem_CodeCantBeModified
implement an interface (or embed it in a wrapper which implements the interface) then pass in a (new) factory which will create these items.
In your test you will create a mock of the factory to pass in to your Manager class, then you can monitor what methods are called on that mocked object.
Although this would be more about testing the internals of the system, not the byproducts. What interface is the Manager implementing? If it's not proving itself externally, what results are you testing for?
Upvotes: -1
Reputation: 97656
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:
UpdateAllItems
at all. Its implementation is trivial, so this is an option to consider (though not ideal).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.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 ISingleItem
s, and you'll be free to pass in mock ISingleItem
s 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.
Upvotes: 5
Reputation: 2237
As usual, you can add another level of indirection.
SingleItem_CodeCantBeModified
IItem
interfaceSingleItemManager
depend on IItem
instead of SingleItem_CodeCantBeModified
OR
If Increment
is a virtual method (I understand it isn't in your sample code, but just in case), use partial mocking.
Upvotes: 0
Reputation: 100238
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);
Upvotes: 1