简体   繁体   English

模拟从IEnumerable <T>继承的抽象类

[英]Mocking an abstract class that inherits from IEnumerable<T>

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() . 首先,冗余继承,也没有非抽象方法的实现,例如GetEnumeratorIEnumerable.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. 当你foreach一个序列,IIRC编译器会desugar,要到通用版的调用GetEnumerator ,所以这是你必须嘲笑之一。

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). Event有三个公共成员(以及一个显式实现的接口方法)。 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). 如果你想让它在你的测试代码中返回一些东西,你需要实际模拟GetEnumerator实现(当然你也应该模拟非泛型版本,以防其他一些测试代码试图使用它)。

Foreword The code of the Event class that you pasted is only a metadata representation. 前言您粘贴的Event类的代码只是元数据表示。 If you really want to see its source code, use a full decompiler such as ILSpy ( VS extension ). 如果你真的想看到它的源代码,请使用完整的反编译器,如ILSpyVS扩展 )。 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 Event类as-is无法完全模拟,因为GetEnumerator不是虚拟的(至少就你的代码片段而言),所以

  • you must use its body as-is; 你必须按原样使用它的身体; and
  • not even a mocking libraries can replace it. 即使是模拟库也无法取代它。

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. 由于该类隐式实现IEnumerable<Message> ,因此foreach循环直接调用声明的方法,而不是您在GetMockedEvent方法中设置的“显式实现”。

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. 我决定将NotImplementedException作为未知方法体的“中性”替换。

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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM