5

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.

Sam Rabeeh
  • 119
  • 1
  • 2
  • 10
  • You may want to take a look at this: http://stackoverflow.com/questions/16053433/moq-verify-exception-was-thrown – NKD May 19 '16 at 00:09
  • I can't seem to find any Moq documentation, the quickstart has little on it https://github.com/Moq/moq4/wiki/Quickstart. And the docs only has one simple example http://www.nudoq.org/#!/Packages/Moq/Moq/IThrows/M/Throws I guess where my confusion lies is how a moq exception works.It appears the method being tested has to throw an exception for it to bubble up to the test method, and then bubble up to the MsTest attribute but this is confusing to me. – Sam Rabeeh May 19 '16 at 04:17
  • I think your title is somewhat misleading others to provide the proper way to test moq. If you want know what is going on under the hood causing your test to fail or pass. I suggest you come up with a better title. All answers posted here are totally valid pertaining to the proper way to test moq exception. Anyway, I think your question is the underlying behavior why your test pass/fail. I took a stab and posted the answer. – NKD May 19 '16 at 20:28
  • I'm sure the title may be misleading as I noted in the question. I literally don't have a clue what MoQ "throws" is doing. – Sam Rabeeh May 19 '16 at 21:36
  • 2
    So did the answer I posted answer your question or you are still confused or I misunderstood your question? Anyway, let me know. I'll try my best to help. – NKD May 19 '16 at 21:47
  • Does the test pass with the basic `SaveCart` method if you remove the [ExpectedException] attribute? – xofz May 19 '16 at 21:47
  • No, it won't. It is because the "result" is 0 and _cartSaveExceptionValue (i assume here is the ApplicationException). Commenting out the [ExpectedException] attribute is only letting the test method know what to expect. – NKD May 19 '16 at 21:49
  • @Sam Rabeeh To add to my previous comment for clarity purposes. If the test method does NOT throw an exception and you decorate your test method with ExpectException attribute, MSTest will mark the test as a fail. – NKD May 19 '16 at 22:10
  • I just commented below and upvoted two answers. They both clarified the behaviours to me so you both get a vote in my opinion. I just wish there was better docs for MoQ? And yes, I removed the MsTest attribute, and synced the exception type between methods and return the zero in the catch and voila. I still feel like there's more to understand with MoQ. Is there a definitive source of docs? I commented above my question above with a couple of links but to say they're "thin" is giving them more credit than they deserve. – Sam Rabeeh May 19 '16 at 23:02
  • Hmm. I can't make two answers the "answer" haha. So what to do.....NKD has a great point and is a correct answer. And I feel it's "more" correct than OldFox's but they BOTH answered my question in a way. – Sam Rabeeh May 19 '16 at 23:11

3 Answers3

8

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>();
Old Fox
  • 8,629
  • 4
  • 34
  • 52
  • Thanks for the answer. I'm seeing everyone keying on the exception type which isn't the question I'm trying to ask. I'm well versed in that, this is a scratch pad of code to see how MoQ works. I don't need help writing tests, assertions or catching exceptions. Just want to figure out that if I use Moq with MsTest, should I use the attribute or use the MoQ throws? – Sam Rabeeh May 19 '16 at 21:37
  • @SamRabeeh I didn't say that the exception is the question. I explained the way to: 1. combine ExcpectedException with Moq. 2. What to do when you want to simulate the scenario where your fake object throw an exception. – Old Fox May 19 '16 at 21:42
  • 3
    @SamRabeeh As I wrote use `ExpectedExceptionAttribute` when your unit under tests raise an exception. Use MoQ throws when you want to simulate the scenario where your dependency throws an exception. Combine them together when your unit under test raise an exception after your dependency throw one.... – Old Fox May 19 '16 at 21:51
  • Ah you wily old fox! I had just edited the question to explain it a little better. I wonder if I shouldn't change the title of the question to something more appropriate? – Sam Rabeeh May 19 '16 at 22:40
  • I ended up removing the MsTest attribute altogether as noted above. I upvoted your answer but I wish I could select both as an answer as they both helped. – Sam Rabeeh May 19 '16 at 23:34
2

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.

NKD
  • 1,039
  • 1
  • 13
  • 24
  • Yes sir you are spot on in your assessment. I removed the MsTest attribute, and synced the exception type between methods and voila. I still feel like there's more to understand with MoQ. Is there a definitive source of docs? I commented above my question above with a couple of links but to say they're "thin" is giving them more credit than they deserve. – Sam Rabeeh May 19 '16 at 23:00
  • I think I understand where your confusion comes from. In reality, MSTest framework does not have a "throw" assert. The attribute [ExpectedException(typeof())] is the closest thing MSTest offers comparing to an Assert.Throw() available in other frameworks like xUnit or nUnit. If you want verify a specific exception was thrown, you may want to check out nUnit/xUnit which is available out of the box. – NKD May 20 '16 at 00:11
  • Here are some options: 1) If you would like to further explore this scenario with MSTest, it can be done with an extension or writing your own try catch like others pointed out in this post (http://www.bradoncode.com/blog/2012/01/asserting-exceptions-in-mstest-with.html) 2) If you want to purely use Moq way of checking exception, you want to do something like "mock.Verify" 3) If you want to utilize both MStest and Moq, then do what you are doing... mock a throw exception and then assert the result. 4) Check out nUnit or xUnit – NKD May 20 '16 at 00:14
  • this line is moq at work: _cartDatabaseMock.Setup(c => c.SaveCart(_cart)) .Throws(); and this line is MSTest at work Assert.AreEqual(result, _cartSaveExceptionValue); – NKD May 20 '16 at 00:16
  • Everyone and their dog seems to love NUnit and most of my contracts use it. The reason i hear most often is the test case usage. Yet i prefer the datasource driven tests of mstest that can be held in a database. I also heart allot of arguments against that because of difficulty setting it up and I'm not sure why. I can set it up in minutes. – Sam Rabeeh May 20 '16 at 02:18
  • @SamRabeeh In my opinion, each of the framework has its own unique strength. Finding one fits all might be tough. Use whichever serve your needs and personal preferences. In case you haven't seen this comparison chart already: http://qadesigngurus.blogspot.it/2015/03/mstest-vs-nunit.html – NKD May 20 '16 at 17:25
  • They both win in my opinion. And you're right, whichever one works in the situation you are in then use it. I've had clients that used both on the same project to get the best of each. – Sam Rabeeh May 20 '16 at 18:09
0
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.

AD.Net
  • 13,352
  • 2
  • 28
  • 47
  • I realize the "return 0" is swallowing the exception. If this was production code it would be handled with logging or some other mechanism etc. This isn't a question of how to handle exceptions using Moq and MsTest. If I throw the exception that will bubble up to the MsTest attribute and all is fine. I think. And the generic Exception is AppicationException again is only for this exercise of learning MoQ. Not to handle it elegantly. I'm not sure where your answer is or if you just want to point out errors in other items? – Sam Rabeeh May 19 '16 at 04:22
  • @SamRabeeh, the answer is right there, you're not throwing any exception, so mstest `ExpectedException` would not work. Just as it says you are expecting an exception with that attribute, but in your code you are not throwing. Not sure if this could be anymore obvious. – AD.Net May 19 '16 at 13:35
  • Removed my comment as I updated the question details to alleviate confusion. I don't need a tutorial in exception handling or testing. Just help with MoQ. – Sam Rabeeh May 19 '16 at 21:44