在单元测试try / catch中未捕获到引发的异常

时间:2018-07-04 16:22:24

标签: c# oop exception

我现在正在设计一些代码,如果字符串参数为null或为空,并且会按应有的方式引发异常,那么我将引发异常,但是在进行UnitTesting时不会被捕获。

这是我正在使用的客户端。

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

这是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);
        }
    }

因此,当我运行该测试时,据我所知应该在调用Send方法(确实如此)时引发异常,然后由于未在PipeClient中捕获它而被catch子句捕获。但事实并非如此,它只是以失败的测试退出。

如果您需要更多信息,请提前告诉我。

1 个答案:

答案 0 :(得分:2)

我想在这个答案中提出几点。我不确定您的经验水平,所以请不要以为我在任何时候都很自负。

首先简要介绍一下异步方法和任务。

  • 除非在异步事件处理程序中,否则应避免异步void。异步方法应返回Task或Task,否则调用方将无法知道该方法何时完成并报告该方法是否引发异常。异步虚空本质上是一劳永逸,忘记了,没有人可以观察到异常。
  

“在观察到的任务中没有人可以尖叫” -Me,2018年

  • 在异步方法中抛出的异常被很好地解开并抛出 等待异步方法时,所有调用堆栈都保留 并且合理地合理。如果您不等待结果,最终会在 在将来的某个时候,您将获得UnobservedTaskException 如果您尚未为其配置全局处理程序,则会降低 你的申请。如果得到异步方法的结果 同步使用.Wait()或.Result或通过 .GetAwaiter()。GetResult()(您应该尝试避免的所有3个,但第三个 选项是最好的,如果您必须告知我),那么您将 获取包装在AggregateException中的原始异常。

现在,如果这一切对您都没有意义,我建议您进行一些阅读,了解Tasks和async / await。

现在进入测试。

您的方法是异步无效的,因此没有任何调用方法可以返回给它来表示工作或让它知道该方法已引发异常。因此它继续进行,测试完成,然后一切正常完成,因为UnobservedTaskException可以在将来的任何时候抛出(我认为这与垃圾收集器整理有问题的Task的时间有关,然后抛出该异常,因为垃圾收集器是不确定的,我们无法确定何时会发生

那么,如果您使异步方法返回任务怎么办???嗯,这还不太正确。现在,由于异常,您将返回一个处于故障状态的任务,但是由于您从未等待过它,因此异常从未被“解包”并实际被抛出,因此您很高兴地进行了测试。

您需要做的是使您的测试异步并返回一个Task,并使正在测试异步的方法Task异步无效,然后在测试中等待该方法。

[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();
            }
        }
    }
}