Correct method for testing for an exception using Moq and MSTest

六眼飞鱼酱① 提交于 2020-01-02 06:33:53

问题


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.


回答1:


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.




回答2:


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



回答3:


catch (Exception)
        {
            return 0;
        }

you are not throwing the exception, rather swallowing the exception, so why would you expect exception? It has nothing to do with MOQ. Your test and code are not in sync.

This is a bad practice btw, to swallow exception.

catch (Exception)
        {
            throw new ApplicationException();
        }

That's also a code smell. You are catching all kinds of exception and then throwing a different type.



来源:https://stackoverflow.com/questions/37311645/correct-method-for-testing-for-an-exception-using-moq-and-mstest

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!