断言从模拟对象构造函数抛出的异常

时间:2011-04-12 19:11:16

标签: c# unit-testing tdd nunit moq

假设:VS2010,.NET 4,C#,NUnit,Moq

我是TDD的新手,在完成一个项目时遇到了这个问题。

鉴于课程:

public abstract class MyFileType
{                
    public MyFileType(String fullPathToFile)
    {
        if (!File.Exists(fullPathToFile))
        {
            throw new FileNotFoundException();
        }

        // method continues

    }
}

我正在尝试使用以下方法测试它:

[Test]
[ExpectedException(typeof(System.IO.FileNotFoundException))]
public void MyFileType_CreationWithNonexistingPath_ExceptionThrown()
{
    String nonexistingPath = "C:\\does\\not\\exist\\file.ext";
    var mock = new Mock<MyFileType>(nonexistingPath);
}

测试失败,NUnit报告从未抛出异常。

我确实找到section in the NUnit docs谈论断言的例外情况,但这些例子看起来并不像我想做的那样。我仍然开始使用NUnit和Moq,所以我可能会以错误的方式解决这个问题。

更新:

为了帮助阐明此示例使用抽象类的原因,它是一系列文件类型的基类,其中只有加载和处理数据在子类型之间会有所不同。我最初的想法是将open / setup的逻辑放入基类中,因为它对所有类型都是相同的。

6 个答案:

答案 0 :(得分:5)

在引用mock.Object之前,不会调用构造函数。这应该会引发你期待的例外。

另一方面,让构造函数抛出除使用异常之外的异常(例如各种ArgumentException派生类)通常是不好的做法。大多数开发人员不希望'new'抛出异常,除非他们已经做了某些事情非常错误;不存在的文件是可以在程序控制之外合法发生的异常类型,因此您可能希望将其设置为静态工厂方法,而不是“FromFileName”。编辑:鉴于这是一个基类构造函数,这也不是真的适用,所以你可能想考虑哪里是进行此检查的最佳位置。毕竟,文件可能在任何时候停止存在,因此检查构造函数甚至没有意义(无论如何,您都需要检查所有相关方法。)

答案 1 :(得分:3)

如果你必须让这个类成为一个抽象的类,那么我们应该只是实现它(简单): MSDN:an abstract class

所以,同意(和alexanderb一样)这里可能不需要模拟,也可以在.Throws NUnit Assert扩展中使用Stecy,你可以在测试中创建一个调用基类的类,如下所示:

using System;
using System.IO;

namespace fileFotFoundException {
    public abstract class MyFile {

        protected MyFile(String fullPathToFile) {
            if (!File.Exists(fullPathToFile)) throw new FileNotFoundException();
        }
    }
}

namespace fileFotFoundExceptionTests {
    using fileFotFoundException;
    using NUnit.Framework;

    public class SubClass : MyFile {
        public SubClass(String fullPathToFile) : base(fullPathToFile) {
            // If we have to have it as an abstract class...
        }
    }

    [TestFixture]
    public class MyFileTests {

        [Test]
        public void MyFile_CreationWithNonexistingPath_ExceptionThrown() {
            const string nonExistingPath = "C:\\does\\not\\exist\\file.ext";

            Assert.Throws<FileNotFoundException>(() => new SubClass(nonExistingPath));
        }
    }
}

答案 2 :(得分:3)

我今天遇到了类似的问题。我使用以下解决方案解决了这个问题:

[Test]
[ExpectedException(typeof(System.IO.FileNotFoundException))]
public void MyFileType_CreationWithNonexistingPath_ExceptionThrown()
{
    String nonexistingPath = "C:\\does\\not\\exist\\file.ext";
    var mock = new Mock<MyFileType>(nonexistingPath);
    try
    {
        var target = mock.Object;
    }
    catch(TargetInvocationException e)
    {
        if (e.InnerException != null)
        {
            throw e.InnerException;
        }
        throw;
    }
}

答案 3 :(得分:2)

假设您正在使用最新版本的NUnit(您应该),则不推荐使用ExpectedException属性。

您应该使用以下内容:

var exception = Assert.Throws<FileNotFoundException> (() => new MyFileType (nonExistingPath));
Assert.That (exception, Is.Not.Null);  // Or you can check for exception text...

不需要在那里使用模拟。 事实上,模拟在你的例子中没有任何意义。

答案 4 :(得分:2)

如果您尝试测试My FileType类,如果文件不存在则抛出异常,为什么要创建mock。你的代码应该很简单

[Test]
[ExpectedException(typeof(System.IO.FileNotFoundException))]
public void MyFileType_CreationWithNonexistingPath_ExceptionThrown()
{
    // arrange
    var nonexistingPath = "C:\\does\\not\\exist\\file.ext";

    // act / assert
    var mock = new MyFileType(nonexistingPath);
}

答案 5 :(得分:0)

可以使用answer by Karol Tyl来改进Fluent Assertions

Func<MyFileType> createMyFileType = () => mock.Object;

createMyFileType.Should().Throw<TargetInvokationException>().WithInnerException<FileNotFoundException>();