I'm new to C# Moq (used Rhino Mochs in the past) and needing to test a sequence of calls to the same method. I found this cool solution that tests a sequence of return values:
http://haacked.com/archive/2009/09/29/moq-sequences.aspx/
public static class MoqExtensions
{
public static void ReturnsInOrder<T, TResult>(this ISetup<T, TResult> setup,
params TResult[] results) where T : class {
setup.Returns(new Queue<TResult>(results).Dequeue);
}
}
What I need to do is to test the values sent as a parameter to the method (rather than the values it returns) in a sequence of calls to the same method.
Rough outline ...
var expression = new MyExpressionThing();
processor.Setup(x => x.Execute(expected1)).Verifiable();
processor.Setup(x => x.Execute(expected2)).Verifiable();
processor.Setup(x => x.Execute(expected3)).Verifiable();
expression.ExecuteWith(processor.Object);
processor.Verify();
Here's what I've attempted but I'm getting the exception:
"System.ArgumentException : Invalid callback. Setup on method with parameters (String,Object[]) cannot invoke callback with parameters (String)."
// Arrange
var processor = new Mock<IMigrationProcessor>();
IList<string> calls = new List<string>();
processor.Setup(p => p.Execute(It.IsAny<string>()))
.Callback<string>(s => calls.Add(s));
// Act
var expr = new ExecuteScriptsInDirectoryExpression { SqlScriptDirectory = #"SQL2\1_Pre" };
expr.ExecuteWith(processor.Object);
// Assert
calls.ToArray().ShouldBe(new[]
{ "DELETE FROM PRE1A", "DELETE FROM PRE1B", "INSERT INTO PRE2\r\nLINE2" });
Looks like I'm using boilerplate code from the Moq "Getting Started" examples:
This link discusses this exception and links to the Moq code that fires it.
http://dailydevscoveries.blogspot.com.au/2011/04/invalid-callback-setup-on-method-with.html
I would use a callback to capture the parameter each time the mock is called, and then assert the result:
var parameters = new List<ParameterType>();
processor.Setup(x => x.Execute(It.IsAny<ParameterType>()))
.Callback<ParameterType>(param => parameters.Add(param));
CallCodeUnderTest(processor.Object);
Assert.That(parameters, Is.EqualTo(new[] { expected1, expected2, expected3 }));
Update: Based on the error message you quoted, it looks like the method you're mocking takes a second parameter that's a params object[]. You don't need to specify that parameter when you call the method (which is why you don't need it in the Setup lambda), but you do need to specify it in the generic type parameters to .Callback. Change your Callback line to:
.Callback<string, object[]>((s, o) => calls.Add(s));
Related
I have the following function that I am trying to put under unit test
public MessageListDto GetMessageList(SimpleSearchCriteria criteria)
{
var messages = _repository.GetMessages(criteria, out int total);
return new MessageListDto(messages, total);
}
and the following is my test so far, in which I am able to determine that _repository.GetMessages is call with the correct parameters.
However, how do I test that the second line is tested properly, I need to test that
A new object of type MessageListDto is constructed with the two parameters returned from the line above
the newly constructed object is returned
[Test]
public void Test1()
{
int total = 10;
var searchCriteria = new SimpleSearchCriteria();
var mockRepo = new Mock<IMessageRepository>();
var messageService = new MessageService(mockRepo.Object);
messageService.GetMessageList(searchCriteria);
mockRepo.Verify(r => r.GetMessages(searchCriteria, out total), Times.Once);
mockRepo.Verity ??????????
}
As we have discussed in the comment section your goal is to test the returned messages is passed to the dto's constructor without any modification.
With your current code you can't do that because you are creating the DTO explicitly inside your method. So, you can't replace it with a spy.
Just to clarify some terminologies:
Dummy: simple code that returns bogus data
Fake: a working alternative which can take shortcuts
Stub: custom logic with predefined data
Mock: custom logic with expectations (interactive stub)
Shim: custom logic at run-time
Spy: interceptors to record calls
To be able to capture the arguments of the MessageListDto constructor call you need to introduce an other layer between your GetMessageList method and the constructor. (In other words indirection)
For the sake of simplicity let me assume that IMessageRepository's GetMessages returns a string array.
So, you can introduce an interface like this:
public interface IDtoProvider
{
public MessageListDto CreateMessageList(string[] messages, int total)
=> new MessageListDto(messages, total);
}
Here I have used C# 8's interface default implementation feature
It can be further shortened with C# 9's target-typed new expression feature
If you are using older C# version then you need to separate the interface and the default implementation from each other
Now lets amend your MessageService to receive a IDtoProvider instance via its constructor
class MessageService
{
private readonly IMessageRepository _repository;
private readonly IDtoProvider _dtoProvider;
public MessageService(IMessageRepository repository, IDtoProvider dtoProvider)
=> (_repository, _dtoProvider) = (repository, dtoProvider);
public MessageListDto GetMessageList(SimpleSearchCriteria criteria)
{
var messages = _repository.GetMessages(criteria, out int total);
return _dtoProvider.CreateMessageList(messages, total);
}
...
}
Here I've took advantage of ValueTuple's deconstruct capability
With these in our hand we can write a unit test like this:
//Arrange - Repo
string[] messages = new[] { "OnlyElement" };
int total = 10;
var mockRepo = new Mock<IMessageRepository>();
mockRepo
.Setup(repo => repo.GetMessages(It.IsAny<SimpleSearchCriteria>(), out total))
.Returns(messages);
//Arrange - Provider
var dto = new MessageListDto(messages, total);
string[] messagesArgument = null;
int totalArgument = -1;
var mockProvider = new Mock<IDtoProvider>();
mockProvider
.Setup(provider => provider.CreateMessageList(It.IsAny<string[]>(), It.IsAny<int>()))
.Callback<string[], int>((messages, total) => (messagesArgument, totalArgument) = (messages, total))
.Returns(dto);
//Arrange - SUT
var searchCriteria = new SimpleSearchCriteria();
var sut = new MessageService(mockRepo.Object, mockProvider.Object);
//Act
sut.GetMessageList(searchCriteria);
//Assert - Repo
mockRepo.Verify(r => r.GetMessages(searchCriteria, out total), Times.Once);
//Assert - Provider
Assert.Equal(messages, messagesArgument);
Assert.Equal(total, totalArgument);
I've defined how should the IMessageRepository mock behave
I've defined how should the IDtoProvider mock behave
2.1 I've used here the Callback method to capture the calling argument
2.2 If you perform multiple calls against your mocked method then please consider to use Capture.In
I've instantiated a MessageService (which is our system under test) with the mock objects
I've called the GetMessageList inside the Act phase
I've made assertion against the repo mock call
I've made assertion against the calling parameters of the CreateMessageList
6.1 Here I have used xunit's assertion
The first step is to setup the MessageService mock, so that it returns something deterministic and then in the second step you verify that that has been used to construct your MessageListDto.
[Test]
public void Test1()
{
// arrange
int total = 10;
var searchCriteria = new SimpleSearchCriteria();
var messages = new [] {"message1", "message2"} // or similar
var mockRepo = new Mock<IMessageRepository>();
// or similar, I am not quite certain as to the specific syntax. Especially wrt. out parameters. Check the documentation.
mockRepo.Setup(x => x.GetMessages(It.IsAny<SimpleSearchCriteria>(), It.IsAny<int>())).Returns(messages);
var messageService = new MessageService(mockRepo.Object);
// act
var result = messageService.GetMessageList(searchCriteria);
// assert
mockRepo.Verify(r => r.GetMessages(searchCriteria, out total), Times.Once);
// or similar; here you might want to check out FluentAssertions as #Peter Csala suggested
Assert.Equal(result.Messages, messages);
}
What's missing from previous answers is that whenever possible, you should set up the mocked method with the exact parameters, that you expect it to receive. In other words set up the method with concrete values instead of It.IsAny<>(). In such case you won't have to verify the method later at all. (Unless it's critical to test, how many times a method is called). Simply, if mocked GetMessages receives different arguments than expected, it will return null, set total to 0 and your test will fail. Having the mocked methods set up properly, you can now focus on verifying what GetMessageList returns, which is the purpose of this unit test.
[Test]
public void Test1()
{
int total = 10;
var messages = new[] {new Message()};
var searchCriteria = new SimpleSearchCriteria();
var mockRepo = new Mock<IMessageRepository>();
mockRepo.Setup(_ => _.GetMessages(searchCriteria, out total))
.Returns(messages);
var messageService = new MessageService(mockRepo.Object);
var dto = messageService.GetMessageList(searchCriteria);
Assert.AreSame(messages, dto.Messages);
Assert.AreEqual(total, dto.Total);
}
I am having a test method that asserts if the CreateClient method of the client account repository has been called. Please See the test bellow.
[TestMethod]
public void CreateNewBasicClientAccount_NewBasicClient_CreatesNewClientBasicClient()
{
// Arrange
var clientAccountToCreate = new ClientAccount
{
Name = "Name",
};
var clientAccountToCreateDto = AutoMapper.Mapper.Map<ClientAccount, ClientAccountDto>(clientAccountToCreate);
var clientAccountRepository = A.Fake<IClientAccountRepository>();
var clientAccountManager = new ClientAccountManager(clientAccountRepository);
// Act
clientAccountManager.CreateClient(clientAccountToCreate);
// Assert
A.CallTo(
() => clientAccountRepository.CreateClient(A<ClientAccountDto>.That.IsNotNull<ClientAccountDto>()))
.MustHaveHappened();
A.CallTo(
() => clientAccountRepository.CreateClient(A<ClientAccountDto>.Ignored))
.MustHaveHappened();
A.CallTo(
() => clientAccountRepository.CreateClient(clientAccountToCreateDto))
.MustHaveHappened();
}
The Act portion of my ClientAccountManager class in the test is calling the CreateClient method of the repository
public void CreateClient(ClientAccount client)
{
var clientDto = AutoMapper.Mapper.Map<ClientAccount, ClientAccountDto>(client);
_clientAccountRepository.CreateClient(clientDto);
}
The first two asserts in the test pass, but the more specific 3rd assert fails with result message
InterfaceNameSpace.IClientAccountRepository.CreateClient(clientDto: DtoNameSpace.ClientAccountDto)
Expected to find it at least once but found it #0 times among the calls:
Both the ClientAccount and ClientAccountDto classes have the exact same properties. Input in getting the failed assert to pass would be appreciated as the code is wired up for it to pass but it fails.
This is because the actual ClientAccountDto that is passed to the method is not the same instance as the one you create in your test, so they're not considered equal.
There are several options to solve this:
override the Equals method in ClientAccountDto (not ideal, since you normally wouldn't need this for a DTO)
inject an IMapper into ClientAccountManager, instead of using the static Mapper class, and configure the IMapper to return a specific instance of ClientAccountDto
test specific properties of the ClientAccountDto, like this:
A.CallTo(
() => clientAccountRepository.CreateClient(A<ClientAccountDto>.That.Matches(x => x.Name == "Name")))
.MustHaveHappened();
Unrelated note: you don't need to specify the type again in A<ClientAccountDto>.That.IsNotNull<ClientAccountDto>(), you can just write A<ClientAccountDto>.That.IsNotNull().
Taken the question from this article (How to moq a Func) and adapted it as the answer is not correct.
public class FooBar
{
private Func<IFooBarProxy> __fooBarProxyFactory;
public FooBar(Func<IFooBarProxy> fooBarProxyFactory)
{
_fooBarProxyFactory = fooBarProxyFactory;
}
public void Process()
{
_fooBarProxyFactory();
_fooBarProxyFactory();
}
}
I have a need to mock a Func<> that is passed as a constructor parameter, the assert that the func was call twice.
When trying to mock the function var funcMock = new Mock<Func<IFooBarProxy>>(); Moq raises and exception as the Func type is not mockable.
The issue is that without mocking the func it is not possible to verify that the func was called (n) times. funcMock.Verify( (), Times.AtLeast(2));
I don't think it is necessary to use a mock for the Func.
You can simply create an ordinary Func yourself that returns a mock of IFooBarProxy:
int numberOfCalls = 0;
Func<IFooBarProxy> func = () => { ++numberOfCalls;
return new Mock<IFooBarProxy>(); };
var sut = new FooBar(func);
sut.Process();
Assert.Equal(2, numberOfCalls);
As of at least Moq 4.5.28, you can mock and verify the Func as you would expect to be able to. I couldn't tell when this feature was added (according to the original question at some point this did not work).
[Test]
public void TestFoobar()
{
var funcMock = new Mock<Func<IFooBarProxy>>();
var fooBar = new FooBar(funcMock.Object);
fooBar.Process();
funcMock.Verify(x => x(), Times.AtLeast(2));
}
Since Moq v4.1.1308.2120
As of this version, which was released some months after this question was asked (Aug 21, 2013), the functionality to mock a Func<> has been added. So with any current version of mock, you can use var funcMock = new Mock<Func<IFooBarProxy>>();.
Original (outdated) answer
If you have a lot of callback Func's, Actions, etc, it's better to define a helper interface in your tests and mock that interface. This way you can use the regular Moq functionality, like setting up return values, testing input arguments, etc.
interface IFooBarTestMethods
{
IFooBarProxy FooBarProxyFactory();
}
Usage
var testMethodsMock = new Mock<IFooBarTestMethods>();
testMethodsMock
.Setup(x => x.FooBarProxyFactory())
.Returns(new Mock<IFooBarProxy>());
var sut = new FooBar(testMethodsMock.Object.FooBarProxyFactory);
testMethodsMock.Verify(x => x.FooBarProxyFactory(), Times.Exactly(2));
For Complex scenario like the below one
public interface ISqlConnector
{
Task<int> ConnectAsync(Func<IDbConnection, Task<int>> performDatabaseOperationAsync, string connectionString);
}
You can use
var funcMock = new Mock<Func<IDbConnection,Task<int>>>();
//setup Mock
sqlConnector.Setup(a => a.ConnectAsync(funcMock.Object, It.IsAny<string>()));
I have the following method:
public void MoveChannelUp(string channelName)
{
var liveChannels = _repository.GetChannels<LiveChannel>();
var channels = GetModifiedChannelsList(channelName, liveChannels);
_repository.SaveChannels(channels);
}
I want to set up an expectation on the SaveChannels() call so that the correct channels parameter is passed in.
I tried :
channelsRepository.Setup(x => x.SaveChannels(reorderedChannels));
where reorderedChannels is what I expect the GetModifiedChannelsList() call will return and but I got Mock verification exception (probably due to reorderedChannels is not the same object as channels???)
So it is GetModifiedChanneslsList() which I really want to test (I know I can use reflection to test this)
So how do I test the correct channels list is passed to SaveChannels()?
You can do something like this (I assume there is a type called Channel and the parameter for SaveChannels is List<Channel>; substitute with the actual):
var expectedChannels = new List<Channel> { new Channel() }; // set up expected channels here
var channelsRepo = new Mock<IChannelsRepository>();
// perform your unit test using channelsRepo here, for example:
channelsRepo.Object.SaveChannels(new List<Channel> { new Channel() });
channelsRepo.Verify(x => x.SaveChannels(It.Is<List<Channel>>(l => l.SequenceEqual(expectedChannels)))); // will throw an exception if call to SaveChannels wasn't made, or the List of Channels params did not match the expected.
What this code does is verify that the SaveChannels method is called at least once with the right list of channels. If that does not happen, Verify will throw an exception and your unit test will fail as expected.
I was very happy with Moq until I needed to test a method that takes a delegate as parameter and got an UnsupportedException. The issue is also mentioned here and on Moq issue list.
Is there any framework that supports this kind of mocking?
For example:
///
/// Interfaces
///
public interface IChannelFactory<T> {
TReturn UseService<TReturn>(Func<T, TReturn> function);
}
public interface IService {
int Calculate(int i);
}
///
/// Test
///
Mock<IChannelFactory<IService>> mock = new Mock<IChannelFactory<IService>>();
// This line results in UnsupportedException
mock.Setup(x => x.UseService(service => service.Calculate(It.IsAny<int>()))).Returns(10);
I'm not quite sure what you're trying to do, but this compiles and runs using your interfaces with Moq 4:
var mock = new Mock<IChannelFactory<IService>>();
mock.Setup(x => x.UseService(It.IsAny<Func<IService, int>>())).Returns(10);
int result = mock.Object.UseService(x => 0);
Console.WriteLine(result); // prints 10
See also this answer for a more complex case.
I ran into this same issue recently, and here's how to use moq (v4.0.10827) to test the correct method and parameters are called. (Hint: you need two layers of mocks.)
//setup test input
int testInput = 1;
int someOutput = 10;
//Setup the service to expect a specific call with specific input
//output is irrelevant, because we won't be comparing it to anything
Mock<IService> mockService = new Mock<IService>(MockBehavior.Strict);
mockService.Setup(x => x.Calculate(testInput)).Returns(someOutput).Verifiable();
//Setup the factory to pass requests through to our mocked IService
//This uses a lambda expression in the return statement to call whatever delegate you provide on the IService mock
Mock<IChannelFactory<IService>> mockFactory = new Mock<IChannelFactory<IService>>(MockBehavior.Strict);
mockFactory.Setup(x => x.UseService(It.IsAny<Func<IService, int>>())).Returns((Func<IService, int> serviceCall) => serviceCall(mockService.Object)).Verifiable();
//Instantiate the object you're testing, and pass in the IChannelFactory
//then call whatever method that's being covered by the test
//
//var target = new object(mockFactory.Object);
//target.targetMethod(testInput);
//verifying the mocks is all that's needed for this unit test
//unless the value returned by the IService method is used for something
mockFactory.Verify();
mockService.Verify();
Have a look at Moles. It supports delegates as mocks.
Moles