Unit testing of several levels of exceptions - where to stay?

Suppose I have 3 classes, each of which has exactly one responsible (and method). Let them also pretend that these classes have interfaces to facilitate dependency injection. Class A calls the interface for class B, and class B calls the interface for class C. If class C throws a NotAPrimeNumberException (if, say, the int parameter is not a prime number), I would expect a unit test to happen, which does what C throws a NotAPrimeNumberException exception if the passed parameter is not a prime number. So far so good.

Currently, I am convinced that unit tests provide all the documentation necessary to understand the behavior of the test method. So the aforementioned unit test will be something like MakeSureNotAPrimeNumberExceptionIsThrownIfNumberisNotPrimeTest ().

Class B knows that class C can throw a NotAPrimeNumberException. If I want the NotAPrimeNumberException bubble to exit class B, should I write a unit test for class B to somehow verify that a NotAPrimeNumberException is thrown in some cases? What about Class A? If A also allows a NotAPrimeNumberException bubble, if it also has a unit test for this?

I am worried that if I DO NOT write unit test in class B, then consumers for class B will not know that class B may raise this type of exception. However, if I write unit test, it’s a little silly that I need to get a class B call to throw a NotAPrimeNumberException exception so as NOT to throw an exception in class B. If I don’t write a unit test, what is the appropriate way, if any, to document in the API, what this exception can arise?

Bonus question - how do you do this in NUnit with Rhino-mocks? This, of course, depends on the first two questions.

+4
source share
4 answers

If classes A and B expect certain behavior from the classes they collaborate with, then you should check that this behavior occurs. If you want to avoid the huge ball of dirt you are heading for with hard calls from A to B to C, you must apply the Hollywood / DIP principle to break this dependency. A should depend on IB (interface B), and not on instance B. Then tests A can simply say that A does what it should do, including correctly executing before exceptions thrown by IB (or ignoring them), Similarly, B may depend on the C, IC interface, and B tests, may similarly ignore the details of what C may or may not do, instead focusing only on what B. does.

The integration test that connects AB to C will be able to verify that the classes are interacting correctly, and will also be the place for the exception raised in C to bubble from A if this is what you want this to happen. And if it later turns out that C simply returns null, and does not throw an exception, then your integration test will fail and you will see that a new behavior has occurred. However, I would not clutter up my individual tests with tests showing that if the situation is violated, the exception will bubble, as this is the default behavior. Instead, I can verify that any new exceptions that I raise are thrown when expected, and that any exception handling that I execute works as I expect. This is due to the fact that in unit test you only need to test your test code, and not the behavior of the co-authors.

+2
source

Here are a few thoughts:

  • Class C is intended to throw a NotAPrimeNumberException. Therefore, you should have a test that checks if an exception is thrown while waiting.
  • Regardless of whether you are testing class B or A, in this case it depends on whether they are doing something to handle the exception. If class B needs to do something when class C throws an exception, you should check it. If class B simply allows this exception to pass, then you should not check for it. Otherwise, you will also need to test the behavior of NullReferenceException, ArithmeticOverflowException, HttpException and all other types of exceptions. This is an ad nausaeam testing module and does not help.
  • There are several things you should consider about changing the behavior of class C. Any test that depends on the changed behavior must also be changed. (Since you are doing TDD, you changed the tests before you changed the behavior, right?) In addition, you will also need to change any layouts that emulate the class C exception exception behavior. There is no point checking what happens when the class C issues an exception if it never throws that exception. This last consideration is somewhat problematic, and I do not know how it can be formally managed. I'm interested in further thoughts.
+2
source

You must use the mockery of B and C and call their methods. This is the UNIT test point - you only need to check the code unit, not the dependencies.

Otherwise, if you do not want to use the mockery in this case, I think that there should not be special conditions allowing it to pass if the exception is thrown in one of the dependencies. This should fail. The first reason: the exact code you are testing does not actually work (you are testing it in general, with dependencies). Second reason: your tests get messy this way.

Remember to keep it simple.

+1
source

If classes A and B really do not need to care about whether there is an exception, then you should not unit test this functionality. If they have any specific behavior in this exception (maybe they transfer it to another exception or catch it and complain loudly about audio signals), and you need to check this behavior, then you need to.

In the first case, I would decide not to unit test A and B to avoid the fragility of my tests. If it is not important that you test the behavior of A and B before a specific exception, then you do not want to associate these tests with the behavior of another module.

0
source

Source: https://habr.com/ru/post/1336177/


All Articles