76

I have a java class full of void methods, and I want to make some unit test to get maximal code coverage.

For example I have this method :

protected static void checkifValidElements(int arg1,  int arg2) {
    method1(arg1);
    method2(arg1);
    method3(arg1, arg2);
    method4(arg1, arg2);
    method5(arg1);
    method6(arg2);
    method7();
}

Its poorly named for a reason because I translated the code for better understanding. Each methods verify if the arguments are valid in some way and are well written.

Example :

private static void method1(arg1) {
    if (arg1.indexOf("$") == -1) {

        //Add an error message 
        ErrorFile.errorMessages.add("There is a dollar sign in the specified parameter");
    }
}

My unit test are covering the small methods fine because I ask them to check if the ErrorFile contains the error message, but I dont see how I can test my method checkIfValidElements, it returns nothing or change nothing. When I run code coverage with Maven, it tells me that the unit test doesent cover this part of my class.

The only way I see is to change this method to return an int or bollean value, like this :

protected static int checkifValidElements(int arg1,  int arg2) {
    method1(arg1);
    method2(arg1);
    method3(arg1, arg2);
    method4(arg1, arg2);
    method5(arg1);
    method6(arg2);
    method7();
    return 0;
}

With this method I am able to do an assert equals, but it seems to me that it is futile to do this. The problem is that I have a couple of class that are designed like this and its lowering my unit test coverage %.

Michael Schmidt
  • 9,090
  • 13
  • 56
  • 80
metraon
  • 1,229
  • 2
  • 11
  • 26
  • I don't understand how return type of a method affects coverage. Asserts are irrelevant to coverage too. The only fact matters - do you run a method during a unit test or not. – kan Apr 16 '13 at 18:02

7 Answers7

92

I want to make some unit test to get maximal code coverage

Code coverage should never be the goal of writing unit tests. You should write unit tests to prove that your code is correct, or help you design it better, or help someone else understand what the code is meant to do.

but I dont see how I can test my method checkIfValidElements, it returns nothing or change nothing.

Well you should probably give a few tests, which between them check that all 7 methods are called appropriately - both with an invalid argument and with a valid argument, checking the results of ErrorFile each time.

For example, suppose someone removed the call to:

method4(arg1, arg2);

... or accidentally changed the argument order:

method4(arg2, arg1);

How would you notice those problems? Go from that, and design tests to prove it.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • 1
    Thanks ! I am still new into unit testing, and I think too much on a linear/complex way. I will simplify the tests and vary thoses test to get more "coverage" or possible cases to tests. – metraon Apr 18 '13 at 01:19
  • "Code coverage should never be the goal of writing unit tests" Could you explain a bit more deatiled? – Gobliins Jan 16 '15 at 09:46
  • 19
    @Gobliins: Code coverage is a number. If the number goes up without you actually having any more faith in your code's reliability, what's the point? The goal should be improving the quality of the code (both design and implementation). Coverage is one *possible* indicator of this, but it's entirely possible to have good tests for critical and complex aspects of your code and still have relatively low coverage - or have high coverage but poor tests. – Jon Skeet Jan 16 '15 at 09:48
  • 1
    I find this statement very interesting but somehow i am curious about it. From my understanding, Code Coverage actually "is" a way to proove the realiability of your code. I've never looked at unit testing as a way to redesign or refactor your code. Can you provide me with some links where i can read a bit about this point. – Gobliins Jan 16 '15 at 09:57
  • 5
    @Gobliins: I don't have any links to hand. But just because a line is covered doesn't mean that you've actually covered all the corner cases. Consider a simple comparison reverser which just returns `-original.compare(x, y)`. You can cover that completely with a single test - but that doesn't mean the code is correct, and indeed it isn't if the original comparison returns `Integer.MIN_VALUE`. – Jon Skeet Jan 16 '15 at 09:59
  • Nowadays it's considered better to create the unit test before implementing the method. So when I have a void method that I just know what is supposed to do but haven't implemented it yet, how can I write a test? I mean I'm yet to know the methods that this method will call and the order of the parameters. In order to know that I need to write the implementation before I write the test. It's a kind of paradox, isn't it? – Alon Jun 01 '18 at 23:26
  • 2
    @Alon "Nowadays it's considered better to create the unit test before implementing the method". What you're talking about is Test Driven Development (TTD). It's one of the many possible coding approaches. You have many others as well. It's not "nowadays" :). It's a matter of which approach is better for your software in particular. – Luís Henriques Jun 04 '18 at 09:36
  • "You should write unit tests to prove that your code is correct", that is not so. Tests can really only identify problems, the only way to prove code is correct is through exhaustive testing, which is infeasible. – Anthony O Oct 31 '19 at 04:35
  • Unit tests with 100% coverage but without assertions don't guarantee nothing is broken :D – havryliuk May 24 '22 at 08:16
  • 1
    @havryliuk: I don't think anything in my answer suggested it did... – Jon Skeet May 24 '22 at 08:19
