【问题标题】:How to catch event while unit testing?如何在单元测试时捕获事件?
【发布时间】:2017-04-22 17:22:52
【问题描述】:

您好,我在使用 NUnit 测试事件时遇到问题。我什至不确定这应该是单元测试还是功能测试。让我先给你看示例类(我正在尝试测试OnValueInjected 事件):

public class Foo
{
    private IBar CurrentBar { get; set; }
    public event EventHandler<MoveEventArgs> OnValueInjected;

    public Foo()
    {
        StartFoo();
    }    

    private async void StartFoo()
    {
        await Task.Factory.StartNew(() =>
        {
            while (State != FooState.Finished)
            {
                IResult result = CurrentBar.WaitForValue(); // This is blocking function, wait for a value

                OnValueInjected?.Invoke(this, new ResultEventArgs(result));

                // .. rest of the loop
            }
        });
    }

    public void InjectValue(int a, int b)
    {
        CurrentBar.Inject(a,b);
    }
}

所以,基本上我要做的是订阅该事件,调用InjectValue 并检查是否调用了该事件。像这样:

    [Test]
    public void FooOnValueInjectedTest()
    {
        bool OnValueInjectedWasRasied = false;

        IFoo foo = new Foo();
        foo.OnValueInjected += (s, e) => OnValueInjectedWasRasied = true;

        foo.InjectValue(0,0);

        Assert.AreEqual(true, OnValueInjectedWasRasied);
    }

非常简单,但看起来InjectValue 太慢了。测试失败了..我认为它太慢了,因为当我在 InjectValueAssert 之间添加 Thread.Sleep 时。

        foo.InjectValue(0,0);
        Thread.Sleep(1000);
        Assert.AreEqual(true, OnValueInjectedWasRasied); 

有没有更好的方法来测试这样的事件?谢谢

我修好了我的课,所以现在是这样的:

public class Foo
{
    private AutoResetEvent AutoReset { get; }

    private IBar CurrentBar { get; set; }
    public event EventHandler<MoveEventArgs> OnValueInjected;

    public Foo()
    {
        AutoReset = new AutoResetEvent(false);
        StartFoo();
    }

    private async void StartFoo()
    {
        await Task.Factory.StartNew(() =>
        {
            while (State != FooState.Finished)
            {
                IResult result = CurrentBar.WaitForValue(); // This is blocking function, wait for a value

                OnValueInjected?.Invoke(this, new ResultEventArgs(result));

                AutoReset.Set();

                // .. rest of the loop
            }
        });
    }

    public void InjectValue(int a, int b)
    {
        if (CurrentBar.Inject(a,b))
        {
            AutoReset.WaitOne();
        }
    }
}

【问题讨论】:

  • 那么,您是否想要在事件处理程序确定被引发之前返回您的InjectValue?您基本上看到了异步的自然结果……这实际上与事件无关,而是与异步执行有关。
  • @JonSkeet hmm.. 从技术上讲,您是说 InjectValue 可以被阻止直到 OnValueInjected 被提出?其实我还没想好..
  • 如果你想要这种行为,你可以确保它,是的。但不管你是否这样做是另一回事。
  • @JonSkeet 谢谢!我实际上想要这种行为。我用更新的代码编辑了我的问题。你能检查一下这是否是你的意思吗?测试通过了,但我只是想确保我们在同一页面上,谢谢!
  • 这是一种潜在的解决方案,但您可能需要考虑多个线程同时调用InjectValue 的情况。如果您想确保对InjectValue 的一次调用仅在 那个 值触发所有事件处理程序时完成,这将非常棘手。哦,如果事件处理程序自己调用InjectValue,它也会死锁。很抱歉指出这些问题 - 线程本身就很难:(

标签: c# .net unit-testing testing nunit


【解决方案1】:

我相信这是异步调用的问题。每当您在 NUnit 测试中有一个异步方法时,它都不会等待它完成,因为实际上没有人在等待它完成并返回结果。相反,您必须对异步方法执行 .Wait 以强制测试等待它完成。

我没有在代码编辑器中编写此代码,因此它可能并不完美,但这是基本思想。

public class Foo
{
    private AutoResetEvent AutoReset { get; }

    private IBar CurrentBar { get; set; }
    public event EventHandler<MoveEventArgs> OnValueInjected;

    public Foo()
    {
        AutoReset = new AutoResetEvent(false);
        StartFoo();
    }

    private async void StartFoo()
    {
        await Task.Factory.StartNew(() =>
        {
            while (State != FooState.Finished)
            {
                IResult result = CurrentBar.WaitForValue(); // This is blocking function, wait for a value

                OnValueInjected?.Invoke(this, new ResultEventArgs(result));

                AutoReset.Set();

                // .. rest of the loop
            }
        });
    }

    public async void InjectValue(int a, int b)
    {
        if (CurrentBar.Inject(a,b))
        {
            AutoReset.WaitOne();
        }
    }
}

然后在 ACT 的测试方法中执行 .Wait

[Test]
public void FooOnValueInjectedTest()
{
    // Arrange
    bool OnValueInjectedWasRasied = false;

    IFoo foo = new Foo();
    foo.OnValueInjected += (s, e) => OnValueInjectedWasRasied = true;

    // Act
    foo.InjectValue(0,0).Wait();

    // Assert
    Assert.AreEqual(true, OnValueInjectedWasRasied);
}

【讨论】:

  • 感谢您的建议,但 foo.InjectValue(0,0); 功能不可等待。不必如此。它应该将一个值注入到阻塞集合中。收集完成后,将其释放。这是一个非常真实的场景 - 所以我想知道如何测试它(不更改代码 ofc)。我应该测试它吗?
  • 我一定是对方法有误解,认为 Inject 方法在其中调用了另一个异步方法。我的错。至于你应该测试它吗?这取决于“您要测试什么?”这个问题的答案。那里似乎没有太多要测试的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-14
  • 2018-04-17
  • 2016-10-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多