1

Here is specific case. I want to test this Assert.ThrowsException<DbException>(() => { MyMethod(0, 100); });

The problem is that exception thrown is SqlException. Why do I want to test base exception? Because I want a unified test for any of my 3 DB providers. The exception sometimes is OracleException or MySqlException. All 3 providers exceptions derive from DbException

Is there a way to test it nicely, using the Microsoft Unit Test framework?

T.S.
  • 18,195
  • 11
  • 58
  • 78
  • Unless it's recently changed and you're using an older version, it's still not possible. It will check for the specific type of the exception; not its base type. Maybe the "best" way to deal with that is to implement your own `MyDbException` in your access layer, catch it as `SqlException` and throw and assert it as `MyDbException`. – ChiefTwoPencils Aug 15 '22 at 02:35

2 Answers2

1

According to the documentation for Assert.ThrowsException this is expected. However, if you're able to use Fluent Assertions, this is in fact very easy to achieve.

Action action = () => MyMethod(0, 100);
action.Should().Throw<DbException>(); // add some more checks

// this would mimic the behavior of Assert.ThrowsException, which should fail.
action.Should().ThrowExactly<DbException>();

See also the tips on improving assertions under Tips/Exceptions.

The alternative would be to use regular try...catch like in the example below. You need to make sure that you capture all the code path' and don't accidentally succeed:

    /// <summary>
    /// Catching a base exception class just by using <see cref="Assert"/>.
    /// Do not use <see cref="ExpectedExceptionAttribute"/>, since it doesn't
    /// provide any type of validation.
    /// </summary>
    [TestMethod]
    public void CatchAderivedExceptionTheAssertWay()
    {
        try
        {
            // A test method that throws an exception.
            Action action = () => throw new ArgumentNullException("param1");
            action();
        }
        catch (ArgumentException e)
        {
            // This will catch any exception derived from ArgumentException.
            // Do some validation to ensure the right thing is caught,
            // like checking the parameter name.
            if (!e.ParamName.Equals("param1", StringComparison.Ordinal))
            {
                Assert.Fail("Reason why the validation failed.");
            }

            // Otherwise jump out of the test.
            return;
        }

        Assert.Fail("Make sure the test fails, in case the code inside try doesn't throw at all.");
    }

For completeness sake, there's also the ExpectedException attribute that can be added to a [TestMethod]. Due to the lack of validation options I would not recommend using it.

I'll add this question to my list of interview questions. Such a nice example.

  • Cool. I don't want to start including additional libraries in this case because it is some legacy code. But definitely worth researching for the new projects. Basically, your answer is - not possible. – T.S. Aug 15 '22 at 03:12
0

Thanks for all the leads, one from @StephanAdler

Considering limitations of given testing framework my sensible solution was this

Exception retEx = null;

try
{
    MyMethod(0, 100);
}
catch (Exception ex)
{
    retEx = ex;
}

Assert.IsNotNull(retEx);
Assert.IsInstanceOfType(retEx, typeof(DbException));

Assert.IsInstanceOfType works perfectly with the base type

T.S.
  • 18,195
  • 11
  • 58
  • 78