如何在抛出异常的方法中对接收到的调用进行单元测试

时间:2018-04-10 10:08:09

标签: c# unit-testing mocking nsubstitute

我正在尝试对抛出异常的方法进行单元测试,并且在抛出之前必须执行一些任务,例如日志记录。我正在使用NSubstitute而无法理解这一点。

所以我的测试看起来像这样

[TestMethod]
[ExpectedException(typeof(IOException))]
public void RecordAnalyser_FileReadFailedInFirstAttempt_WarningLogged()
{
    //Arrange
    var fileMock = Substitute.For<IFile>();
    fileMock.ReadLines(Arg.Any<string>()).Throws(new IOException());

    //Act
    var recordAnalyser = new RecordAnalyser(fileMock, logger); //--> throws exception.

    //Assert
    logger.Received(1).Warn(Arg.Any<string>(), Arg.Any<Exception>());
}

现在我想断言如果logger收到警告日志,但由于上面的行发送了一个异常,并且我有一个预期的异常属性,test不会检查断言。

我能想到的一个脏代码是将错误语句包装在测试中的try catch中,但它不是最好的。

//Act
try
{
    var recordAnalyser = new RecordAnalyser(fileMock, logger);
}
catch (Exception)
{
    // eat
}

正在测试的代码 -

public RecordAnalyser(IFile file, ILoggerService logger)
{
    this.logger = logger;
    try
    {
        names = file.ReadLines(Constants.Names).ToList();
    }
    catch (System.IO.IOException e)
    {
        logger.Error("Names file could not be read.", ex);
      // How do I test above line without a try catch block in unit test 
        throw;
    } 
}

在这里寻找建议。

2 个答案:

答案 0 :(得分:2)

这可能是XY problem

您正尝试在一次测试中测试/断言多项内容。因此问题。

如果目标仅仅是为了测试异常被抛出那么很好,没有尝试/ catch和测试会通过。

[TestMethod]
[ExpectedException(typeof(IOException))]
public void RecordAnalyser_Should_FailInFirstAttempt_When_FileRead() {
    //Arrange
    var fileMock = Substitute.For<IFile>();
    fileMock.ReadLines(Arg.Any<string>()).Throws(new IOException());

    //Act
    var recordAnalyser = new RecordAnalyser(fileMock, logger); //--> throws exception.
}

在另一个测试中,如果要在抛出异常时声明某些事情发生,那么您需要捕获异常以允许执行测试以完成测试并允许验证断言。

[TestMethod]
public void RecordAnalyser_Should_LogWarning_When_FileReadFailedInFirstAttempt() {
    //Arrange
    var fileMock = Substitute.For<IFile>();
    fileMock.ReadLines(Arg.Any<string>()).Throws(new IOException());
    IOException error = null;

    //Act
    try {
        var recordAnalyser = new RecordAnalyser(fileMock, logger); //--> throws exception.
    } catch(IOException ex) {
        error = ex; //catch and hold error for later
    }

    //Assert

    if(error == null)
        Assert.Failed("exception expected"); // error was not thrown.

    logger.Received(1).Warn(Arg.Any<string>(), Arg.Any<Exception>());
}    

答案 1 :(得分:0)

您可以使用以下Extension来提供Assert.Throws(Action)和Assert.Throws(Action)的实现:https://github.com/bbraithwaite/MSTestExtensions