简体   繁体   English

抛出异常未在UnitTest try / catch中捕获

[英]Thrown Exception not caught in UnitTest try/catch

I'm designing some code right now where I'm throwing an exception if a string parameter is null or empty and the exception is thrown as it should be, but it isn't getting caught when I'm UnitTesting. 我现在正在设计一些代码,如果字符串参数为null或为空,我抛出一个异常,并且应该抛出异常,但是当我进行UnitTesting时它不会被捕获。

Here's the client I'm using. 这是我正在使用的客户端。

public class PipeClient : IPipeClient
{
    public async void Send(string host, string pipeName, Message msg)
    {
        if (string.IsNullOrEmpty(msg.PreparedMessage))
            throw new ArgumentException("MESSAGE_NOT_FOUND");

        if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(pipeName))
            throw new ArgumentNullException();

        if (!host.TryParseHost()) 
            throw new ArgumentException("INVALID_HOST_NAME");

        using (var pipeClient = new NamedPipeClientStream(host, pipeName, PipeDirection.Out))
        {
            pipeClient.Connect(200);

            using (var writer = new StreamWriter(pipeClient))
            {
                await Task.Run(() => writer.WriteLine(msg.PreparedMessage));
                writer.Flush();
            }
        }
    }
}

And here's the UnitTest 这是UnitTest

    [TestMethod]
    public void Send_FailsOnWrongHostName()
    {
        var name = "FailWithHostname";
        var msg = new Message(MyStates.Register, "UnitTest", "Test");

        try
        {
            var client = new PipeClient();
            client.Send("lol", name, msg);
        }
        catch (Exception e)
        {
            Assert.IsTrue(e is ArgumentException);
        }
    }

So when I run that test it should as far as I know throw the exception when I call the the Send method (which is does) and then get caught in the catch clause because I'm not catching it inside the PipeClient. 因此,当我运行该测试时,就我所知,当我调用Send方法时抛出异常(这样做)然后被catch子句捕获,因为我没有在PipeClient中捕获它。 Yet it doesn't, it just exits with a failed test. 然而事实并非如此,只是在测试失败的情况下退出。

If you need any more information just let me know, thanks in advance. 如果您需要更多信息,请提前告知我们。

there's a few things I want to raise in this answer. 我想在这个答案中提出一些事情。 I'm not sure of your experience level so please don't think I'm being condescending at any point. 我不确定你的经验水平,所以请不要认为我在任何时候都是居高临下。

Firstly a brief note on async methods and Tasks. 首先是关于异步方法和任务的简要说明。

  • Async void should be avoided unless in an async event handler. 除非在异步事件处理程序中,否则应避免异步void。 Async methods should return Task or Task otherwise there is nothing for the calling method to keep hold of to know when the method is done and to report back whether the method threw an exception. 异步方法应该返回Task或Task,否则调用方法没有任何东西可以保持知道方法何时完成并报告该方法是否引发了异常。 Async void is essentially fire and forget, there is no one left to observe the exceptions. Async void本质上是火和忘记,没有人留下观察异常。

"In observed Tasks no one can you scream" -Me ,2018 “在观察到的任务中,没有人可以尖叫”-Me,2018年

  • Exceptions thrown in async methods are nicely unwrapped and thrown when the async method is awaited, with the call stack all preserved and reasonably sensible. 异步方法中抛出的异常在等待异步方法时很好地展开和抛出,调用堆栈全部保留并且合理合理。 If you don't await the result eventually at some point in the future you will get an UnobservedTaskException that, if you haven't configured a global handler for, will bring down your application. 如果你最近在某个时候没有等待结果,你将得到一个UnobservedTaskException,如果你还没有为其配置全局处理程序,它将关闭你的应用程序。 If you get the result of an async method synchronously using .Wait() or .Result or via .GetAwaiter().GetResult() (all 3 you should try and avoid but the 3rd option is best if you have to I have been informed), then you will get the original exception wrapped in an AggregateException. 如果你使用.Wait()或.Result或通过.GetAwaiter()同步获得异步方法的结果.GetResult()(所有3你应该尝试避免,但第3选项是最好的,如果你必须我已被告知),然后您将获得包含在AggregateException中的原始异常。

Now if none of this is making much sense to you, I would recommend doing some reading up Tasks and async/await. 现在,如果这对你没有多大意义,我建议你做一些阅读任务和异步/等待。

Now onto your Test. 现在进入你的测试。

Your method is async void so there is nothing for the calling method to have returned to it to represent the work or to let it know that the method has thrown an exception. 您的方法是异步void,因此调用方法没有任何内容可以返回它来表示工作或让它知道该方法引发了异常。 So it carries on, the test finishes and then everything completes with no exceptions because the UnobservedTaskException can be thrown at anypoint in the future (I think it is related to when the garbage collector tidies up the faulted Task and then it throws and because the garbage collector is non-deterministic we can't say when that will happen) 所以它继续,测试完成,然后一切都没有异常完成,因为UnobservedTaskException可以在未来的任何一点被抛出(我认为这与垃圾收集器整理故障的任务然后它抛出并因为垃圾有关收藏家是非确定性的,我们不能说什么时候会发生)

So what if you made your async method return a Task??? 那么如果你让你的异步方法返回一个任务怎么办? Well that's still not quite right. 那还不太对劲。 You are now returning a Task that will be in a faulted state because of the exception, however because you never await it, the exception is never 'unwrapped' and actually thrown and so you're test happily continues. 您现在正在返回一个因异常而处于故障状态的任务,但是因为您永远不会等待它,该异常永远不会被“解包”并且实际上被抛出,因此您将继续愉快地进行测试。

What you need to do is make your Test async and return a Task and make the method you're testing async Task not async void and await that method in your test. 你需要做的是让你的测试异步并返回一个任务并使你正在测试异步的方法任务不是async void并等待测试中的那个方法。

Like this 像这样

[TestMethod]
public async Task Send_FailsOnWrongHostName()
{
    var name = "FailWithHostname";
    var msg = new Message(MyStates.Register, "UnitTest", "Test");

    try
    {
        var client = new PipeClient();
        await client.Send("lol", name, msg);
    }
    catch (Exception e)
    {
        Assert.IsTrue(e is ArgumentException);
    }
}

public class PipeClient : IPipeClient
{
    public async Task Send(string host, string pipeName, Message msg)
    {
        if (string.IsNullOrEmpty(msg.PreparedMessage))
            throw new ArgumentException("MESSAGE_NOT_FOUND");

        if (string.IsNullOrEmpty(host) || string.IsNullOrEmpty(pipeName))
            throw new ArgumentNullException();

        if (!host.TryParseHost()) 
            throw new ArgumentException("INVALID_HOST_NAME");

        using (var pipeClient = new NamedPipeClientStream(host, pipeName, PipeDirection.Out))
        {
            pipeClient.Connect(200);

            using (var writer = new StreamWriter(pipeClient))
            {
                await Task.Run(() => writer.WriteLine(msg.PreparedMessage));
                writer.Flush();
            }
        }
    }
}

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

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