Asserting an exception thrown from a mock object constructor

10,831

Solution 1

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

Solution 2

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.)

Solution 3

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

Solution 4

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.

Solution 5

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);
}
Share:
10,831

Related videos on Youtube

Noren
Author by

Noren

Updated on November 12, 2020

Comments

  • Noren
    Noren over 3 years

    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.

    • Thebigcheeze
      Thebigcheeze about 13 years
      I'm not certain this is directly related to your problem, but changing your string definition to using the @"" syntax is more readable and less error prone. For example, instead of String nonexistingPath = "C:\\does\\not\\exist\\file.ext"; you could simply put String nonexistingPath = @"C:\does\not\exist\file.ext";
  • Noren
    Noren about 13 years
    Doesn't the fact that MyFileType is abstract prohibit me from using the 'new' operator as you have shown?
  • Noren
    Noren about 13 years
    I am using the latest version of NUnit, so thank you for the heads up about the deprecation. In trying your solution, I encountered the same error as I did with alexanderb's solution -- cannot create an instance of of the abstract class 'MyFileType'.
  • Alexander Beletsky
    Alexander Beletsky about 13 years
    opps, sorry I missed that. Sure, you can't do 'new' there. But this also mean that you should not directly test this class, since it is abstract.. It has some behaviour, but this behaviour have to be tested on classes inherited from MyFileType. TDD, also could point to Desing issues. If something is hard to test, that 99% is design mistake. In you case, if class is abstract it is bad idea to put logic into constructor.
  • Noren
    Noren about 13 years
    I am accepting this post as the answer since it does answer my original question.
  • Noren
    Noren about 13 years
    I agree that my initial attempt at this problem had the code smell of a constructor doing too much work.
  • Pedro
    Pedro about 13 years
    +1 for not throwing exceptions from a ctor. FxCop has a rule that will flag such code.

Related