Moq - Updating Reference Parameter Inside Callback Not Working - c#

I'm trying to unit test a method where a function is called in a loop. One of the parameters supplied to the method is a List<string>, declared outside the loop, that should be updated by each call that it is passed to.
I've been trying to mock this behaviour using some examples that I found on SO that involve updating the parameter inside of a Callback(), but this hasn't worked for me as expected.
Here is a short-hand example of the problem I'm having:
Method
public async Task DoSomething() {
var strings = new List<string>();
for(i = 0; i < 2; i++) {
var response = await _responder.GetResponse(i, strings);
//method adds a new string into the collection on each call
}
}
So to test this, I would need to mock both method calls, knowing that the string collection would be empty on one and contain one element on the other...
Test
public async Task TestDoSomething() {
var strings = new List<string>();
var mock = new Mock<Responder>();
mock.Setup(x => x.GetResponse(0, strings)) //mocks first iteration of loop
.ReturnsAsync(new Response())
.Callback<int, List<string>>((number, stringCollection) => {
stringCollection = new List<string> {"addedString"}; //this is where the problem occurs
strings = stringCollection;
});
mock.Setup(x => x.GetResponse(1, strings)) //mocks second iteration of loop
.ReturnsAsync(new Response());
//...
}
So at the point where I try to update the collection of strings inside the callback, studio highlights the parameter and gives me the warning The value passed to the method is never used because it is overwritten in the method body before being read.
The test fails because the setups are not matched, and trying to debug causes the test to crash and drop out.
Can anyone point me in the right direction here? Between the warning message and the fact that all the other examples of this stuff just use Returns rather than ReturnsAsync, I would guess that it's to do with the timing of updating the parameter.
Thanks in advance!

Not sure what exactly you want to test the method call or that values added to the collection.
But if you want to test that method was called, you can use Verify function like this -
mock.Verify(mock => mock.GetResponse(It.IsAny<int>(), strings), Times.Exactly(2));
PS. You should use It.IsAny<int>() cause first param obtain index from loop SO _responder.GetResponse(0, strings) calls only once and so on.

Related

Unit test. Why Mock returns different value in second loop iteration?

