Reputation: 30205
I have an ILogger interface with LogTrace(string value, params object[] parameters). Now I want to verify that the LogTrace is called and the string to log contains some id. The problem is that it can be called differently. E.g. 1) LogTrace("MyString " + id) 2) LogTrace("MyString {0}", id) and so on.
Is there a good way with Moq to verify all the scenarios? I can only think of creating a hand-made mock that will format the string that will be available for verification.
Upvotes: 17
Views: 12051
Reputation: 1246
I don't think there is an easy way for doing what you need here. The problem is that you need to ensure that a certain combination of values will be passed to your method, which results in many different verifiable scenarios:
However, Moq does not support this sort of conditional expressions between different arguments of your verifiable method. One possible solution is to check for the absence of an id, instead of its presence in either argument. Try something like:
mock.Verify(m => m.LogTrace(
It.Is<string>(s => !s.Contains(id)),
It.Is<object[]>(o => !o.Contains(id))), Times.Never());
What we are doing here is verifying whether the fail condition is ever met - that is, your string does not contain an id, and neither does your object array. We use Times.Never() to make sure that this situation should never happen.
Keep in mind, however, that the code might not be obvious at first glance; make sure you properly explain your intent once you write it.
Upvotes: 3
Reputation: 48230
mock.Verify( m => m.LogTrace( It.IsAny<string>(), It.IsAny<object[]>() ) );
The params object[]
is passed to the method as object[]
anyway so you just have to match the array somehow (as above for example, this accepts anything).
If you need more control over the list, use the It.Is
matcher which allows you to create your own predicate:
mock.Verify( m => m.LogTrace( It.IsAny<string>(),
It.Is<object[]>(ps =>
ps != null &&
ps.Length == 1 &&
ps[0] is int &&
(int)ps[0] == 5
) ) );
This example shows how to verify if the param list is not empty and contains 5
as the only parameter of type int
.
Upvotes: 20
Reputation: 36649
You could try to use the Callback
, but it get fairly convoluted (not tested):
var mock = new Mock<ILogger>();
string trace = null;
mock.Setup(l => l.LogTrace(It.IsAny<string>(), It.IsAny<object[]>()))
.Callback((s1, par) =>
{
trace = string.Format(s1, par);
});
//rest of the test
Assert.AreEqual(expected, trace);
If ILogger
has a small interface, you might consider implementing a stub manually (that would keep all the lines it is supposed to log), and verify that at the end of the test. If you have multiple lines being logged than this will be a more readable setup.
Upvotes: 1