使用XUnit声明异常


111

我是XUnit和Moq的新手。我有一个以字符串为参数的方法,如何使用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;
}

1
您所说的“未按预期工作”是什么意思?(另外,请更清晰地设置代码格式。使用预览,然后在看似想要阅读的外观时发布。)
Jon Skeet

4
提示:您在打电话GetSettingsForUserID("")之前先打电话Assert.Throws。该Assert.Throws呼叫不能帮助你。我建议您不要对AAA保持僵化……
Jon Skeet

Answers:


183

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
    var exception = Assert.Throws<ArgumentException>(act);
    //The thrown exception can be used for even more detailed assertions.
    Assert.Equal("expected error message here", exception.Message);
}

注意异常如何也可以用于模式详细断言


5
如果使用异步方法,Visual Studio将使用以上语法引发警告。它喜欢这样:async Task act() => await service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);
Alec

5
实际上,对于我来说,这导致了一个错误,“无法将Task隐式转换为Func <Task>”,而如果我只是这么说的Task act() => service.DoTheThingAsync(); await Assert.ThrowsAsync<InvalidOperationException>(act);话,它对此感到满意并可以正常工作。
亚历克

使用异步/等待如何影响呢?当我尝试在测试中使用ThrowsAsync进行此操作时,它永远不会到达Assert.Equal行,因为它成功引发了错误并退出了测试。测试水,看看这是否应该是一个新问题...
nathanjw

@AlecDenholm谢谢!那是唯一对我有用的东西。我认为其他一些建议对于异步内容并不能真正发挥作用。
商标

45

如果您确实想对AAA 严格态度,则可以使用xUnit中的Record.Exception在Act阶段捕获Exception。

然后,您可以在Assert阶段基于捕获的异常进行断言。

xUnits测试中可以看到一个示例。

[Fact]
public void Exception()
{
    Action testCode = () => { throw new InvalidOperationException(); };

    var ex = Record.Exception(testCode);

    Assert.NotNull(ex);
    Assert.IsType<InvalidOperationException>(ex);
}

由您决定要遵循的路径,并且xUnit提供的功能完全支持这两个路径。


1
FWIW,如果您需要验证异常消息等,此解决方案非常有用。我认为那是您可以使用Record.Exception的时候。
Jeff LaFay

@JeffLaFay我感谢我在这里参加聚会有点晚,这与使用var exception = Assert.Throws<InvalidOperationException>(testCode);和声明有exception.Message什么不同?还是实现同一件事的另一种味道?
ColinM

3

如果要坚持使用AAA,可以考虑使用以下方法:

// Act 
Task act() => handler.Handle(request);

// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.