简体   繁体   中英

Asserting an exception thrown from a mock object constructor

Assume: VS2010, .NET 4, C#, NUnit, Moq

I am new to TDD and came across this issue while working through a project.

Given the class:

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

        // method continues

    }
}

I am trying to test it using the method:

[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);
}

The test fails and NUnit reports an exception was never thrown.

I did find a section in the NUnit docs talking about asserting with exceptions, but the examples didn't seem like what I am trying to do. I am still getting started with NUnit and Moq so I may be going about this the wrong way.

UPDATE:

To help clarify why this example uses an abstract class, it is the base class of a series file types where only the loading and disposing of the data would differ between subclassed types. My initial thought was to put the logic for open/setup into a base class since it is the same for all types.

The constructor will not be called until you reference mock.Object. That should trigger the exception you're expecting.

On a side note, it's generally bad practice to have a constructor throw exceptions other than usage exceptions (such as the various ArgumentException derivatives.) Most developers don't expect 'new' to throw an exception unless they've done something very wrong; a file not existing is the kind of exception that can legitimately happen beyond the control of the program, so you might want to make this a static factory method instead like "FromFileName". EDIT: Given that this is a base class constructor, that's not really applicable either, so you may want to consider where is the best place to institute this check. After all, the file may cease existing at any point, so it might not even make sense to check in the constructor (you'll need to check in all relevant methods anyway.)

I faced similar problem today. I worked it out using the following solution:

[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;
    }
}

If you have to have the class an abstract one, we should then just implement it as it's meant to be (simplicity): MSDN: an abstract class

So, agreeing (with alexanderb)that a mock is probably not needed here and also with Stecy on the .Throws NUnit Assert extension, you can create a class in the test that calls the base class as follows:

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

Assuming you're using the latest version of NUnit (you should) then the ExpectedException attribute has been deprecated.

You should instead use the following:

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

No need to use a mock there. In fact, the mock does nothing interesting in your example.

If you tring to test MyFileType class, that it throws an exception if file does not exist, why you are creating mock. You code should be simple

[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);
}

The answer by Karol Tyl can be improved by using Fluent Assertions :

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

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM