简体   繁体   English

如何使用xUnit测试ref struct方法是否抛出异常?

[英]How to test whether a ref struct method is throwing an exception using xUnit?

I'm new to xUnit, but, as far as I know, the standard way of checking whether something throws an exception is to use Assert.Throws<T> or Assert.ThrowsAny<T> methods. 我是xUnit的新手,但据我所知,检查是否抛出异常的标准方法是使用Assert.Throws<T>Assert.ThrowsAny<T>方法。

But these methods expect an Action as parameter; 但是这些方法期望将Action作为参数; and ref structs can't be "embedded" in lambdas. 并且ref结构不能“嵌入”lambda中。

So, how does one test whether a given method of a ref struct is throwing? 那么,如何测试ref struct的给定方法是否抛出? Example of code that doesn't work: 不起作用的代码示例:

[Fact]
public void HelpMe() {
    var pls = new Span<byte>();
    Assert.ThrowsAny<Exception>(() => {
        plsExplode = pls[-1];
    });
}

A ref struct can't be captured in a lambda expression, but you can still use it in a lambda expression - you just need to declare the variable there, so that it's never a field within a non-ref-struct. 无法在lambda表达式中捕获 ref结构,但您仍然可以在lambda表达式中使用它 - 您只需要在那里声明变量,这样它就不会是非ref-struct中的字段。

For example, this compiles and succeeds: 例如,这会编译并成功:

[Fact]
public void HelpMe()
{
    Assert.ThrowsAny<Exception>(() => {
        var pls = new Span<byte>();
        var plsExplode = pls[-1];
    });
}

Now I'll be the first to admit that this isn't ideal: you really want to do the minimum amount of work possible within the action, so that you only pass if the expected piece of code fails. 现在我将是第一个承认这不理想的人:你真的想在动作中尽可能少地完成工作,这样你只有在预期的代码段失败时才会通过。

Using Assert.Throws helps there, so that only the expected exception results in a pass. 使用Assert.Throws帮助,因此只有预期的异常会导致传递。 Additionally, you could capture a bool which is changed just before the throwing part, then check that you'd got that far: 另外,你可以捕获一个在投掷部分之前改变的bool ,然后检查你是否已经达到了这个目的:

[Fact]
public void HelpMe()
{
    bool reachedThrowingPart = false;
    Assert.Throws<IndexOutOfRangeException>(() =>
    {
        var span = new Span<byte>();
        reachedThrowingPart = true;
        var ignored = span[-1];
    });
    Assert.True(reachedThrowingPart);
}

It's all significantly wordier than it would be if it weren't for ref struct restrictions, but they're understandable... 如果它不是用于reftruct限制的话,它会显得更加冗长,但它们是可以理解的......

You could implement your own Assert.Throws that passes the ref struct through a parameter to avoid capturing it in a closure. 您可以实现自己的Assert.Throws ,它通过参数传递ref struct ,以避免在闭包中捕获它。

using System;
using Xunit;

public ref struct RefStruct1
{
    public void MethodThatThrows(int x) => throw new NotImplementedException();
}

public class Test1
{
    [Theory]
    [InlineData(0)]
    [InlineData(int.MaxValue)]
    public void MethodThatThrows_Always_ThrowsNotImplementedException(int x)
    {
        var refStruct1 = new RefStruct1();

        AssertThrows<NotImplementedException>(ref refStruct1, (ref RefStruct1 rs1) => rs1.MethodThatThrows(x));
    }

    private delegate void RefStruct1Action(ref RefStruct1 rs1);

    [System.Diagnostics.DebuggerStepThrough]
    private static T AssertThrows<T>(ref RefStruct1 rs1, RefStruct1Action action)
        where T : Exception
    {
        if (action == null)
            throw new ArgumentNullException(nameof(action));

        try
        {
            action(ref rs1);
        }
        catch (Exception ex)
        {
            if (ex.GetType() == typeof(T))
                return (T)ex;

            throw new Xunit.Sdk.ThrowsException(typeof(T), ex);
        }

        throw new Xunit.Sdk.ThrowsException(typeof(T));
    }
}

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

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