使用 XUnit 断言异常
Assert an Exception using XUnit
我是 XUnit 和 Moq 的新手。我有一个方法将字符串作为 argument.How 来处理使用 XUnit 的异常。
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException() {
//arrange
ProfileRepository profiles = new ProfileRepository();
//act
var result = profiles.GetSettingsForUserID("");
//assert
//The below statement is not working as expected.
Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}
正在测试的方法
public IEnumerable<Setting> GetSettingsForUserID(string userid)
{
if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id Cannot be null");
var s = profiles.Where(e => e.UserID == userid).SelectMany(e => e.Settings);
return s;
}
Assert.Throws 表达式将捕获异常并断言类型。但是,您在断言表达式之外调用被测方法,因此测试用例失败。
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
//arrange
ProfileRepository profiles = new ProfileRepository();
// act & assert
Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}
如果一心想要跟随 AAA,您可以将动作提取到它自己的变量中。
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
//arrange
ProfileRepository profiles = new ProfileRepository();
//act
Action act = () => profiles.GetSettingsForUserID("");
//assert
ArgumentException exception = Assert.Throws<ArgumentException>(act);
//The thrown exception can be used for even more detailed assertions.
Assert.Equal("expected error message here", exception.Message);
}
请注意异常还可以用于更详细的断言
如果进行异步测试,Assert.ThrowsAsync 与前面给出的示例类似,只是应该等待断言,
public async Task Some_Async_Test() {
//...
//Act
Func<Task> act = () => subject.SomeMethodAsync();
//Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(act);
//...
}
如果您确实想严格遵守 AAA,那么您可以使用 xUnit 中的 Record.Exception 来捕获 Act 阶段的异常。
然后可以在Assert阶段根据捕获到的异常进行断言
可以在 xUnits tests 中看到这方面的一个例子。
[Fact]
public void Exception()
{
Action testCode = () => { throw new InvalidOperationException(); };
var ex = Record.Exception(testCode);
Assert.NotNull(ex);
Assert.IsType<InvalidOperationException>(ex);
}
这取决于您想要遵循的路径,并且 xUnit 提供的完全支持这两种路径。
如果你想坚持AAA,你可以考虑这样的事情:
// Act
Task act() => handler.Handle(request);
// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
我认为有两种方法可以处理我个人喜欢的这种情况。假设我有以下要测试的方法
public class SampleCode
{
public void GetSettingsForUserID(string userid)
{
if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id
Cannot be null");
// Some code
}
}
我可以用下面的测试用例来测试它,确保你在你的测试项目中添加了 FluentAssertions nuget。
public class SampleTest
{
private SampleCode _sut;
public SampleTest()
{
_sut = new SampleCode();
}
[Theory]
[InlineData(null)]
[InlineData(" ")]
public void TestIfValueIsNullorwhiteSpace(string userId)
{
//Act
Action act= ()=> _sut.GetSettingsForUserID(userId);
// Assert
act.Should().ThrowExactly<ArgumentException>().WithMessage("User Id Cannot be null");
}
}
但是我在这里发现了一个问题,whitespace和Null是两个不同的东西。
c# 为空白提供 ArgumentException,为空引用提供 ArgumentNullException。
所以你可以像这样重构你的代码
public void GetSettingsForUserID(string userid)
{
Guard.Against.NullOrWhiteSpace(userid, nameof(userid));
}
你的代码项目中需要 Ardalis.GuardClauses nuget 测试用例将是这样的
[Fact]
public void TestIfValueIsNull()
{
//Act
Action act = () => _sut.GetSettingsForUserID(null);
//Assert
act.Should().ThrowExactly<ArgumentNullException>().WithMessage("*userId*");
}
[Fact]
public void TestIfValueIsWhiteSpace()
{
//Act
Action act= ()=> _sut.GetSettingsForUserID(" ");
//Assert
act.Should().ThrowExactly<ArgumentException>().WithMessage("*userId*");
}
我发现使用 try catch 块比遵循复杂的协议更方便:
try
{
var output = Settings.GetResultFromIActionResult<int>(controller.CreateAllFromExternalAPI());
Assert.True(output > 0);
}
catch(InvalidOperationException e)
{
Assert.True("Country table can only be filled from ExternalAPI if table is blank"==e.Message);
}
我是 XUnit 和 Moq 的新手。我有一个方法将字符串作为 argument.How 来处理使用 XUnit 的异常。
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException() {
//arrange
ProfileRepository profiles = new ProfileRepository();
//act
var result = profiles.GetSettingsForUserID("");
//assert
//The below statement is not working as expected.
Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}
正在测试的方法
public IEnumerable<Setting> GetSettingsForUserID(string userid)
{
if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id Cannot be null");
var s = profiles.Where(e => e.UserID == userid).SelectMany(e => e.Settings);
return s;
}
Assert.Throws 表达式将捕获异常并断言类型。但是,您在断言表达式之外调用被测方法,因此测试用例失败。
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
//arrange
ProfileRepository profiles = new ProfileRepository();
// act & assert
Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}
如果一心想要跟随 AAA,您可以将动作提取到它自己的变量中。
[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
//arrange
ProfileRepository profiles = new ProfileRepository();
//act
Action act = () => profiles.GetSettingsForUserID("");
//assert
ArgumentException exception = Assert.Throws<ArgumentException>(act);
//The thrown exception can be used for even more detailed assertions.
Assert.Equal("expected error message here", exception.Message);
}
请注意异常还可以用于更详细的断言
如果进行异步测试,Assert.ThrowsAsync 与前面给出的示例类似,只是应该等待断言,
public async Task Some_Async_Test() {
//...
//Act
Func<Task> act = () => subject.SomeMethodAsync();
//Assert
var exception = await Assert.ThrowsAsync<InvalidOperationException>(act);
//...
}
如果您确实想严格遵守 AAA,那么您可以使用 xUnit 中的 Record.Exception 来捕获 Act 阶段的异常。
然后可以在Assert阶段根据捕获到的异常进行断言
可以在 xUnits tests 中看到这方面的一个例子。
[Fact]
public void Exception()
{
Action testCode = () => { throw new InvalidOperationException(); };
var ex = Record.Exception(testCode);
Assert.NotNull(ex);
Assert.IsType<InvalidOperationException>(ex);
}
这取决于您想要遵循的路径,并且 xUnit 提供的完全支持这两种路径。
如果你想坚持AAA,你可以考虑这样的事情:
// Act
Task act() => handler.Handle(request);
// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
我认为有两种方法可以处理我个人喜欢的这种情况。假设我有以下要测试的方法
public class SampleCode
{
public void GetSettingsForUserID(string userid)
{
if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id
Cannot be null");
// Some code
}
}
我可以用下面的测试用例来测试它,确保你在你的测试项目中添加了 FluentAssertions nuget。
public class SampleTest
{
private SampleCode _sut;
public SampleTest()
{
_sut = new SampleCode();
}
[Theory]
[InlineData(null)]
[InlineData(" ")]
public void TestIfValueIsNullorwhiteSpace(string userId)
{
//Act
Action act= ()=> _sut.GetSettingsForUserID(userId);
// Assert
act.Should().ThrowExactly<ArgumentException>().WithMessage("User Id Cannot be null");
}
}
但是我在这里发现了一个问题,whitespace和Null是两个不同的东西。 c# 为空白提供 ArgumentException,为空引用提供 ArgumentNullException。
所以你可以像这样重构你的代码
public void GetSettingsForUserID(string userid)
{
Guard.Against.NullOrWhiteSpace(userid, nameof(userid));
}
你的代码项目中需要 Ardalis.GuardClauses nuget 测试用例将是这样的
[Fact]
public void TestIfValueIsNull()
{
//Act
Action act = () => _sut.GetSettingsForUserID(null);
//Assert
act.Should().ThrowExactly<ArgumentNullException>().WithMessage("*userId*");
}
[Fact]
public void TestIfValueIsWhiteSpace()
{
//Act
Action act= ()=> _sut.GetSettingsForUserID(" ");
//Assert
act.Should().ThrowExactly<ArgumentException>().WithMessage("*userId*");
}
我发现使用 try catch 块比遵循复杂的协议更方便:
try
{
var output = Settings.GetResultFromIActionResult<int>(controller.CreateAllFromExternalAPI());
Assert.True(output > 0);
}
catch(InvalidOperationException e)
{
Assert.True("Country table can only be filled from ExternalAPI if table is blank"==e.Message);
}