简体   繁体   中英

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.

But these methods expect an Action as parameter; and ref structs can't be "embedded" in lambdas.

So, how does one test whether a given method of a ref struct is throwing? 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.

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. Additionally, you could capture a bool which is changed just before the throwing part, then check that you'd got that far:

[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...

You could implement your own Assert.Throws that passes the ref struct through a parameter to avoid capturing it in a closure.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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