如何使用 xUnit 测试 ref struct 方法是否抛出异常?
How to test whether a ref struct method is throwing an exception using xUnit?
我是 xUnit 的新手,但据我所知,检查是否抛出异常的标准方法是使用 Assert.Throws<T>
或 Assert.ThrowsAny<T>
方法。
但是这些方法需要一个 Action 作为参数;并且 ref 结构在 lambdas 中不能是 "embedded"。
那么,如何测试 ref 结构的给定方法是否正在抛出?
不有效的代码示例:
[Fact]
public void HelpMe() {
var pls = new Span<byte>();
Assert.ThrowsAny<Exception>(() => {
plsExplode = pls[-1];
});
}
无法在 lambda 表达式中捕获 引用结构,但您仍然可以在 lambda 表达式中使用它 - 您只需要在那里声明变量,这样它就永远不会是非引用结构中的字段。
例如编译成功:
[Fact]
public void HelpMe()
{
Assert.ThrowsAny<Exception>(() => {
var pls = new Span<byte>();
var plsExplode = pls[-1];
});
}
现在我将第一个承认这并不理想:您真的希望在操作中完成尽可能少的工作,这样您只有在预期的代码片段失败时才通过。
使用 Assert.Throws
会有所帮助,因此只有预期的异常会导致通过。此外,您可以捕获一个 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);
}
如果没有 ref struct 限制,这一切都比它要冗长得多,但它们是可以理解的...
您可以实现自己的 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));
}
}
我是 xUnit 的新手,但据我所知,检查是否抛出异常的标准方法是使用 Assert.Throws<T>
或 Assert.ThrowsAny<T>
方法。
但是这些方法需要一个 Action 作为参数;并且 ref 结构在 lambdas 中不能是 "embedded"。
那么,如何测试 ref 结构的给定方法是否正在抛出? 不有效的代码示例:
[Fact]
public void HelpMe() {
var pls = new Span<byte>();
Assert.ThrowsAny<Exception>(() => {
plsExplode = pls[-1];
});
}
无法在 lambda 表达式中捕获 引用结构,但您仍然可以在 lambda 表达式中使用它 - 您只需要在那里声明变量,这样它就永远不会是非引用结构中的字段。
例如编译成功:
[Fact]
public void HelpMe()
{
Assert.ThrowsAny<Exception>(() => {
var pls = new Span<byte>();
var plsExplode = pls[-1];
});
}
现在我将第一个承认这并不理想:您真的希望在操作中完成尽可能少的工作,这样您只有在预期的代码片段失败时才通过。
使用 Assert.Throws
会有所帮助,因此只有预期的异常会导致通过。此外,您可以捕获一个 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);
}
如果没有 ref struct 限制,这一切都比它要冗长得多,但它们是可以理解的...
您可以实现自己的 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));
}
}