C# 如何在单元测试时捕获事件?

C# 如何在单元测试时捕获事件?,c#,.net,unit-testing,testing,nunit,C#,.net,Unit Testing,Testing,Nunit,您好,我在使用NUnit测试事件时遇到问题。我甚至不确定这是单元测试还是功能测试。让我首先向您展示示例类(我正在尝试测试OnValueInjectedevent): 非常简单,但是看起来InjectValue太慢了。测试失败了..我认为它太慢了,因为当我添加线程时,InjectValue和Assert之间的Sleep起作用 foo.InjectValue(0,0); Thread.Sleep(1000); Assert.AreEqual(true,

您好,我在使用
NUnit
测试事件时遇到问题。我甚至不确定这是单元测试还是功能测试。让我首先向您展示示例类(我正在尝试测试
OnValueInjected
event):

非常简单,但是看起来
InjectValue
太慢了。测试失败了..我认为它太慢了,因为当我添加
线程时,
InjectValue
Assert
之间的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();
        }
    }
}
公共类Foo
{
私有自动重置事件自动重置{get;}
专用IBar CurrentBar{get;set;}
公共事件事件处理程序OnValueInjected;
公共食物(
{
自动重置=新自动重置事件(假);
StartFoo();
}
私有异步void StartFoo()
{
等待任务。工厂。开始新建(()=>
{
while(State!=FooState.Finished)
{
IResult result=CurrentBar.WaitForValue();//这是阻塞函数,请等待值
OnValueInjected?.Invoke(这个,新的结果ventargs(结果));
AutoReset.Set();
//…循环的其余部分
}
});
}
公共值(整数a、整数b)
{
if(电流条注入(a,b))
{
AutoReset.WaitOne();
}
}
}

我认为这是异步调用的问题。无论何时在NUnit测试中有一个异步方法,它都不会等待它完成,因为实际上没有人在等待它完成并返回结果。相反,您必须对async方法执行.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();
        }
    }
}

那么,您是否希望在事件处理程序确实被引发之前返回
InjectValue
?您基本上看到了异步的自然结果。。。它实际上不是关于事件,而是关于异步执行。@JonSkeet-hmm。。从技术上讲,您是说,
InjectValue
可以被阻止,直到引发
OnValueInjected
?其实我还没想过……如果你想要那种行为,你可以保证,是的。不管你做不做都是另外一回事。@JonSkeet谢谢!我真的想要那种行为。我用更新的代码编辑了我的问题。你能看看这是不是你的意思吗?测试通过了,但我只是想确定我们在同一页上,谢谢!这是一个可能的解决方案,但是您可能需要考虑多线程同时调用<代码>注入值< /代码>的情况。如果您想确保只有当
InjectValue
的一个调用触发了所有事件处理程序时,才能完成该调用,那么这将非常棘手。哦,如果事件处理程序调用
InjectValue
本身,它也会死锁。很抱歉指出这些问题-线程本来就很难:(谢谢你的建议,但是
foo.InjectValue(0,0);
函数是不可等待的。不必等待。它应该向阻塞集合中注入一个值。在集合被填充后,它就被释放了。这是一个非常真实的场景-所以我想知道如何测试它(没有更改ofc的代码)。我应该测试它吗?我一定误解了该方法,认为Inject方法正在调用其中的另一个异步方法。我的错。至于你应该测试它吗?这取决于“你想测试什么?”这一问题的答案似乎没有太多需要测试的地方。
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();
        }
    }
}
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();
        }
    }
}
[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);
}