Testing whether a method was called twice with different arguments - c#

I need to verify that a method has been invoked 2 times with specific arguments:
public void Execute()
{
Counter с = new Counter(1);
// Basically, I need to verify that DoSomething method was called twice:
// First time with an argument, which internal value is 1...
_subject.DoSomething(с);
c.Increment();
// ... and the second time - with internal value of 2
_subject.DoSomething(c);
}
Here is my verification part, with both assertions failing:
// Fails with message "Expected invocation on the mock once, but was 0 times"
mock.Verify(
m => m.DoSomething(
It.Is<Counter>(
x => x.GetValue() == 1
)
),
Times.Once
);
// Fails with message "Expected invocation on the mock once, but was 2 times"
mock.Verify(
m => m.DoSomething(
It.Is<Counter>(
x => x.GetValue() == 2
)
),
Times.Once
);
So the problem is that both invocations inside the Execute method use the same argument, which by the time of performing the assertion contains 2 as its internal value.
The question is how to make the mock to "record" the arguments somehow, so that they are evaluated correctly?
Below is the full listing:
[Test]
public void CheckSomething()
{
Mock<Subject> mock = new Mock<ISubject>();
mock.Setup(m => m.DoSomething(It.IsAny<Counter>()));
var myService = new MyService(mock.Object);
myService.Execute();
mock.Verify(m => m.DoSomething(It.Is<Counter>(x => x.GetValue() == 2)), Times.Once);
mock.Verify(m => m.DoSomething(It.Is<Counter>(x => x.GetValue() == 1)), Times.Once);
}
public interface ISubject
{
public void DoSomething(Counter с);
}
public class Subject : ISubject
{
public void DoSomething(Counter с)
{
// doesn't matter
}
}
public class MyService
{
private readonly ISubject _subject;
public MyService(ISubject subject)
{
_subject = subject;
}
public void Execute()
{
Counter с = new Counter(1);
_subject.DoSomething(с);
с.Increment();
_subject.DoSomething(с);
}
}
public class Counter
{
private int _val;
public Counter(int val)
{
_val = val;
}
public void Increment()
{
_val++;
}
public int GetValue()
{
return _val;
}
}
I really appreciate any feedback, thanks!

You can set a callback when setting up your DoSomething to track the increment of counter's value.
int counterEqual1 = 0;
int counterEqual2 = 0;
mock.Setup(m => m.DoSomething(It.IsAny<Counter>()))
.Callback<Counter>(counter =>
{
if (counter.GetValue() == 1)
counterEqual1++;
else if (counter.GetValue() == 2)
counterEqual2++;
});
Then verify those calls:
mock.Verify(m => m.DoSomething(It.IsAny<Counter>()), Times.Exactly(2));
Assert.That(counterEqual1, Is.EqualTo(1));
Assert.That(counterEqual2, Is.EqualTo(1));

Related

How to validate two async rules using single api call / How to share async state between multiple validators

I have multiple async rules that requires the same API calls.
How can I avoid calling the same api twice in a single validation run?
public class ModelValidator : AbstractValidator<Model>
{
public ReservationDetailModelValidator(IApiClient api)
{
RuleFor(x => x.Amount)
.MustAsync(async (x, val) =>
{
var apiResult = api.Fetch(x.Id);
return apiResult.Value < value)
}).WithMessage("Validation rule 1");
.MustAsync(async (x, val) =>
{
var apiResult = api.Fetch(x.Id);
return apiResult.OtherProperty == x.OtherProperty)
}).WithMessage("Validation rule 2");
}
}
Should I use RootContextData, or override prevalidate?
A work around for this is using a method that will return the cached value if it exists otherwise fetch the data, cache it and then return it.
public class ModelValidator : AbstractValidator<Model>
{
private readonly IApiClient _apiClient;
private ApiResult? _apiResult;
public ModelValidator(IApiClient apiClient)
{
_apiClient = apiClient;
DefineRules();
}
private async Task<ApiResult> GetOrFetchAsync(int id)
{
/*
* Same as :
*
* if (_apiResult is null)
* _apiResult = await _apiClient.Fetch(id);
*
* return _apiResult;
*
*/
return _apiResult ??= await _apiClient.Fetch(id);
}
private void DefineRules()
{
RuleFor(x => x.Amount)
.MustAsync(async (x, val) =>
{
// use of `await GetOrFetchAsync`
})
.WithMessage("Validation rule 1")
.MustAsync(async (x, val) =>
{
// use of `await GetOrFetchAsync`
})
.WithMessage("Validation rule 2");
}
}

How to moq a class with Get() and Set() methods

