I want to test the following code:
foreach (CallQueueData queueData in _configurationProvider.CallQueues.Values)
{
_chimeManager.AddConfig(new ChimeConfigData(
ChimeKey.Create(ChimeType.CallQueue, queueData.Name));
if (queueData.LowChime != null)
{
_chimeManager.AddConfig(new ChimeConfigData(
ChimeKey.Create(ChimeType.QueueLowChime, queueData.Name));
}
if (queueData.HighChime != null)
{
_chimeManager.AddConfig(new ChimeConfigData(
ChimeKey.Create(ChimeType.QueueHighChime, queueData.Name));
}
}
One of my tests looks like this:
public void ShouldAddHighChimeConfigToChimeManager_IfServiceIsStarted_AndHighChimeIsConfigured()
{
// GIVEN
InitializeObjectUnderTestWithougStartingService();
var callQueueData = new CallQueueData
{
Name = "Emergency",
HighChime = new ChimeType(1, "High")
};
CallQueues.Add(callQueueData.Id, callQueueData);
// WHEN
_mgr.Startup();
// THEN
ChimeManager.AssertWasCalled(x => x.AddConfig(Arg<ChimeConfigData>.Matches(
y => y.Key == ChimeKey.Create(ChimeType.HighChime, callQueueData.Name))));
}
The problem here is that the AddConfig method of the ChimeManager is called multiple times and I dont want to specify how often it has to be called before it matches my method.
// i dont like this repeat twice because this ties the test code to much to the production code
ChimeManager.AssertWasCalled(x => x.AddConfig(Arg<ChimeConfigData>.Matches(
y => y.Key == ChimeKey.Create(ChimeType.HighChime, callQueueData.Name)),
y => y.Repeat.Twice));
I would rather like to say something like:
ChimeManager.AssertWasCalled(x => x.AddConfig(Arg<ChimeConfigData>.Matches(
y => y.Key == ChimeKey.Create(ChimeType.HighChime, callQueueData.Name)),
y => y.Repeat.Any / y.Match.Any));
Unfortunatley Repeat.Any is invalid in this case, any there is no Match.Any.
How can i assert that this method was called with the specified arguments, but it should not matter how often it is called. When one of the calls to the method match with the specified arguments than the assertion will not fail.
Instead of writing a test to validate the implementation of the method (which becomes brittle over time), write a test to validate the results of the method. In this case, it looks like you want to make sure _chimeManager is populated properly.
If you write a test that simply sends some data and then validates the results, your unit tests will be less likely to break over time. Suppose that at some point in the future, the population of _chimeManager happens via database calls or some other method other than then Add method? If you write a test against the implementation, your tests will break. However, if you write your test to simply make sure that, given a certain input, the _chimeManager is properly populated, then you've got a test that won't break as the "how" is changed.