I'm trying to verify that a method with a generic argument gets called but I can't get the test to pass even though it runs without mocking.
I've got a test method:
var mockBus = new Mock<MessageBus>();
mockBus.Setup(e => e.PublishEvent(It.IsAny<EventToVerify>())).Verifiable();
var repo = new Repository(mockBus.Object);
Entity ent = new Entity();
ent.ApplyEvent(new EventToVerify("New Data"));
repo.Save(ent);
mockBus.Verify(e=>e.PublishEvent(It.IsAny<EventToVerify>()), Times.AtLeastOnce);
The verify is always failing with no invocations. I know it does get called though since a breakpoint on it gets hit when I don't mock it. Am I not setting up the Mock correctly?
Here's the rest of the code for context:
public class Event
{
public readonly Guid CorrelationId;
public Event()
{
CorrelationId = Guid.NewGuid();
}
}
public class EventToVerify : Event
{
public readonly string Data;
public EventToVerify(string data)
{
Data = data;
}
}
public interface IEventPublisher
{
public void PublishEvent<T>(T ev) where T: Event;
}
public class MessageBus : IEventPublisher
{
public virtual void PublishEvent<T>(T ev) where T : Event
{
Console.WriteLine("------------------Event WAS published: " ev.CorrelationId);
}
}
public class Entity
{
private IList<Event> _events;
public string Data { get; private set; }
public Entity()
{
Data = "Nothing";
_events = new List<Event>();
}
public void ApplyEvent(EventToVerify ev)
{
Data = ev.Data;
_events.Add(ev);
}
public IList<Event> GetEvents()
{
return _events;
}
}
public class Repository
{
private IEventPublisher _publisher;
public Repository(IEventPublisher publisher)
{
_publisher = publisher;
}
public void Save(Entity e)
{
//persistence logic here
foreach (var ev in e.GetEvents())
{
_publisher.PublishEvent(ev);
}
}
}
I have a repro project here: https://github.com/amguilmet/MoqNotMoqinExample
CodePudding user response:
It is a casting issue. IList<Event> GetEvents()
at
//...Repository
public void Save(Entity e) {
//persistence logic here
foreach (var ev in e.GetEvents()) {
_publisher.PublishEvent(ev);
}
}
means that when the test is exercised the mock is actually recoding Event
being passed into the mocked member.
I tested the code by changing to
//...
mockBus.Verify(e => e.PublishEvent(It.IsAny<Event>()), Times.AtLeastOnce);
//...
and it passes with no issue.
Even the following works with no issue as well
//...
mockBus.Verify(e => e.PublishEvent<Event>(It.IsAny<EventToVerify>()), Times.AtLeastOnce);
//...
The mock's expectations needed to be explicitly defined to behave as expected since the implied generic argument in the original example had unwanted results.