I can't seem to moq my class that has getter and setter methods (not properties). I need to save the variable somewhere to return it later on.
Signature of my class:
public interface IMyClass
{
int Get();
void Set(int i);
}
My real implementation :
Set() => Encrypts the int and saves it in a local variable
Get() => Decrypts the int from local variable and returns int
How I want the moq to behave :
public class MyClassFake : IMyClass
{
private int _local;
public int Get()
{
return _local;
}
public void Set(int i)
{
_local = i;
}
}
What I've tried and didn't work so far :
var mockMyClass = new Mock<IMyClass>();
var number = new int();
mockMyClass.Setup(x => x.Set(It.IsAny<int>())).Callback<int>(x=> number = x);
mockMyClass.Setup(x => x.Get()).Returns(number);
You have to change Get() setup to use the overload that receives a Func<> as parameter:
var mockMyClass = new Mock<IMyClass>();
var number = 0;
mockMyClass.Setup(x => x.Set(It.IsAny<int>())).Callback<int>(x => number = x);
mockMyClass.Setup(x => x.Get()).Returns(() => number);
In your current code, you are defining Get() result as a fixed number (initial value of number)

NSubstitute - Check arguments passed to method

We are currently in the process of moving from RhinoMocks to NSubstitute.
I have a method that takes an object of type DatabaseParams. This class has the following structure (simplified):
public class DatabaseParams
{
public string StoredProcName { get; private set; }
public SqlParameter[] Parameters { get; private set; }
public DatabaseParams(string storeProcName, SqlParameter[] spParams)
{
StoredProcName = storeProcName;
Parameters = spParams;
}
}
I have the following method I want to check the arguments being passed to it are correct:
public interface IHelper
{
Task<object> ExecuteScalarProcedureAsync(DatabaseParams data);
}
How do I test that an instance of DatabaseParams was passed into that method with the correct values?
I could do this in RhinoMocks with something like this:
helperMock.Expect(m => m.ExecuteScalarProcedureAsync(Arg<DatabaseHelperParameters>.Matches(
p => p.StoredProcName == "up_Do_Something"
&& p.Parameters[0].ParameterName == "Param1"
&& p.Parameters[0].Value.ToString() == "Param1Value"
&& p.Parameters[1].ParameterName == "Param2"
&& p.Parameters[1].Value.ToString() == "Param2Value"
))).Return(Task.FromResult<DataSet>(null));
The helperMock is mocking the interface IHelper that contains the ExecuteScalarProcedureAsync method.
I've figured out the answer myself.
NSubstitute just needs to use the .Received() call and then when you specify your argument to the method. You can specify the argument matching as a predicate.
For example:
helperMock.Received().ExecuteScalarProcedureAsync(Arg.Is<DatabaseParams>(
p => p.StoredProcName == "up_Do_Something"
&& p.Parameters[0].ParameterName == "Param1"
&& p.Parameters[0].Value.ToString() == "Param1Value"
&& p.Parameters[1].ParameterName == "Param2"
&& p.Parameters[1].Value.ToString() == "Param2Value"));
An alternative is to use Do (see https://nsubstitute.github.io/help/actions-with-arguments/). I prefer this as it lets you call assertions against specific properties of the arguments, which gives you better feedback on which specific properties of the argument object are incorrect. For example:
StoredProc sp = null; // Guessing the type here
// Setup Do to capture arg
helperMock.ExecuteScalarProcedureAsync(Arg.Do<DatabaseParams>(p => sp = p));
// Call method
helperMock.ExecuteScalarProcedureAsync(dbParams);
// NUnit assertions, but replace with whatever you want.
Assert.AreEqual("up_Do_Something", sp.StoredProcName);
Assert.AreEqual("Param1", p.Parameters[0].ParameterName);
Assert.AreEqual("Param1Value", p.Parameters[0].Value.ToString());
Assert.AreEqual("Param2", p.Parameters[1].ParameterName);
Assert.AreEqual("Param2Value", p.Parameters[1].Value.ToString());
A bit late for the party, but ran into the same need.
I am working with mockito in java, and they have an Argument capture helper that I like.
It is basically the same as #Castrohenge answer
So here is my NSubstitute implementation.
public interface IFoo
{
void DoSomthing(string stringArg);
}
Argument capture class
public class ArgCapture<T>
{
private List<T> m_arguments = new List<T>();
public T capture()
{
T res = Arg.Is<T>(obj => add(obj)); // or use Arg.Compat.Is<T>(obj => add(obj)); for C#6 and lower
return res;
}
public int Count
{
get { return m_arguments.Count; }
}
public T this[int index]
{
get { return m_arguments[index]; }
}
public List<T> Values {
get { return new List<T>(m_arguments);}
}
private bool add(T obj)
{
m_arguments.Add(obj);
return true;
}
}
And the usage test case
[Test]
public void ArgCaptureTest()
{
IFoo foo1 = Substitute.For<IFoo>();
ArgCapture<string> stringArgCapture = new ArgCapture<string>();
foo1.DoSomthing("firstCall");
foo1.DoSomthing("secondCall");
foo1.Received(2).DoSomthing(stringArgCapture.capture());
Assert.AreEqual(2,stringArgCapture.Count);
Assert.AreEqual("firstCall",stringArgCapture[0]);
Assert.AreEqual("secondCall", stringArgCapture[1]);
}

Using Moq with HttpPostedFileBase and one other parameter

I have a method that takes 2 parameters like:
assetService(assetDto dto, HttpPostedFileBase photo)
and i can't use moq with this. How can i do that? (using 'moq')
public ResultObjectDto CreateAsset(AssetDto model, HttpPostedFileBase file)
and i want to moq this
Assert.IsTrue(_assetService.CreateAsset(new AssetDto(), postedFileBase).ResultType == ResultType.Error);
this moq is wrong, how can i do that
First you have a class you want to mock with a method with several parameters
public class foo
{
public virtual int bar(int num, string str, bool b)
{
return 1;
}
}
than to make a test you mock it
public void TestMethod1()
{
//Mock of the foo class
var t = new Mock<foo>(MockBehavior.Strict);
//Setup to return what we want 0 instead of 1
t.Setup(e => e.bar(It.IsAny<int>(), It.IsAny<string>(), It.IsAny<bool>()))
.Returns((int i, string s, bool b) => { return 0; });
//the actual object
var f = t.Object;
//the actual test
Assert.AreEqual(0, f.bar(1, "s", false));
}

Verifying an enumerable list in Moq

I'm trying to write a unit test for a method which looks like this:
public int Save(IEnumerable<int> addedIds, IEnumerable<int> removedIds)
{
var existingIds = repository.Get();
IEnumerable<int> ids = existingIds.Except(removedIds).Union(addedIds));
return repository.Create(ids);
}
The test in Moq looks like this:
repository.Setup(r => r.Get()).Returns(CreateList());
service.Save(addedIds, removedIds);
repository.Verify(r => r.Create(It.Is<IEnumerable<int>>(l => VerifyList(l))));
This fails, with this error, and VerifyList() is never called:
Expected invocation on the mock at least once, but was never
performed:
r => r.Create(It.Is<IEnumerable'1>(list => VerifyList(list)))
Performed invocations:
IRepo.Create(System.Linq.Enumerable+<UnionIterator>d__88'1[System.Int32])
As the invoked type is not IEnumerable<int> but is in fact System.Linq.Enumerable+<UnionIterator>d__88'1[System.Int32]), the test fails. (Stepping through the test, everything is happening correctly and the results are as expected)
If I call ids.ToList() in the method under test, these are the results:
Expected invocation on the mock at least once, but was never performed:
r => r.Create(It.Is<List'1>(l => VerifyList(l)))
Performed invocations:
IRepo.Create(System.Collections.Generic.List'1[System.Int32])
Is there any way round this? Or am I doing something wrong?
Edit: it turns out I had a mistake in my VerifyList method so it was returning false, but Moq wasn't giving me that information. The type difference is a red herring..
This seems to work. Made some assumptions though. Guess the VerifyList method could be better. =)
[Test]
public void Test()
{
// SETUP
Mock<IRepository> repository = new Mock<IRepository>();
Service service = new Service(repository.Object);
repository.Setup(r => r.Get()).Returns(CreateList());
IEnumerable<int> addedIds = new[]{1,2};
IEnumerable<int> removedIds = new[]{3,4};
service.Save(addedIds, removedIds);
repository.Verify(r => r.Create(It.Is<IEnumerable<int>>(l => VerifyList(l))));
}
private static bool VerifyList(IEnumerable<int> enumerable)
{
return enumerable.Contains(1) && enumerable.Contains(2) && enumerable.Contains(5);
}
private IEnumerable<int> CreateList()
{
return new[] { 3, 4, 5 };
}
public interface IRepository
{
IEnumerable<int> Get();
int Create(IEnumerable<int> id);
}
public class Service
{
public Service(IRepository repository)
{
this.repository = repository;
}
private IRepository repository;
public int Save(IEnumerable<int> addedIds, IEnumerable<int> removedIds)
{
var existingIds = repository.Get();
IEnumerable<int> ids = existingIds.Except(removedIds).Union(addedIds);
return repository.Create(ids);
}
You can do:
var another = new List<int> { 1 , 2, 3 };
repository.Verify(r => r.Create(It.Is<IEnumerable<int>>(l => l.SequenceEqual(another)));
Something quick and dirty -
public interface IBlah
{
void Sum(IEnumerable<int> baz);
}
class Blah : IBlah
{
public void Sum(IEnumerable<int> baz)
{
return;
}
}
public class Baz
{
private readonly IBlah blah;
public Baz(IBlah blah)
{
this.blah = blah;
}
public void Sum(IEnumerable<int> baz)
{
blah.Sum(baz);
}
}
And Test it like -
[Test]
public void foo()
{
var mock = new Mock<IBlah>();
var enumerable = Enumerable.Range(1, 10);
var baz = new Baz(mock.Object);
baz.Sum(enumerable.Where(x => x%2 == 0));
mock.Verify(p => p.Sum(It.Is<IEnumerable<int>>(z => z.All(x => x%2==0))));
}

Categories