I'm trying to mock an abstract class from a library that I'm using. I don't have access to the source code, only the decompiled version:
public abstract class Event : IEnumerable<Message>, IEnumerable
{
protected Event();
public abstract bool IsValid { get; }
public IEnumerator<Message> GetEnumerator();
public IEnumerable<Message> GetMessages();
}
This decompiled code confuses me slighty. First, the redundant inheritance, and also there's no implementation of non-abstract methods eg GetEnumerator
or IEnumerable.GetEnumerator()
. But it has compiled, and it works so I suppose it's just an artefact of the decompilation (if that is even a thing?)
I have tried the following mock, which compiles and runs without throwing exceptions.
public static Event GetMockedEvent()
{
var mock = new Mock<Event>();
mock.Setup(e => e.IsValid).Returns(true);
mock.As<IEnumerable>().Setup(e => e.GetEnumerator()).Returns(MessageList());
return mock.Object;
}
private static IEnumerator<Message> MessageList()
{
yield return GetMockedMessage();
yield return GetMockedMessage();
}
private static Message GetMockedMessage()
{
var mock = new Mock<Message>();
// Unimportant setups...
return mock.Object;
}
However I don't get any elements in the mocked object which I test in the following way
var ev = GetMockedEvent();
foreach (var msg in ev)
{
//
}
But the enumeration is empty, and I cannot figure out why. I have scratched my head with this issue for a full day now so I'd be very appreciative of your help.
Kind regards
When you foreach
over a sequence, IIRC the compiler will desugar that to a call to the generic version of GetEnumerator
, so that's the one you'll have to mock.
Something like this might do the trick, although I haven't tried:
public static Event GetMockedEvent()
{
var mock = new Mock<Event>();
mock.Setup(e => e.IsValid).Returns(true);
mock.As<IEnumerable<Message>>()
.Setup(e => e.GetEnumerator())
.Returns(() => MessageList());
return mock.Object;
}
Event
has three public members (and one explicitly implemented interface method). You've mocked two of them, and then written test code that uses the third. You need to actually mock the GetEnumerator
implementation if you want it to return something in your test code (and of course you should mock the non-generic version as well, in case some other test code tries to use it).
Foreword The code of the Event
class that you pasted is only a metadata representation. If you really want to see its source code, use a full decompiler such as ILSpy ( VS extension ). End foreword
The Event
class as-is can't be fully mocked, because the GetEnumerator
is not virtual (at least as far as your snippet is telling), so
Since the class implements IEnumerable<Message>
implicitly, the foreach
loop calls directly the declared method, not the "explicit implementation" that you setup in the GetMockedEvent
method.
To be clear, below is the full snippet that I tried to run. I decided to throw a NotImplementedException
as a "neutral" replacement for the unknown method bodies.
void Main()
{
var ev = GetMockedEvent();
foreach (var msg in ev)
{
Console.WriteLine(msg);
}
}
public abstract class Event : IEnumerable<Message>, IEnumerable
{
protected Event() { }
public abstract bool IsValid { get; }
public IEnumerator<Message> GetEnumerator() { throw new NotImplementedException(); }
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
public IEnumerable<Message> GetMessages() { throw new NotImplementedException(); }
}
public class Message { }
public static Event GetMockedEvent()
{
var mock = new Mock<Event>();
mock.Setup(e => e.IsValid).Returns(true);
mock.As<IEnumerable<Message>>().Setup(e => e.GetEnumerator()).Returns(MessageList());
// The next line doesn't work either because the method is not virtual
//mock.Setup(e => e.GetEnumerator()).Returns(MessageList());
return mock.Object;
}
private static IEnumerator<Message> MessageList()
{
yield return GetMockedMessage();
yield return GetMockedMessage();
}
private static Message GetMockedMessage()
{
var mock = new Mock<Message>();
// Unimportant setups...
return mock.Object;
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.