【问题标题】:C# and Moq - How to Assert an Event is Not RaisedC# 和 Moq - 如何断言未引发事件
【发布时间】:2012-09-15 14:57:45
【问题描述】:

我有一个应用程序根据 Sim 对象的状态引发事件。特别是,如果 Sim 的状态发生变化 - 即从 PinRequired 变为 Accessible - 将引发 OnStatus 事件。事件通知新线程上的侦听器。

我在单元测试中使用 Moq 并且能够断言 OnStatus 是这样引发的:

        [Test]
    public void TestOnStatusIsRaised()
    {
        Sim sim = new Sim();

        sim.OnStatus += OnStatus;

        lock (this)
        {
            sim.UpdateSimInfo(new Info("a new status"));
            Monitor.Wait(this);
        }

        Assert.IsTrue(_onStatusCalled);
    }

    private void OnStatus(SimStatus obj)
    {
        lock (this)
        {
            _onStatusCalled = true;
            Monitor.Pulse(this);
        }
    }

如您所见,使用 Monitor 类,我可以等到引发事件后再继续并断言 _onStatusCalled 标志已设置为 true。

当我想断言没有引发事件时,我的困难就出现了。我不能使用 Monitor 等待事件不被引发,因为测试会永远等待!我可以在等待中添加一个超时,但这似乎是一个肮脏的黑客攻击。

我尝试了以下方法:

        [Test]
    public void TestOnStatusIsNotFired()
    {
        Sim sim = new Sim();
        sim.OnStatus += onStatus => Assert.Fail("OnStatus Was called");

        sim.UpdateSimInfo(new Info("the same status"));
    }

但它不起作用 - 在修复代码以确保不引发事件之前,测试始终通过。我什至单步执行了代码并观察到Assert.Fail() 引发了异常,但这不会导致测试失败,因为我的事件在 try/catch 块中引发并且 NUnit 异常被捕获。

有什么想法吗?

【问题讨论】:

  • 你不能在你想测试的代码循环完成时做一个事件,当你收到那个事件时,确保没有引发另一个事件吗?

标签: c# unit-testing events moq


【解决方案1】:

简而言之,您不要对多线程代码进行单元测试。您将其拆分为单线程部分并分别测试。

【讨论】:

  • 那会在我的测试中留下一个漏洞。我可以对我的 Sim 对象是否正确更新进行单元测试——当然必须有一种模式来测试观察者是否被告知/不被告知,即使它不被称为“单元测试”。
  • @barry 模式很简单:您将多线程移动到另一个模块(我们称之为dispatcher),然后测试您的Sim 调用或不调用模拟dispatcher(它一切都是同步发生的,所以很容易),您可以测试真正的dispatcher 在调用时调度事件。在工作中分离关注点。
  • 谢谢 Serg,这听起来很明智
【解决方案2】:

如果你也有一个 NoStatus 事件呢。

[Test]
public void TestOnStatusIsNotFired()
{
    Sim sim = new Sim();
    var mre = new ManualResetEvent(false);
    sim.OnStatus += onStatus => { mre.Set(); Assert.Fail("OnStatus Was called");}
    sim.NoStatus += () => { mre.Set();}

    sim.UpdateSimInfo(new Info("the same status"));
    mre.WaitOne()
}

【讨论】:

  • 如果可能的话,我真的不想在我的应用程序代码中添加事件。此外,这仍然依赖于 Assert.Fail() 导致测试失败,但它没有。
【解决方案3】:

如何使用类似的东西:

mock.Verify(foo => foo.Execute("ping"), Times.Never());

您还可以验证 OnStatus 是否已被调用:

mock.Verify(foo => foo.Execute("ping"), Times.AtLeastOnce());

试试这个链接以获得很多好的短样本:http://code.google.com/p/moq/wiki/QuickStart

【讨论】:

  • 这能在多线程环境中可靠地工作吗?我的意思是,在什么时候调用验证。在我们验证它没有被调用之后,是否可以调用onstatus?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-10-07
  • 2016-09-14
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
相关资源
最近更新 更多