Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Correct method for testing for an exception using Moq and MSTest

A little confusion as to the behaviour of Moq with MsTest.

Edit: This is not a question of "How do I test?" or "How do I assert?", this is a scratch pad to see how MoQ works so don't focus on the exception type etc.

I think a better question may be => "Does Moq Throws<> behave similar to MsTest ExpectedExceptionAttribute?" That is, they're expecting an exception in the test or the SUT?

I'd like to know just how MoQ "Throws" works when used with MsTest. Is it better to not use the MsTest expected exception attribute? Is it better to perform a try..catch within the test? I have a few more questions surrounding this.

I am Mocking a database call and when an error occurs I would like to return zero (0).

The TestMethod is straight forward with the MsTest exception attribute, and the throws exception with Moq. It only works when I throw an exception within the SaveCart method and not when I return zero.

I would like to understand the underlying behaviour because it feels as though I shouldn't, nor want to throw an exception within the SaveCart method.

Here is the Test under question:

[TestMethod]
[ExpectedException(typeof(ApplicationException))]
public void CartRepoSaveCartExceptionShouldReturnZero()
{
     _cartDatabaseMock.Setup(c => c.SaveCart(_cart))
                                   .Throws<ApplicationException>();

    var result = _cartRepository.SaveCart(_cart);

    Assert.AreEqual(result, _cartSaveExceptionValue);
}

Here is the basic SaveCart which does NOT throw an exception causing the test to fail:

public long SaveCart(Cart cart )
{
    long returnValue;

    try
    {
        returnValue = _cartDatabase.SaveCart(cart);
    }
    catch (Exception)
    {
        return 0;
    }
    return returnValue;
}

Here is a basic SaveCart where the test works because it's throwing an exception:

public long SaveCart(Cart cart )
{
    long returnValue;

    try
    {
        returnValue = _cartDatabase.SaveCart(cart);
    }
    catch (Exception)
    {
        throw new ApplicationException();
    }
    return returnValue;
}

Feel free to suggest a better title for the question if it doesn't quite explain it clearly.

like image 344
Sam Rabeeh Avatar asked Jan 07 '23 01:01

Sam Rabeeh


2 Answers

You should use ExpectedExceptionAttribute when the unit under test throws an exception.

In your first example the method didn't throw any exception therefore the test failed.

Since your method under test doesn't throw any exception you don't need to use this attribute at all...(just verify the return value in this scenario)

When you want to verify that exception was thrown and you want to verify that some additional operations occurred, use the following pattern:

[TestMethod]
[ExpectedException(typeof(<The specific exception>))]
public void FooTest()
{
    //arrange

    try
    {
       // act
    }
    catch(<the specific exception>)
    {
       // some asserts
       throw;
    }
}

The above snippet will failed if:

  1. wrong exception raised
  2. exception was not raised
  3. one of your asserts failed.

BTW, since your catch in the method is no Exception instead of ApplicationException, I offer you to change the setup to:

_cartDatabaseMock.Setup(c => c.SaveCart(_cart)).Throws<Exception>();
like image 101
Old Fox Avatar answered Jan 08 '23 14:01

Old Fox


You are right - the second test "SaveCart" works because it's throwing an exception and the the first test fail because you are turning 0. From your response to previous answers, I am sure you already know all of this. If you are asking for the behavior how it failed your first test... it goes like this:

  1. SaveCart is called
  2. It returns an exception (result of your moq setup)
  3. Your try catch caught the exception (you did this on purpose to alter the result)
  4. Your try catch returns 0 (result is now 0 as you intended to alter it)
  5. Assert checks your result against _cartSaveExceptionValue
  6. You get a fail test stating something similar to this "Message: Assert.AreEqual failed. Expected. Actual<0 (System.Int32)>."

If you want to double check this... you can try the following test

  1. comment out the [ExpectedException(typeof())]
  2. change the Assert.AreEqual(result, _cartSaveExceptionValue) to Assert.AreEqual(result, 0);
  3. the test should pass because you are comparing "result" (aka 0) to 0

I hope this answer your question.

like image 22
NKD Avatar answered Jan 08 '23 16:01

NKD