C# 使用XUnit断言异常

C# 使用XUnit断言异常,c#,unit-testing,xunit,C#,Unit Testing,Xunit,我是XUnit和Moq的新手。我有一个将字符串作为参数的方法。如何使用XUnit处理异常 [Fact] public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException() { //arrange ProfileRepository profiles = new ProfileRepository(); //act var result =

我是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 void Profile Repository_GetSettings ForuseridWithinValidarguments_ThrowArgumentException(){
//安排
ProfileRepository profiles=新建ProfileRepository();
//表演
var result=profiles.GetSettingsForUserID(“”);
//断言
//下面的语句未按预期工作。
Assert.Throws(()=>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;
}
public IEnumerable GetSettingsForUserID(字符串用户ID)
{            
if(string.IsNullOrWhiteSpace(userid))抛出新的ArgumentException(“用户Id不能为null”);
var s=profiles.Where(e=>e.UserID==UserID)。选择many(e=>e.Settings);
返回s;
}
表达式将捕获异常并断言类型。但是,您正在assert表达式之外调用测试中的方法,因此测试用例失败

[Fact]
public void ProfileRepository_GetSettingsForUserIDWithInvalidArguments_ThrowsArgumentException()
{
    //arrange
    ProfileRepository profiles = new ProfileRepository();
    // act & assert
    Assert.Throws<ArgumentException>(() => profiles.GetSettingsForUserID(""));
}
[事实]
public void Profile Repository_GetSettings ForuseridWithinValidarguments_ThrowArgumentException()
{
//安排
ProfileRepository profiles=新建ProfileRepository();
//行动与主张
抛出与前面给出的示例类似,只是应该等待断言

public async Task Some_Async_Test() {

    //...

    //Act
    Func<Task> act = () => subject.SomeMethodAsync();

    //Assert
    var exception = await Assert.ThrowsAsync<InvalidOperationException>(act);

    //...
}
public async Task Some\u async\u Test(){
//...
//表演
Func act=()=>subject.SomeMethodAsync();
//断言
var exception=await Assert.ThrowsAsync(act);
//...
}

如果您确实希望严格遵守AAA,那么您可以使用from xUnit在Act阶段捕获异常

然后,您可以在断言阶段根据捕获的异常进行断言

这方面的一个例子可以在中看到

[事实]
公共无效例外()
{
操作testCode=()=>{抛出新的InvalidOperationException();};
var ex=记录异常(testCode);
Assert.NotNull(ex);
Assert.IsType(ex);
}

你要遵循什么样的路径,这两条路径都被XUnice所提供的完全支持。

如果你想坚持AAA:你可以考虑这样的事情:

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

// Assert
await Assert.ThrowsAsync<MyExpectedException>(act);
//Act
Task act()=>handler.Handle(请求);
//断言
等待Assert.ThrowsAsync(act);

我认为有两种方法可以处理我个人喜欢的场景。假设我有下面的方法,我想测试一下

    public class SampleCode
    {
       public void GetSettingsForUserID(string userid)
       {
          if (string.IsNullOrWhiteSpace(userid)) throw new ArgumentException("User Id 
             Cannot be null");
          // Some code 
       }
    }
我可以用下面的测试用例来测试这个,确保在测试项目中添加FluentAssertionsNuget

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

        }
    }
在这里,您的代码项目中需要Ardalis.GuardClauses nuget,testcase将是这样的

    public void GetSettingsForUserID(string userid)
    {
        Guard.Against.NullOrWhiteSpace(userid, nameof(userid));
    }
    [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*");
    }
[事实]
public void TestIfValueIsNull()
{
//表演
Action act=()=>_sut.GetSettingsForUserID(null);
//断言
act.Should().ThrowExactly().WithMessage(“*userId*”);
}
[事实]
public void TestIfValueIsWhiteSpace()
{
//表演
Action act=()=>_sut.GetSettingsForUserID(“”);
//断言
act.Should().ThrowExactly().WithMessage(“*userId*”);
}

您所说的“未按预期工作”是什么意思?(另外,请更容易地格式化您的代码。使用预览,并在它看起来像您阅读时希望的样子时发布。)提示:您正在调用
GetSettingsForUserID(“”)
在您开始调用
Assert.Throws
之前,
Assert.Throws
调用在这方面帮不了您。我建议您对AAA…FWIW的要求不要太严格,如果您需要验证异常消息等,这个解决方案非常好。我想您可以在这个时候使用Record.exception。@JeffLaFay我很感激我来晚了一点在这里,这与使用
var exception=Assert.Throws(testCode);
和断言
exception.Message
有什么不同?或者这仅仅是实现相同功能的另一种方式?有关如何使用
Record.ExceptionAsync
进行异步案例,请参阅。
    public void GetSettingsForUserID(string userid)
    {
        Guard.Against.NullOrWhiteSpace(userid, nameof(userid));
    }
    [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*");
    }