0

I apologize ahead as I'm very new to software testing. But I have what looks like a simple code to create a White-box test cases with 100% code coverage:

01 public class ShapeAreas {
02
03 public double oneParameter(String shape, float x1)
04 {
05     float area;
06     if (shape.equals("A"))
07         return x1 * x1 * Math.XI;
08     else if (shape.equals("B"))
09         return x1 * x1;
10     else 
11         return -1.0;
12 }
13 
14 public double twoParameter(String shape, float x1, float x2)
15 {
16     float area;
17     if (shape.equals("N"))
18         return x1 * x2;
19     else if (shape.equals("M"))
20         return 0.5 * x1 * x2;
21     else
22         return -1.0;
23 }
24 }

I need help on what my input data should look like on this code to achieve 100% code coverage with the least number of test cases.

I appreciate any help I can get on this, thanks!

6
  • Any feedback on the provided answer @jaggsharry ? Is this what you was looking for?
    – Dan_Maff
    Commented Mar 7, 2019 at 5:48
  • Can you be more specific about what kind of coverage you are interested in? Statement coverage, branch coverage, MCDC, path coverage? Is this a course exercise, or is your actual goal to learn how to create high-quality test suites? Commented Mar 7, 2019 at 20:08
  • @DirkHerrmann statement coverage, my goal is to learn how to create high-quality test suites. Thanks!
    – jaggsharry
    Commented Mar 8, 2019 at 10:57
  • @Dan_Maff thanks a lot for the answer. I have a follow up question, In typical scenario, what would be an input data and expected results for this test case for either method? Thanks again!
    – jaggsharry
    Commented Mar 8, 2019 at 11:14
  • As mentioned in the answer, you will basically have to create 3 test cases for each method with one of the given shape parameter values each. The float parameters are irrelevant from a coverage point of view.
    – Dan_Maff
    Commented Mar 8, 2019 at 11:20

2 Answers 2

1

I took the freedom to add line numbers to your code, for being able to give a better explanation. You have in the comments mentioned that you are interested in statement coverage. Statements in your code examples are found in lines 07, 09 and 11 as well as in 18, 20 and 22. And, certainly, the if statements themselves are also statements (hence the name), but these will be executed anyway on each execution of the respective function.

In one execution of function oneParameter there will be exactly one of the conditional statements executed: Either in line 07, or in line 09 or in line 11. That is because of the exclusive nature of the if-else if-else statement. Similarly, in one call to function twoParameter either the statement in line 18, or in line 20 or in line 22 will be executed.

Consequently, to have all the statements covered, you have to call each function three times. The argument which controls the actual branch taken is in both cases the shape argument. This means, the value of the other arguments is not relevant with respect to which statement will be executed. A trivial set of calls could be:

oneParameter("A", 0.0);
oneParameter("B", 0.0);
oneParameter("any other", 0.0);
twoParameter("N", 0.0, 0.0);
twoParameter("M", 0.0, 0.0);
twoParameter("any other", 0.0, 0.0);

This is an example for a minimal set of calls that would achieve 100% statement coverage. What may be surprising is, that these are just calls - there is not even any evaluation of the results. When we talk about test coverage, the implicit assumption is, that the respective code lines are not just executed, but that the corresponding results are evaluated as part of the test. This could then look like follows:

assertEquals(0.0, oneParameter("A", 0.0));
assertEquals(0.0, oneParameter("B", 0.0));
assertEquals(-1.0, oneParameter("any other", 0.0));
assertEquals(0.0, twoParameter("N", 0.0, 0.0));
assertEquals(0.0, twoParameter("M", 0.0, 0.0));
assertEquals(-1.0, twoParameter("any other", 0.0, 0.0));

Despite the fact that this now has 100% statement coverage and also performs evaluation of the results, it is still far from being a high quality test suite. The reason is the following: Unit-testing is typically performed with the primary goal of finding bugs in the code. The above set of tests, however, is not well suited to find any interesting bugs. To give you some examples of possible bugs that this test suite will not find:

  • By mistake, the computations in function oneParameter for "A" and "B" were exchanged.
  • In function twoParameter for "N" the operation * was mistakenly exchanged by +.
  • In function twoParameter for "N", instead of multiplying x1 with x2, the expression multiplied x1 with x1.
  • In function twoParameter for "M" the constant was set to 0.05 instead of the correct 0.5.

These are just examples of possible bugs. Taking the goal of finding possible bugs seriously therefore requires to think beyond coverage alone. In fact, there can even be parts of code which are not suited at all for being tested with unit-testing - although this is not the case in your simple example. Examples for such code parts are unreachable code (e.g. default branch of a switch statement added for robustness), or code that only consists of interactions with other components, in which case integration testing is a better suited approach. Therefore the goal to reach a coverage of 100% does often not make sense, even if you are sincerely striving for an excellent-quality unit-test suite.

Coverage is, nevertheless, a valuable information for developers who are interested to ensure that they have addressed all relevant scenarios in their code. Sadly, coverage is by far less valuable when it comes to judge the quality of a test suite from a managerial perspective: In this context it is often reduced to a mere percentage, and developers are forced to create tests until a certain coverage percentage is reached - but often enough without giving them enough training, time and encouragement to do the testing properly. As a consequence, to reach a coverage of, say, 80%, all of the trivial code (getters and setters and the like) might get "tested" to increase coverage. The testing of the most complex and difficult-to-test 20% of the code, however, is postponed for lack of time (although this is likely to be the code where the bugs are hidden). And, even the 80% that have been covered may be tested badly as with the minimal test suite I have shown above.

0

You have to look for the branches in each of the methods to get 100% coverage. It's A, B, X as shape parameter for the oneParameter method and N, M, X for the twoParameter method.

3 test cases for each method would give you 100% coverage.

However, it will not tell you that your code is 100% correct.

Try e.g. null as shape. (Which would result in a NullpointerException)

You would also need to define the precision required for your calculations. (double return type vs. float calculations.)

public void testOneParameterWithShapeB() {
  double result = sut.oneParameter("B", 1.0);
  //TODO check the result
}
0

Not the answer you're looking for? Browse other questions tagged or ask your own question.