views:

145

answers:

7

I'm relatively new to JUnit, and I was writing a few of my first tests today. For some particular method, I wanted to pass random values (all of which are within the correct range). If the method fails for any reason, I want to know which value caused it to fail. So what's the recommended way of doing this?

(Or is it bad to use random values in JUnit tests?)

+2  A: 

You can try to use: http://www.openfuture.de/Log4Unit/ for logging, but I would recommend against random values for unit tests as they are supposed to be repeated. If you want to test a lot of values just use a for loop and some modifications to the index value, which is easily repeated.

If you think about it there is really no situation where it would be more beneficial to use random values than "hard coded" ones. If you want a good spread over a value range you can use a function or use random values with a fixed seed (to get the same numbers).

If a test fails you want to be able to fix it and run the test again. That's the problem with random numbers in unit tests.

DeletedAccount
+1. Tests should be "repeatable"... use a diverse set of values testing boundary conditions as well.. a RowTest should fit your need.
Gishu
Thank you. But testing using random values isn't that bad IMHO, as long as it is put to use correctly. After checking for boundary and some middle values, a random test can be good at revealing other special cases that I didn't think of when creating the test. ...
Hosam Aly
By logging the seed value, the error can be reproduced, and thus fixed. Otherwise, the error may slip for a very long time. (Which reminds me of the binary search implementation bug :) ). What do you think?
Hosam Aly
It sounds cumbersome. And by your logic you want to find errors my "accident". If you use a fixed random value and test 10000 values, but the same ones, each time, it will be as good and repeatable without having to look in log files.
DeletedAccount
Log files in unit tests are more meant to log things that work, if you want some additional information, as JUnit doesn't give you anything more than passed in those cases.
DeletedAccount
Why is it cubersome? Yes, I want to find errors by accident if I couldn't think of tests that would reveal them. Actually I don't intend to use a fixed random value. I've currently implemented it as using System.nanotime() for the seed, and showing it in the assert message if it fails.
Hosam Aly
Good that you saw what I meant above (it should have been fixed random seed value[but seed was missing]). I still argue that using the same 10000 values is better than 10000 random, as the fixed values are easier to repeat. I think using both in combination may be a good thing, but the fixed ones
DeletedAccount
are the most important ones by my logic. Showing info if the assertion fails is certainly the way to go. Those who use JUnit logging are looking for the ability to get more information from the tests that passes, something I've never had a need to.
DeletedAccount
Also a note on the above. If I have 10000 random values, but the same random values each time. Then it will cover lots of cases where you didn't think of those tests and got them "by accident", but it will be the same accident each time.
DeletedAccount
Random and changing values will cover more ground over time, sure. I just argue that if you have A: The test values you can think of yourself. B: Random but fixed values, and C: Random changing values. Then I think A+B will cover 99.999% of the cases and at the same time be easily repeatable.
DeletedAccount
Of course you could do A+B+C, but I don't think the C is worth the extra effort. In either case, doing just A+C is something I would never do. As I argue that A+B will cover almost everything and be easier to manage.
DeletedAccount
I think I've explained my opinion now, but it's not a science. Good look with your approach! :-)
DeletedAccount
A: 

If your unit test fails for any reason, you will see a red traffic light in your test runner. The test runner will also show you which test method failed and the test runner's log reports more detail (the dumped stack trace, for example). Investigate that error, correct it, and your test should never fail again, unless you break the code.

Thus, I don't see the necessity for logging exceptions. You should fix any error (red traffic light) immediately.

Using random values can be quite dangerous if you cannot guarantee that your generation of these values is free of errors. Checking the boundary conditions might be more useful.

olli
Thank you, but I'm not actually trying to log exceptions, as much as I am trying to create reproducible, pseudo-random test cases. Check my comment on @Kent's answer, or the link to Fuzzy Testing in @ShuggyCoUk's answer.
Hosam Aly
+1  A: 

Just report the actual and expected values in the "diagnostic message" parameter of your assertions. That's the common practice for JUnit tests, and the "helper" assert methods tend to do this by default, e.g. assertEquals will say something like "expected 6 and got 7".

Random values are OK, as long as you can ensure that the random range constitutes an equivalence class for the code under test.

Morendil
Thank you. This is what I was looking for.
Hosam Aly
+2  A: 

If you really want to use random values then simply place the value in use in the textual part of the assert methods. Then if an assert is blown the input value will be present and you can investigate why it had a problem.

This is Fuzz Testing and is a powerful technique but is most useful when you don't have the available source code or when testing a system with complex internal state and multiple interactions.

A more useful type of testing for you may be white box testing where test inputs are deliberately selected to cover the various classes of input you might get. JTest appears to be an automated tool for this in java. MS Research supplies PEX for c#).

Simply using a coverage tool and validating that you cover the relevant paths is normally sufficient if doing it by hand, though the boundary cases supplied by the automated tools are often instructive.

ShuggyCoUk
Thanks a lot. This is very insightful information.
Hosam Aly
+2  A: 

Have you tried using the 'assertThat' methods and Hamcrest Matchers that are part of JUnit 4.4+? Check out the README [1] and search for 'assertThat'.

I've become quite fond of the much more semantic look of the code and the failure messages are much more informative.

[1] http://junit.sourceforge.net/README.html

nbeyer
Thank you. This is indeed useful information. I didn't try these methods because I was using JUnit 4.3.1 (which comes bundled with Eclipse Ganymede). I'll definitely try the new version.
Hosam Aly
A: 

You can have repeatable random values by providing a constant seed to the random number generator. In Java create a java.util.Random with a fixed seen and pass to the class as a constructor parameter (dependency injection). Like this:

new ClassUnderTest(new Random(123L));

Depending on what you are testing, you might also separate the generating of the random values from their use. If you have class X which takes random values from range 1 to 10, then you can test it by passing it the edge values 1 and 10, and some value from the middle such as 4. Then you need another test for the producer of those random values. For example give it a java.util.Random with a fixed seed and generate 100 values, and check that all of them are within the allowed range.

Esko Luontola
+1  A: 

I would propose parameterized test cases. so you can use random values (in the Data method) and it's "logged" in your runner, if any method will fail.

furtelwart