I'm practicing unit testing on the methods of the Java Period class. The method minusDays looks like this:
public Period minusDays(long daysToSubtract) {
return (daysToSubtract == Long.MIN_VALUE ? plusDays(Long.MAX_VALUE).plusDays(1) : plusDays(-daysToSubtract));
My unit test looks like this:
#Test
public void testMinusDays ()
{
Period x = Period.of(1,1,2);
Period y = Period.of(1,1,1);
Assert.assertEquals(y, x.minusDays(1));
}
And the problem is I'm getting 50% branch coverage and don't know which parts of the if else I'm testing because I can't follow it.
First Step: If ? : is too confusing, replace it with an equal if condition:
public Period minusDays(long daysToSubtract) {
if (daysToSubtract == Long.MIN_VALUE) {
return plusDays(Long.MAX_VALUE).plusDays(1);
}
return plusDays(-daysToSubtract);
}
And now you know what you are missing. You are tesing for daysToSubtract == 1, but not the possibility daysToSubtract == Long.MIN_VALUE, in other words you are only testing one one of two cases, which makes 50%.
you have to write a test with x.minusDay(Long.MIN_VALUE) and a test with another value. after that you should have 100%
Related
Right now, I have around 107 test input cases for my interpreter, and I have my JUnit tester set up to manually handle each case independently so as to not lump them all together. That is, if I use a loop to iterate over the test files as such
for (int i = 0; i < NUM_TESTS; i++) {
String fileName = "file_" + (i + 1) + ".in";
testFile(fileName);
}
JUnit will create one giant test result for all 107 tests, meaning if one fails, the entire test fails, which I don't want. As I said, right now I have something like
#Test
public static void test001() {
testFile("file1.in");
}
#Test
public static void test002() {
testFile("file2.in");
}
While this works, I imagine there's a much better solution to get what I'm after.
You can use #ParameterizedTest with #MethodSource annotations.
For exemple :
#ParameterizedTest
#MethodSource("fileNameSource")
void test(final String fileName) {
testFile(fileName);
}
private static Stream<String> fileNameSource() {
return IntStream.range(0,NUM_TESTS).mapToObj(i -> "file_" + (i + 1) + ".in");
}
Check the documentation at https://junit.org/junit5/docs/current/user-guide/#writing-tests-parameterized-tests
For each params returned by fileNameSource(), the corresponding test will be considered as a different case.
You have to define own structure based on your need,
one way to define your input to json file in like list of values as below.
{
[
"value1",
"value2"
]
}
Read this value when you test case execute with the help of object mapper.
objectMapper.readValue(fixture("filePathName.json"),CustomInput.class);
Where CustomInput would be something like below.
public class CustomInput {
List<String> values;
}
You can keep increase & decrease your inputs in json.
I've been learning Java just for a bit, so please advise how exception throwing test should look like in this case?
I have following Gambling Machine Class. And then 2 tests for it. I do not really know what should follow the "Integer" in second method (shouldThrowWhenNumbersOutOfRange). Could you please advise as to the exact syntax?
public class GamblingMachine {
public int howManyWins(Set<Integer> userNumbers) throws InvalidNumbersException {
validateNumbers(userNumbers);
Set<Integer> computerNumbers = generateComputerNumbers();
int count = 0;
for (Integer number : userNumbers) {
if (computerNumbers.contains(number)) {
count++;
}
}
return count;
}
private void validateNumbers(Set<Integer> numbers) throws InvalidNumbersException {
if (numbers.size() != 6) {
throw new InvalidNumbersException();
}
if (numbers.stream().anyMatch(number -> number < 1 || number > 49)) { //anyMatch-function to check whether any element in list satisfy given condition
throw new InvalidNumbersException();
}
}
private Set<Integer> generateComputerNumbers() {
Set<Integer> numbers = new HashSet<>();
Random generator = new Random();
while(numbers.size() < 6) {
numbers.add(generator.nextInt(49) + 1);
}
return numbers;
}
}
private GamblingMachine machine = new GamblingMachine();
#ParameterizedTest
#NullAndEmptySource
public void shouldThrowWhenNumbersEmpty(Set<Integer> numbers) throws InvalidNumbersException {
Assertions.assertThrows(NumberFormatException.class, () -> {
Integer.parseInt(" ");
});
}
#ParameterizedTest
#CsvFileSource(resources ="/numbersOutOfRange.cvs", numLinesToSkip = 1)
public void shouldThrowWhenNumbersOutOfRange(Set<Integer> numbers) throws InvalidNumbersException {
Assertions.assertThrows(NumberFormatException.class, () -> {
Integer. //how code should look like here?
});
}
The point of a test is to, you know, test something. Your shouldThrowWhenNumbersEmpty test doesn't do that (well, it tests that Integer.parseInt(" ") throws something. It does, of course. You... don't have to test the core libraries).
In other words, your gambling machine tests need to be calling some stuff from your GamblingMachine class. The idea is to test GamblingMachine. Not to test Integer.parseInt.
It's also a bizarre test: Why in the blazes is shouldThrowWhenNumbersEmpty parameterized? I assume the point of that test is: "Ensure that the gambling machine works as designed when passing an empty set of numbers in, specifically, the part of the design that states that an InvalidNumbersException is thrown if you do that".
Which is done with something like:
#Test
public void shouldThrowWhenNumbersEmpty() {
Assertions.assertThrows(InvalidNumbersException.class, () -> {
Set<Integer> empty = Set.of();
machine.howManyWins(empty);
});
}
Parameterized tests are a fairly exotic concept. Your test setup appears to be falling into a trap: It appears to be set up that you repeat all the logic that is already in your gamblingmachine class, to then apply this logic to the incoming (parameterized) data, figure out what your gambling machine ought to be doing, and then double check its work.
That's not how you should write tests. Tests focus on a specific result. Parameterized tests can make sense, but only if the stuff you have to do for any given input is roughly the same. For example:
Good use of parameterized testing
You have a csv file containing a bunch of lines, each of which has 6 rolls + the correct answer. Your parameterized test treats each line the same: Call howManyWins using the 6 rolls as input, then check that howManyWins returns the expected value.
Bad use of parameterized testing
You have a csv file containing a bunch of lines, each of which has 6 rolls. Your parameterized test will calculate the right result for the rolls, then invoke gambling machine, and check that the gambling machine gives the same answer as what you calculated.
This is bad: You're just repeating the code. It also means your test code is itself doing more than the very basics (it's doing a bunch of business logic), thus raising the question: Who tests your test, then?
Both of your test methods seem like they should NOT be parameterized, unless that csv also contains results.
I've read in lot of places that test methods should/must be void, but no one says what is the reason for this.
I found in MethodValidator the following check without comments/javadocs.
if (each.getReturnType() != Void.TYPE) {
errors.add(new Exception("Method " + each.getName()
+ " should be void"));
}
So why it should be void?
Ask you the reverse question : why JUnit test methods would need to be not void ?
No reason : because a test method is not designed to return a result that will be exploited by a client class.
The goal of an unit test is validating some assertions. A test runner invokes the test methods and and this is the runner that interprets any assertion failure or any exception thrown during the test execution to produce the test result.
We could wonder why tests don't return the assertion result.
But it would be a bad idea as it would be very cumbersome to write unit tests :
#Test
public AssertionResult foo(){
Bar actualBar = foo.doThat(...);
if (actualBar == null){
return AssertionResult.fail("actualBar == null");
}
}
Write something like that is really readable and straight :
#Test
public void foo(){
Bar actualBar = foo.doThat(...);
Assert.assertNotNull(actualBar);
}
We could also wonder why test methods cannot be invoked by other test methods such as :
#Test
public int foo(){
Bar actualBar = foo.doThat(...);
//...
return intValue;
}
#Test
public void fooWithComputedInt(){
Bar actualBar = foo.doThat(foo());
//...
}
But this also would be not a good idea as this would couple the tests execution while unit tests execution has to be isolated from others. And it also would make the tests be executed multiple times and unit tests have to be executed as fast as possible.
So really no value to make a test method return other thing than void.
This is purely a design choice. JUnit does not know about your code, so it could not do anything if your method would return something.
So either it should discard a return value, or require you to use "void" methods. The authors chose the latter option - you could argue that this slightly better because it's not confusing the reader.
Note that non-#Test methods are free to do whatever they want - they don't have this limitation.
A single JUnit test being run under JUnit 4.11 fail the majority of the time while being run via either to module test suite (40 runs: 2 failures, 38 passes), or the class test suite (40 runs: 6 failures, 34 passes), but running the test method by itself did not produce a single failure (50 runs: 0 failures, 50 passes).
To summarize what is happening, the equals(Object MyObject) implementation returns true if the org.joda.time.DateTime corresponding to the key Stamp.START or the key Stamp.STOP is the same for the current instance as the one in instance passed to the method. Here's the code:
import org.joda.time.DateTime;
...
private final Map<Stamp, DateTime> timeStampMap;
...
#Override
public boolean equals(Object obj) {
if (this == obj) { return true; }
if (obj == null || getClass() != obj.getClass()) { return false; }
final MyObject other = (MyObject) obj;
return (Objects.equals(this.timeStampMap.get((Stamp.START)),
other.timeStampMap.get(Stamp.START))
&& Objects.equals(this.timeStampMap.get(Stamp.STOP),
this.timeStampMap.get(Stamp.STOP)));
}
...
public enum Stamp {
START,
STOP
}
And the test itself:
#Test
#Config(configuration = TestConfig.NO_CONFIG)
public void equalityTest() {
MyObject a = new MyObject(BigDecimal.TEN);
MyObject b = a;
assertThat(a.hashCode(), is(b.hashCode()));
assertTrue(a.equals(b));
b = new MyObject(BigDecimal.TEN);
// This line produces the failure
assertThat(a, is(not(b)));
}
Why would this test only fail when run under either test suite, but not when run on it's own?
Since you are using Joda time, an alternative approach might be to fix the current time to something of your choosing using DateTimeUtils.setCurrentMillisFixed(val).
For example:
#Test
#Config(configuration = TestConfig.NO_CONFIG)
public void equalityTest() {
DateTimeUtils.setCurrentMillisFixed(someValue);
MyObject a = new MyObject(BigDecimal.TEN);
MyObject b = a;
assertThat(a.hashCode(), is(b.hashCode()));
assertTrue(a.equals(b));
DateTimeUtils.setCurrentMillisFixed(someValue + someOffset);
b = new MyObject(BigDecimal.TEN);
// This line produces the failure
assertThat(a, is(not(b)));
}
I suggest making the code more testable. Instead of having the code get the date directly, you can pass in an interface named Clock:
public interface Clock {
DateTime now();
}
Then you could add Clock to the constructor:
MyObject(BigDecimal bigDecimal, Clock clock) {
timeStampMap.put(Stamp.START, clock.now());
}
For production code, you can make a helper constructor:
MyObject(BigDecimal bigDecimal) {
this(bigDecimal, new SystemClock());
}
...where SystemClock looked like this:
public class SystemClock implements Clock {
#Override
public DateTime now() {
return new DateTime();
}
}
Your tests could either mock Clock or you could create a fake clock implementation.
Over the process of trying to produce an MCVE and author the question, I discovered something interesting:
When the test is run at the method level, note the timestamp difference of 1 millisecond. The difference is never less than that:
[START: 2015-02-26T11:53:20.581-06:00, STOP: 2015-02-26T11:53:20.641-06:00, DURATION: 0.060]
[START: 2015-02-26T11:53:20.582-06:00, STOP: 2015-02-26T11:53:20.642-06:00, DURATION: 0.060]
But when I run the test ends up being run as part of the suites, this happens nearly every single time:
[START: 2015-02-26T12:25:31.183-06:00, STOP: 2015-02-26T12:25:31.243-06:00, DURATION: 0.060]
[START: 2015-02-26T12:25:31.183-06:00, STOP: 2015-02-26T12:25:31.243-06:00, DURATION: 0.060]
Zero difference. Weird right?
My best guess is that the JVM is proverbially all warmed up and has some momentum built by the time it reaches this particular test when running the test suites. So much so, that the instantiations occur so quickly as to be nearly simultaneous. The tiny amount of time that passes between the time that MyObject a is instantiated and b is assigned until b is reassigned as a new MyObject is so minute as to produce a MyObject with an identical pair of DateTimes.
Turns out, there are a few usable solutions:
The Solution I Went With:
This is really similar to Duncan's. Call DateTimeUtils.setCurrentMillisOffset(val) before reassigning MyObject b and then reset immediately afterward, since I only need the offset long enough to force a difference in the DateTimes between MyObjects a and b:
#Test
#Config(configuration = TestConfig.NO_CONFIG)
public void equalityTest() {
MyObject a = new MyObject(BigDecimal.TEN);
MyObject b = a;
assertThat(a.hashCode(), is(b.hashCode()));
assertTrue(a.equals(b));
// Force an offset
DateTimeUtils.setCurrentMillisOffset(1000);
b = new MyObject(BigDecimal.TEN);
// Clears the offset
DateTimeUtils.setCurrentMillisSystem();
assertThat(a, is(not(b)));
}
Namshubwriter's Solution (link to answer):
Easily the best solution in cases where this issue will likely be seen throughout a project and/or in actual use.
Duncan's Solution (link to answer):
Set the current time to return a fixed time by calling DateTimeUtils.setCurrentMillisFixed(val) at the beginning of the unit test, then adding an offset to that time by calling DateTimeUtils.setCurrentMillisFixed(val + someOffset) to force the difference before reassigning MyObject b. Click the link to jump right to his solution with the code.
It is worth pointing out that you'll need to call DateTimeUtils.setCurrentMillisSystem() at some point to reset the time, otherwise other tests dependent on the time could be affected.
Original Solution:
I think it is worth mentioning here that, it is my understanding this is the only solution that does not depend on the program having certain security privileges on the parent system.
Place a call to Thread.sleep() ensure that there is a time separation between the DateTime timestamps of the two MyObjects:
#Test
#Config(configuration = TestConfig.NO_CONFIG)
public void equalityTest() {
MyObject a = new MyObject(BigDecimal.TEN);
MyObject b = a;
assertThat(a.hashCode(), is(b.hashCode()));
assertTrue(a.equals(b));
try {
Thread.sleep(0, 1);
} catch (Exception e) {
e.printStackTrace();
}
b = new MyObject(BigDecimal.TEN);
// Line that was failing
assertThat(a, is(not(b)));
}
This is a simplified example. I have this enum declaration as follows:
public enum ELogLevel {
None,
Debug,
Info,
Error
}
I have this code in another class:
if ((CLog._logLevel == ELogLevel.Info) || (CLog._logLevel == ELogLevel.Debug) || (CLog._logLevel == ELogLevel.Error)) {
System.out.println(formatMessage(message));
}
My question is if there is a way to shorten the test. Ideally i would like somethign to the tune of (this is borrowed from Pascal/Delphi):
if (CLog._logLevel in [ELogLevel.Info, ELogLevel.Debug, ELogLevel.Error])
Instead of the long list of comparisons. Is there such a thing in Java, or maybe a way to achieve it? I am using a trivial example, my intention is to find out if there is a pattern so I can do these types of tests with enum value lists of many more elements.
EDIT: It looks like EnumSet is the closest thing to what I want. The Naïve way of implementing it is via something like:
if (EnumSet.of(ELogLevel.Info, ELogLevel.Debug, ELogLevel.Error).contains(CLog._logLevel))
But under benchmarking, this performs two orders of magnitude slower than the long if/then statement, I guess because the EnumSet is being instantiated every time it runs. This is a problem only for code that runs very often, and even then it's a very minor problem, since over 100M iterations we are talking about 7ms vs 450ms on my box; a very minimal amount of time either way.
What I settled on for code that runs very often is to pre-instantiate the EnumSet in a static variable, and use that instance in the loop, which cuts down the runtime back down to a much more palatable 9ms over 100M iterations.
So it looks like we have a winner! Thanks guys for your quick replies.
what you want is an enum set
http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumSet.html
put the elements you want to test for in the set, and then use the Set method contains().
import java.util.EnumSet;
public class EnumSetExample
{
enum Level { NONE, DEBUG, INFO, ERROR };
public static void main(String[] args)
{
EnumSet<Level> subset = EnumSet.of(Level.DEBUG, Level.INFO);
for(Level currentLevel : EnumSet.allOf(Level.class))
{
if (subset.contains(currentLevel))
{
System.out.println("we have " + currentLevel.toString());
}
else
{
System.out.println("we don't have " + currentLevel.toString());
}
}
}
}
There's no way to do it concisely in Java. The closest you can come is to dump the values in a set and call contains(). An EnumSet is probably most efficient in your case. You can shorted the set initialization a little using the double brace idiom, though this has the drawback of creating a new inner class each time you use it, and hence increases the memory usage slightly.
In general, logging levels are implemented as integers:
public static int LEVEL_NONE = 0;
public static int LEVEL_DEBUG = 1;
public static int LEVEL_INFO = 2;
public static int LEVEL_ERROR = 3;
and then you can test for severity using simple comparisons:
if (Clog._loglevel >= LEVEL_DEBUG) {
// log
}
You could use a list of required levels, ie:
List<ELogLevel> levels = Lists.newArrayList(ELogLevel.Info,
ELogLevel.Debug, ELogLevel.Error);
if (levels.contains(CLog._logLevel)) {
//
}