Search code examples
nunitmoqnsubstitute

How to assert that when an exception is thrown and caught within the tested method, the method returns a boolean?


I have the following interface to perform mocking:

public interface IIOManager
{
    void WriteAllText(string fullPath, string aFileContents);
    string ReadAllText(string fullPath);
}

And I am testing this static class

using UnityEngine;

public static class FileManager
{    public static bool LoadFromFile(string fullPath, out string result, IIOManager iOFile)
    {
        bool val;
        try
        {
            result = iOFile.ReadAllText(fullPath);
            val = true;
        }
        catch (System.Exception e)
        {
            Debug.LogError($"Failed to read from {fullPath} with exception {e}");
            result = string.Empty;
            val = false;
        }
        return val;
    }
}

In Moq, to test the static class, what I am trying to do would be:

using Moq;
using NUnit.Framework;

namespace Tests
{
    public class FileManagerTests
    {
        private Mock<IIOManager> iOManager;
        private string result = string.Empty;
        private readonly string fullPath = "fullPath";
        private readonly string aFileContents = "aFileContents";

        [SetUp]
        public void SetUp()
        {
            iOManager = Mock<IIOManager>();
            iOManager.Setup( (io) => io.ReadAllText(It.IsAny<string>()))
                .Returns(result);
        }

        [Test]
        public void LoadFromFile_WhenSuccessful_ReturnsTrue()
        {
            iOManager.ReadAllText(fullPath).Returns(aFileContents);
            Assert.IsTrue(FileManager.LoadFromFile(fullPath, out var result, iOManager.Object));
        }

        [Test]
        public void LoadFromFile_ExceptionIsThrown_ReturnsFalse()
        {
            iOManager.Setup( (io) => io.ReadAllText(It.IsAny<string>()))
                .Returns("").Throws<System.Exception>();
            Assert.IsFalse(FileManager.LoadFromFile(fullPath, out var result, iOManager.Object));
        }
    }
}

However, when I tried to perform the Moq equivalence, I cannot assert its return value since the exception is not being caught when testing, given what states the debug console.

using NSubstitute;
using NUnit.Framework;
using NSubstitute.ExceptionExtensions;

namespace Tests
{
    public class FileManagerTests
    {
        private IIOManager iOManager;
        private string result = string.Empty;
        private readonly string fullPath = "fullPath";
        private readonly string aFileContents = "aFileContents";

        [SetUp]
        public void SetUp()
        {
            iOManager = Substitute.For<IIOManager>();
            iOManager.ReadAllText(Arg.Any<string>()).Returns(result);
        }

        [Test]
        public void LoadFromFile_WhenSuccessful_ReturnsTrue()
        {
            iOManager.ReadAllText(fullPath).Returns(aFileContents);
            Assert.IsTrue(FileManager.LoadFromFile(fullPath, out var result, iOManager));
        }

        [Test]
        public void LoadFromFile_ExceptionIsThrown_ReturnsFalse()
        {
            iOManager.ReadAllText(fullPath).Throws<System.Exception>();
            Assert.IsFalse(FileManager.LoadFromFile(fullPath, out var result, iOManager));
        }
    }
}

I have tried searching for similar questions on StackOverflow to look for a solution. However, those were mainly of void methods and not asserting returned values.

I am working with Unity Test Framework 1.1.3.1, which comes with a Unity integration of NUnit 3.5 library.


Solution

  • The method you are testing (LoadFromFile) returns a boolean value, which is true if no exception is thrown.

    Your code already tests those two conditions.

    If you want to also test the value of the out variable, you can do that in separate Assert statements, right after the existing ones.

    The mock you are using cannot both return a value and throw, so for clarity you should drop the Returns statement in your setup.