I have a Mock on the following object whose job is to collect sensor data. It implements this interface:
public interface ISensorDataCollector
{
List<int> CollectSensorData(int amountOfValues);
}
Inside the test I have the following arrangement:
// ARRANGE
var collector = new Mock<ISensorDataCollector>() { CallBase = true };
// Mock SensorDataCollector
collector.Setup((x) => x.CollectSensorData(10)
.Returns(new List<int> { 1,2,3,4,5,6,7,8,9});
myProcess.AdwSensorDataCollector = collector.Object;
// ACT
myProcess.CollectSensorDataRepeatIfFails(5);
The Method to be tested has a while loop in which the data gets collected. The simple Version looks the following way:
public ISensorDataCollector SensorDataCollector { get; set; }
public void CollectSensorDataRepeatIfFails(int counterForRepeatedMeasurement)
{
do
{
List<int> values = this.SensorDataCollector.CollectSensorData(10);
values.Clear();
counterForRepeatedMeasurement--;
} while (counterForRepeatedMeasurement >= 0);
}
The problem: from the second iteration on the line this.AdwSensorDataCollector.CollectSensorData(10); returns an empty list. But I expected it to return the value that I specified in the setup each time:
collector.Setup((x) => x.CollectSensorData(10)
.Returns(new List<int> { 1,2,3,4,5,6,7,8,9});
I guess it has something to do with values.Clear();, because if I remove the list-clearing. The return value of CollectSensorDataRepeatIfFails remains the same in all iterations, and my problem disappears. But that is only a guess. I really would like to know why the second call does not return specified return value.
Question: What did I miss? Does the setup works only for 1 invokation of the mocked method? or is there a button that I forgot to press to make it behave the expected way? Why does the clearing of the list affect the return value of the second call of the mocked method? Can someone please shed light on this matter?
I did a little research before asking, but I could only find posts which explain how to make the mock to return different values at next call. But no post where the problem arises using a loop. I would also be happy about a duplicate tip.
Cheers
What did I miss? Does the setup work only for 1 invocation of the mocked method?
Actually this is expected behavior of the mock. The setup is mutable in the sense that you can change captured arguments or return value by reference. You are doing exactly that by values.Clear();. To avoid this issue just defer the creation of the list by providing factory. Something like this:
collector.Setup((x) => x.CollectSensorData(10))
.Returns<int>((i) => new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
This is expected behavior, because you are working with references of that list. No matter you return it from mock, or somewhere else.
collector.Setup((x) => x.CollectSensorData(10)
.Returns(new List<int> { 1,2,3,4,5,6,7,8,9});
You are registering reference to List<int>.
List<int> values = this.AdwSensorDataCollector.CollectSensorData(10);
So in this call you are receiving reference to exact same List<int> object.
values.Clear()
Calling this is removing all items for the same exact list that mock should return.
You can read more about reference types here:
https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/reference-types
Update:
You can use .Callback(() => new List<int>() { 1, 2, 3}) instead of Returns. In this case callback will be called each time you call mocked function.

Using Moq, How can I change the Setup of a method in the Callback of another method?

I'm trying to mock a third party component for my tests.
The component can store a byte array, but has no property, only two methods to access it.
// simple demo code, _comp is an object of this component
byte[] val = new byte[] { 5 };
// write new value
await _comp.WriteAsync(val);
// read value
byte[] newVal = _comp.ReadAsync();
Now in my tests, I thought I could just Setup the WriteAsync method to change the Setup of the ReadAsync method to return the passed in values.
Mock<IComponent> comp = new Mock<IComponent>;
comp.Setup(x => x.WriteAsync(It.IsAny<byte[]>())).Callback<byte[]>(bytes =>
{
comp.Setup(x => x.ReadAsync()).ReturnsAsync(bytes);
});
But when I debug the demo code in my tests everything works well, the line with
comp.Setup(x => x.ReadAsync()).ReturnsAsync(bytes);
is called with the right values, but when the WriteAsync call returns I get a NullReferenceException in the demo code in the line
await _comp.WriteAsync(val);
I do not know what is null here, _comp and val are not.
I believe it is something deep within the Moq-framework, but I don't get any stacktrace info about that. Stacktrace only contains my demo code.
Am I doing something wrong here?
Or is it not possible to change a Mock-object from within one of its own callbacks?
I think, what I need could be done with a property, but unfortunately the component doesn't have one.
Store the written bytes in a local variable as a backing store for the mock. In the callback you set the value of the array so that it is available when used in the ReadAsync.
The WriteAsync is also an async method so it needs to return a Task to allow the async flow.
Mock<IComponent> comp = new Mock<IComponent>();
byte[] bytes = null;
comp.Setup(x => x.WriteAsync(It.IsAny<byte[]>()))
.Callback<byte[]>(b => bytes = b)
.Returns(Task.FromResult((object)null));
comp.Setup(x => x.ReadAsync()).ReturnsAsync(bytes);
Update based on comments.
In the comments it was mentioned that the WriteAsync return Task<bool>. While the condition of the return value is unknown based on what was provided in the OP, an example of have a condition based on the provided array can look like this
.Returns<byte[]>(b => Task.FromResult(b != null && b.Length > 0));
So for others who stumble upon this, my initial code works, but when the method will return something other than void, one must specify this.
Async methods return Task or Task<>, so the Setup code should be e.g.
Mock<IComponent> comp = new Mock<IComponent>;
comp.Setup(x => x.WriteAsync(It.IsAny<byte[]>())).Callback<byte[]>(bytes =>
{
comp.Setup(x => x.ReadAsync()).ReturnsAsync(bytes);
}).ReturnsAsync(true);

Verifying a call parameter via a callback in NSubstitute

I have a failing test in NSubstitute because a parameter passed in to a substituted call does not match. Here is the relevant code that is being tested:
// Arrange
PermissionsProviderSub = Substitute.For<IPermissionsProvider>();
MenuDataProviderSub = Substitute.For<IMenuDataProvider>();
PermissionsProviderSub.GetPermissions(UserId).Returns(ExpectedUserPermissions);
MenuDataProviderSub.GetMenuData(ExpectedUserPermissions.AuthorisedPageIds).Returns(Arg.Any<IList<BusinessFocusArea>>());
var sut = new MenuViewModelFactory(MenuDataProviderSub, PermissionsProviderSub);
// Act
var result = sut.Create();
// Assert
MenuDataProviderSub.Received().GetMenuData(ExpectedUserPermissions.AuthorisedPageIds);
The problem occurs in the ExpectedUserPermissions.AuthorisedPageIds property, which looks like this:
public IEnumerable<string> AuthorisedPageIds
{
get
{
return ApplicationPagePermissions != null ?
ApplicationPagePermissions.Select(permissionSet => permissionSet.PageId) :
Enumerable.Empty<string>();
}
}
As you can see, there is a LINQ Select, which is extracting the PageId property from within the ApplicationPagePermissions collection and returning it as an IEnumerable<string>. Because the projection within that property creates a new object, the substitution does not match, as it sees the 2 objects as being different.
Can I create a callback on the parameter passed in to GetMenuData so that I can examine the value of it?
The documentation on NSubstitute callbacks only talks about examining the return value from a call, rather than a parameter passed into the call.
Typical. As soon as I post to SO, the answer presents itself. Rather than expecting a specific object when creating the substitute call, I expect any instance of type IEnumerable<string> and create a callback when checking the Received() call that actually verifies the values. The substitute call becomes this:
MenuDataProviderSub.GetMenuData(Arg.Any<IEnumerable<string>>()).Returns(Arg.Any<IList<BusinessFocusArea>>());
The Received() check becomes this:
MenuDataProviderSub.Received().GetMenuData(Arg.Is<IEnumerable<string>>(a => VerifyPageIds(ExpectedUserPermissions.AuthorisedPageIds, a)));
private static bool VerifyPageIds(IEnumerable<string> expected, IEnumerable<string> actual)
{
var expectedIds = expected.ToList();
var actualIds = actual.ToList();
return expectedIds.Count == actualIds.Count && expectedIds.All(actualIds.Contains);
}

SetupSequence in Moq

I want a mock that returns 0 the first time, then returns 1 anytime the method is called thereafter. The problem is that if the method is called 4 times, I have to write:
mock.SetupSequence(x => x.GetNumber())
.Returns(0)
.Returns(1)
.Returns(1)
.Returns(1);
Otherwise, the method returns null.
Is there any way to write that, after the initial call, the method returns 1?
The cleanest way is to create a Queue and pass .Dequeue method to Returns
.Returns(new Queue<int>(new[] { 0, 1, 1, 1 }).Dequeue);
That's not particulary fancy, but I think it would work:
var firstTime = true;
mock.Setup(x => x.GetNumber())
.Returns(()=>
{
if(!firstTime)
return 1;
firstTime = false;
return 0;
});
Bit late to the party, but if you want to still use Moq's API, you could call the Setup function in the action on the final Returns call:
var mock = new Mock<IFoo>();
mock.SetupSequence(m => m.GetNumber())
.Returns(4)
.Returns(() =>
{
// Subsequent Setup or SetupSequence calls "overwrite" their predecessors:
// you'll get 1 from here on out.
mock.Setup(m => m.GetNumber()).Returns(1);
return 1;
});
var o = mock.Object;
Assert.Equal(4, o.GetNumber());
Assert.Equal(1, o.GetNumber());
Assert.Equal(1, o.GetNumber());
// etc...
I wanted to demonstrate using StepSequence, but for the OP's specific case, you could simplify and have everything in a Setup method:
mock.Setup(m => m.GetNumber())
.Returns(() =>
{
mock.Setup(m => m.GetNumber()).Returns(1);
return 4;
});
Tested everything here with xunit#2.4.1 and Moq#4.14.1 - passes ✔
You can use a temporary variable to keep track of how many times the method was called.
Example:
public interface ITest
{ Int32 GetNumber(); }
static class Program
{
static void Main()
{
var a = new Mock<ITest>();
var f = 0;
a.Setup(x => x.GetNumber()).Returns(() => f++ == 0 ? 0 : 1);
Debug.Assert(a.Object.GetNumber() == 0);
for (var i = 0; i<100; i++)
Debug.Assert(a.Object.GetNumber() == 1);
}
}
Just setup an extension method like:
public static T Denqueue<T>(this Queue<T> queue)
{
var item = queue.Dequeue();
queue.Enqueue(item);
return item;
}
And then setup the return like:
var queue = new Queue<int>(new []{0, 1, 1, 1});
mock.Setup(m => m.GetNumber).Returns(queue.Denqueue);
Normally, I wouldn't bother submitting a new answer to such an old question, but in recent years ReturnsAsync has become very common, which makes potential answers more complicated.
As other have stated, you can essentially just create a queue of results and in your Returns call pass the queue.Dequeue delegate.
Eg.
var queue = new Queue<int>(new []{0,1,2,3});
mock.SetupSequence(m => m.Bar()).Returns(queue.Dequeue);
However, if you are setting up for an async method, we should normally call ReturnsAsync. queue.Dequeue when passed into ReturnsAsync and will result in the first call to the method being setup working correctly, but subsequent calls to throw a Null Reference Exception. You could as some of the other examples have done create your own extension method which returns a task, however this approach does not work with SetupSequence, and must use Returns instead of ReturnsAsync. Also, having to create an extension method to handle returning the results kind of defeats the purpose of using Moq in the first place. And in any case, any method which has a return type of Task where you have passed a delegate to Returns or ReturnsAsync will always fail on the second call when setting up via SetupSequence.
There are however two amusing alternatives to this approach that require only minimal additional code. The first option is to recognize that the Mock object's Setup and SetupAsync follow the Fluent Api design patterns. What this means, is that technically, Setup, SetupAsync, Returns and ReturnsAsync actually all return a "Builder" object. What I'm referring to as a Builder type object are fluent api style objects like QueryBuilder, StringBuilder, ModelBuilder and IServiceCollection/IServiceProvider. The practical upshot of this is that we can easily do this:
var queue = new List<int>(){0,1,2,3};
var setup = mock.SetupSequence(m => m.BarAsync());
foreach(var item in queue)
{
setup.ReturnsAsync(item);
}
This approach allows us to use both SetupSequence and ReturnsAsync, which in my opinion follows the more intuitive design pattern.
The second approach is to realize that Returns is capable of accepting a delegate which returns a Task, and that Setup will always return the same thing. This means that if we were to either create an an extension method for Queue like this:
public static class EMs
{
public static async Task<T> DequeueAsync<T>(this Queue<T> queue)
{
return queue.Dequeue();
}
}
Then we could simply write:
var queue = new Queue<int>(new []{0,1,2,3});
mock.Setup(m => m.BarAsync()).Returns(queue.DequeueAsync);
Or would could make use of the AsyncQueue class from Microsoft.VisualStudio.Threading, which would allow us to do this:
var queue = new AsyncQueue<int>(new []{0,1,2,3});
mock.Setup(m => m.BarAsync()).Returns(queue.DequeueAsync);
The main problem that causes all of this, as that when the end of a setup sequence has been reached, the method is treated as not being setup. To avoid this, you are expected to also call a standard Setup if results should be returned after the end of the sequence has been reached.
I have put together a fairly comprehensive fiddle regarding this functionality with examples of the errors you may encounter when doing things wrong, as well as examples of several different ways you can do things correctly.
https://dotnetfiddle.net/KbJlxb
Moq uses the builder pattern, setting up the behavior of the mocks.
You can, but don't have to use the fluent interface.
Keeping that in mind, I solved the problem this way:
var sequence = myMock
.SetupSequence(""the method I want to set up"");
foreach (var item in stuffToReturn)
{
sequence = sequence.Returns(item);
}

Rhino Mock List constraint

I'm trying to asssert that a method was called on a stub. The method I'm trying to assert was called takes an IEnumerable<string>. I don't care about the exact contents, but I just want to test that the count is a certain number. I can't get the assertion correct, I get
Rhino.Mocks.Exceptions.ExpectationViolationException : Bob.DoThings(collection count equal to 10); Expected #1, Actual #0.
I know that DoThings() is indeed being called... I just can't get the constraint correct..
var myBob= MockRepository.GenerateStub<Bob>();
var countConstraint = Rhino.Mocks.Constraints.List.Count(Rhino.Mocks.Constraints.Is.Equal(10));
// execution code....
Joe myJoe = new Joe(myBob);
myJoe.MethodThatShouldCallDoThingWith10();
myBob.AssertWasCalled(s => s.DoThings(null), o => Constraints(countConstraint));
I've also tried adding "IgnoreArguments" as a constraint. What am I missing?
The issue here is with deferred execution. It's not until the IEnumerable<string> is enumerated that the list of items is "built". Since Rhino.Mocks just records what gets called, it never "uses" the method arguments and therefore, the list is never built nor enumerated. As you've seen, adding a ToList() or ToArray() enumerates and builds the list so the test will pass if you use one of those methods.
One workaround is to grab the list that was passed to the method and do your check on that:
var list = (IEnumerable<int>) myBob.GetArgumentsForCallsMadeOn(b => b.DoThings(null))[0][0];
Assert.AreEqual(10, list.Count());
This test passes and doesn't require any changes to your code.
This problem was alredy reported Here. I've been able to reproduce this problem with the following Bob and Joe:
public interface Bob
{ void DoThings(IEnumrable<int> list); }
public class Joe
{
private readonly Bob bob;
public Joe(Bob bob)
{ this.bob = bob; }
public void MethodThatShouldCallDoThingWith10()
{
var values = Enumerable.Range(1, 100).Where(x => x > 0 && x < 11);
bob.DoThings(values);
}
}
It seems there's some problem in Rhino Mocks after all, when it comes to LINQ: either report the bug to Ayende or add ToList() in your production code (not really recommended)...

Categories

Resources