Do you use constants from the implementation in your test cases?

Unit TestingConstants

Unit Testing Problem Overview


Let's say you have some code like this (in a made-up langague, since it does not matter for this question):

constant float PI = 3.14;
float getPi() 
{ 
   return PI;
}

Would you test it like this:

testPiIs3point14()
{
   // Test using literal in test case
   AssertEquals( getPi(), 3.14 );
}

Or like this:

testPiIs3Point14()
{
   // Test using constant from implementation in test case
   AssertEquals( getPi(), PI );
}

In other words, do you use constants from your system under test in your test cases? Or is this considered an implementation detail?

Unit Testing Solutions


Solution 1 - Unit Testing

The two tests verify/assert different purposes.

  • The first one (using literal in the test case)
    Ensures that getPi() will always return 3.14.
    It covers both the constant and the function and will fail if ever someone finds the PI value used in the software is not accurate enough and replace it with, say 3.14159.
    This can be good or bad, depending on the context.

  • The second one (reuse the implementation code)
    only covers the function.
    It will not fail if someone changes the constant;
    It will only fail if the function is modified to return another constant (with a different value).

Choosing between the two depends on the objective of the test.

  • Use a literal if the constant must never change.
  • Use the constant to pin down the behavior of the function: return a constant - whatever its value. In the second case, the test may be needless.

Solution 2 - Unit Testing

I was looking for information on this same topic. My conclusion so far is that you should not use literals, but you should also make sure the constant is what you expect it to be.

If you used literals in 10 different unit tests and the value changed for any reason, then you'd have to change the value in all 10 literals. You could or example need to add more precision to PI.

The best alternative IMHO is to implement a unit test to check the constant's value to be what you expect, and then use the constant freely in your other tests.

Something along the lines of implementing these two tests:

testPiIs3point14()
{
   AssertEquals( PI, 3.14 );
}

testGetPiReturnsPi()
{
   AssertEquals( getPi(), PI );
}

PS: While checking the value may not be so important for all constants, it could be very important for some. One example I can think of is constants that contain URLs or similar values.

Solution 3 - Unit Testing

I think this is the question about coupling between the tests and the production code. When I first started TDD I thought that having the constant in the tests makes the tests more thorough. However, now I think it just causes tighter coupling between the tests and the implementation. Does it make it more safe if you copy and paste the constant to the test? Not really. It just makes it more painful to change the constant, especially if its copy-pasted to multiple tests. These tests don't test if it is the right constant, they just test that this constant is returned from the method, so now I would definitely go for test number two.

Solution 4 - Unit Testing

If you really have both exposed as part of the public interface, it would be two tests:

testPI() {
   AssertEquals(PI, 3.14);
}

test_getPi() {
   AssertEquals(getPi(), 3.14);
}

However, if PI is an implementation detail, and getPi is the public means to retrieve the value, then you would only write test_getPi, as above, and PI should be private with no testPI unit test. This latter case is probably more like what you should do.

I guess you could have a third test like this:

test_getPi_PI_AlwaysAgree() {
   AssertEquals(getPi(), PI);
}

This says that these two pieces of code should always evaluate to the same value, whatever that value is. If that's how your logic is supposed to work, that's how you'd test it, but more likely you shouldn't be having two identical means of doing the same thing in the first place.

Note that this last test does not say that getPi is expected to return 3.14, only that the two pieces of code should have the same value, without any indication of what that value is. To assert that the value of one or the other should be some specific value, use one of the first two tests.

Solution 5 - Unit Testing

I think there are two distinct cases here:

  1. If you are testing a constant that is critical to the result of a calculation, as in your example, I think it's better to use an independent test rather than re-using the same constant from the code you are trying to test. Instead of testing the constant value directly, I would test (for example) the function CalculateAreaOfCircle(), which would verify that the Area formula is correct and at the same time verify the value of PI.

  2. I think it makes sense to re-use enumerations and other constants that do not directly affect the outcome of critical parts of the code.

Solution 6 - Unit Testing

I use the first form - even though it duplicates the value (twice only), it is more readable.

[Test]
public void GetPIReturns3_14()
{
  Assert.AreEqual(3.14, testSubject.GetPI());
}

The duplication is necessary because if you reuse the constant as in the second test, you are not really testing anything. In effect, you're testing "Is Constant == Constant?". This test would never fail. e.g. if I change PI to be 1010.1010, the second test wouldn't fail.

Solution 7 - Unit Testing

Definitely the second form, the purpose of the constant is to not introduce a "magic number". Now, in unit tests, you tend to use magic numbers quite a bit and that's OK.

In this case, you introduced a constant AND used a magic number. I would either use the constant everywhere or not use it at all.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionWW.View Question on Stackoverflow
Solution 1 - Unit TestingphilantView Answer on Stackoverflow
Solution 2 - Unit TestingdiegoreymendezView Answer on Stackoverflow
Solution 3 - Unit TestingGrzenioView Answer on Stackoverflow
Solution 4 - Unit TestingDave CousineauView Answer on Stackoverflow
Solution 5 - Unit TestingMrUpsideDownView Answer on Stackoverflow
Solution 6 - Unit TestingGishuView Answer on Stackoverflow
Solution 7 - Unit TestingIgor ZevakaView Answer on Stackoverflow