4

I'm learning to write unit tests, and have started with an easy "Calculator"-class that I wanted to test. I figured out how to use the EXPECT/ASSERT functions, and what test cases etc. are, but I got a problem when I wanted to test the division by zero. Is there any possibility to test it? I mean, what should I write as test result? Is there anything like "ERROR"? Or do I have to use exceptions?

These are my tests so far:

TEST(TestCalc, TestPos) 
{
    Calc calculate;
    EXPECT_EQ(10.0, calculate.add(5.0, 5.0));
    EXPECT_EQ(9, calculate.mul(3, 3));
    EXPECT_EQ(9, calculate.div(27, 3));
    EXPECT_EQ(9, calculate.sub(12, 3));
}
TEST(TestCalc, TestNeg)
{
    Calc calculate;
    EXPECT_EQ(-1.0, calculate.add(5.0, -6.0));
    EXPECT_EQ(-9, calculate.mul(3, -3));
    EXPECT_EQ(-9, calculate.div(27, -3));
    EXPECT_EQ(15, calculate.sub(12, -3));
}

TEST(TestCalc, TestZero)
{
    Calc calculate;
    EXPECT_EQ(10.0, calculate.add(5.0, 0));
    EXPECT_EQ(9, calculate.mul(3, 0));
    EXPECT_EQ(, calculate.div(27,0));
    EXPECT_EQ(12, calculate.sub(12,0));
}
Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
tinkerbell
  • 229
  • 1
  • 2
  • 9
  • It depends what `div` does. If it does an actual arithmetic division by zero you already have undefined behavior and cannot in any way meaningfully and safely detect it after the fact. – François Andrieux Sep 19 '18 at 12:43
  • 8
    The obvious question is what does your `Calc` class do when it sees a division by zero? What ever it does is what you should test for. A sensible thing to do would be to make your `Calc` class throw an exception, and then make your unit test check that the expected exception has been thrown. Use the EXPECT_THROW macro for that. – john Sep 19 '18 at 12:43
  • at the moment my calc class doesn't have any handling for a division by zero, because I thought I could test it. @john but your solution seems very plausible, thank you! – tinkerbell Sep 19 '18 at 12:49
  • Or ASSERT_DEATH if you expect and intend it to crash your program :-) – Gem Taylor Sep 19 '18 at 13:18
  • If you want your code to detect a divide by zero and `assert`, then Google has an EXPECT_FATAL_FAILURE macro to verify that the assert fires as expected. – Tim Randall Sep 19 '18 at 13:31

3 Answers3

2

I don't agree with @Ketzu. You have an expectation how calculator should behave, when dividing by zero.

EXPECT_EQ(, calculate.div(27,0));

This expectation is perhaps not well formulated in this test.

If calculate.div(27,0) throws an exception, than you can catch this exception and your test fails, if it is not thrown. You can write something like this

TEST(ExceptionTest, ExpectThrowsSpecificException) {
    try {
        calculate.div(27,0);
        FAIL() << "calculate.div(27,0) should throw an error, since a     division by zero is not valid\n";
    } catch (TestException& exception) {
        EXPECT_THAT(std::string(exception.what()), Eq("VALID_SETTING"));
        EXPECT_THAT(exception.errorCode, Eq(20));
    }
}

See here for a detailed discussion.

If no exception is thrown, how do you detect the abnormal usage of calculate.div?

schorsch312
  • 5,553
  • 5
  • 28
  • 57
1

Is there any possibility to test it? I mean, what should I write as test result? Is there anything like "ERROR"? Or do I have to use exceptions?

This is a core question you need to ask (and answer) yourself - how should your calculator (in this case the function Calc::div()) behave if an invalid input is given. There are several ways it can behave:

  1. Crash. This (usually) is the behavior you get if you divide by zero in C++ (technically the behaviour is undefined, so the compiler may do ANYTHING. Fortunately (for us) most compilers agree that terminating the entire process is the "correct" ANYTHING to do here)
  2. Return some value. You could return (in the case of division by zero) infinity. Or possibly NaN. Or any other value that makes sense in your context. This approach however mixes the result with error handling, which is discouraged nowadays (as it would force you to check each and every invokation of the function for "error results" - and if you forget a single check you get nasty bugs as you continue operations with invalid/bogus values.)
  3. Throw an exception. You can throw an exception, signalling that something went wrong. This is the usually used method nowadays (except for extremly performance sensitve stuff, where every microsecond counts), as it neatly separates the normal path (return result value) from the error path (exception) (and if you forget to handle the exception you will notice at once instead of using bad value like in option 2)

Once you have decided on what your behaviour should be you can test it.

For Option 1 gtest provides death tests.
For Option 2 you can simply validate that you get your expected result.
For Option 3 you can catch and evalute the exception, either via exception assertions or a homemade try { } catch { } with a FAIL() at the end of the try block (so you notice if the function fails to throw an exception when you expect it to)

CharonX
  • 2,130
  • 11
  • 33
0

Your comment

I thought I could test it

suggest a misunderstanding about tests.

Your tests only test if the behaviour of your classes is the way you want it to be. Tests are in no way error handling mechanisms!

If one of your functions is creating an application crash with certain parameters, that is not something your tests should mark as correct.

For how to solve it (exception and suitable macro) see john's comment.

Ketzu
  • 660
  • 1
  • 7
  • 12