35

If your method has no side effects, and doesn't return anything, then it's not doing anything.

If your method does some computation and returns the result of that computation, you can obviously enough assert that the result returned is correct.

If your code doesn't return anything but does have side effects, you can call the code and then assert that the correct side effects have happened. What the side effects are will determine how you do the checks.

In your example, you are calling static methods from your non-returning functions, which makes it tricky unless you can inspect that the result of all those static methods are correct. A better way - from a testing point of view - is to inject actual objects in that you call methods on. You can then use something like EasyMock or Mockito to create a Mock Object in your unit test, and inject the mock object into the class. The Mock Object then lets you assert that the correct functions were called, with the correct values and in the correct order.

For example:

private ErrorFile errorFile;

public void setErrorFile(ErrorFile errorFile) {
    this.errorFile = errorFile;
}

private void method1(arg1) {
    if (arg1.indexOf("$") == -1) {

        //Add an error message 
        errorFile.addErrorMessage("There is a dollar sign in the specified parameter");
    }
}

Then in your test you can write:

public void testMethod1() {
    ErrorFile errorFile = EasyMock.createMock(ErrorFile.class);
    errorFile.addErrorMessage("There is a dollar sign in the specified parameter");
    EasyMock.expectLastCall(errorFile);
    EasyMock.replay(errorFile);

    ClassToTest classToTest = new ClassToTest();
    classToTest.setErrorFile(errorFile);
    classToTest.method1("a$b");

    EasyMock.verify(errorFile); // This will fail the test if the required addErrorMessage call didn't happen
}
Graham
  • 4,095
  • 4
  • 29
  • 37
  • I will dig into this. Thanks – metraon Apr 18 '13 at 01:17
  • 4
    <> That's not true, what if your method just modifies state of object ? – magulla Dec 17 '14 at 21:54
  • 14
    That's a side effect, it's modifying something – tddmonkey Apr 30 '15 at 13:33
  • 1
    @MrWiggles: what if a method sends an email and does not return anything, any idea what should a junit test should check for? – Abhishek Singh Jun 12 '15 at 09:42
  • 3
    How is it sending the email? You would either need to provide it with a mocked class that does the emailing, or a fake mail server to handle the email connections, and then check that it works correctly in terms of those things. Sending an email is a side effect again, and this side effect can be monitored. – Graham Jun 13 '15 at 10:49
  • 4
    Usually no one will implement all the logic to send an email these days. It's just a call to a component that has the logic to send the email. Thus, the unit test should just test that method is being called with the correct parameters. The component that actually sends the email should have it's own unit tests. – L. Holanda Oct 09 '15 at 00:09
5

You can still unit test a void method by asserting that it had the appropriate side effect. In your method1 example, your unit test might look something like:

public void checkIfValidElementsWithDollarSign() {
    checkIfValidElement("$",19);
    assert ErrorFile.errorMessages.contains("There is a dollar sign in the specified parameter");
}
Jeff Storey
  • 56,312
  • 72
  • 233
  • 406
5

You can learn something called "mocking". You can use this, for example, to check if: - a function was called - a function was called x times - a function was called at least x times - a function was called with a specific set of parameters. In your case, for example, you can use mocking to check that method3 was called once with whatever you pass as arg1 and arg2.

Have a look at these: https://code.google.com/p/mockito/ https://code.google.com/p/powermock/

Serban Stoenescu
  • 3,136
  • 3
  • 22
  • 41
1

I think you should avoid writing side-effecting method. Return true or false from your method and you can check these methods in unit tests.

Garbage
  • 1,490
  • 11
  • 23
0

If your method is void and you want to check for an exception, you could use expected: https://weblogs.java.net/blog/johnsmart/archive/2009/09/27/testing-exceptions-junit-47

Andreas Hartmann
  • 1,825
  • 4
  • 23
  • 36
0

If it is possible in your case, you could make your methods method1(arg1) ... method7() protected instead of private so they could be accesible from test class within the same package. Then you can simply test all theese methods separately.

martlin
  • 158
  • 2
  • 10