Reputation: 15
I would like to ask for a help and suggestions what is a correct approach in my case (probably its easy but I'm just starting with JUnit). Here is a part of my code
public boolean arrayListEmpty()
{
if(numericalSequence.isEmpty() == true)
return true;
else
return false;
}
This is a public method from model which I suppose i should test, it's just returning true if my numericalsequence is empty as you can see. I cant check it directly invoking numericalSequence.isEmpty (in controller where I need it) because it is private. So obvious thing is to check
assertEquals(true, test.arrayListEmpty());
So my question is about suggestions what other asserts should I use/what cases should I predict which can come out. Should I in Test method fill numericalSequence with some values and assert it also? (for me its not necessary because any value inserted into sequence = not null but maybe it is not so easy)
Upvotes: 1
Views: 73
Reputation: 6645
First of all, welcome to Stack Overflow!
Looking at your question, it sounds like you're new to unit-testing (correct me if I'm wrong). So, I'll break my answers in to two sections; (1) answering your question, and (2) to give a general direction of how to write good tests and testable classes.
1. Answering your question
There are a couple more use cases you can think of here:
numericalSequence
is null
?numericalSequence
has 1 element?numericalSequence
has a null element?Some of the cases above may not be possible depending on how your class is set up, but it's a test worth having so that any changes to the class that violates the "previously agreed behavior" of one of these test cases would fail.
2. Writing good tests
There are no strict guidelines on what to do in order to write good tests, however, if you structure your code to be testable, you'll find that writing good tests becomes easier, and would be less of chore. Allow me to explain.
NOTE: This is not a comprehensive guide, but is meant to start your journey in to the path of how to write better code, and better tests
So assume a class that needs to be tested is written like this:
class MyClass {
// NOTE: This is not `final`
private List<Integer> numericalSequence;
public MyClass() {
this.numericalSequence = new ArrayList<>();
}
public void doSomething(Integer x) {
if (x < 0) {
numericalSequence = new ArrayList<>();
} else {
numericalSequence.add(2 * x);
}
}
public boolean arrayListEmpty() {
// NOTE: Your sample code can be reduced to this one-liner
return numericalSequence.isEmpty();
}
}
Here are some of the flaws in the above code:
doSomething
method allows nullable (boxed integer) values and so can cause NullPointerException
in the first if
statement.numericalSequence
member is not final and hence the code that sets it to null
is valid inside the doSomething
methodarrayListIsEmpty
method does not check if numericalSequence
is nullarrayListIsEmpty
behaves when numericalSequence
is null, or has null elements, it is hard to do so, unless you know how to get the class to that state.If the class was re-written to the following:
class MyClass {
private final List<Integer> numericalSequence;
public MyClass() {
this(new ArrayList<>());
}
// Should be made public only if the classes that use this one needs to
// initialize this instance with the sequence. Make this package-private
// otherwise.
public MyClass(List<Integer> sequence) {
this.numericalSequence = sequence;
}
public void doSomething(int x) {
if (x < 0) {
// COMPILE ERROR: Cannot assign to a final member
// numericalSequence = new ArrayList<>();
numericalSequence.clear();
} else {
numericalSequence.add(2 * x);
}
}
public boolean arrayListEmpty() {
return numericalSequence == null || numericalSequence.isEmpty();
}
}
A few things to note about the above structure:
doSomething()
method doesn't accept Integer
value, but int
value that makes sure that x
is never null
(Java numerical primitive types cannot be null
). This also means, numericalSequence
cannot contain null values through doSomething()
.numericalSequence
can be initialized from outside of this class, the arrayListEmpty()
method makes sure to check that numericalSequence
is either null
is truly empty.Now you can write test cases like so:
@Test
public void arrayListEmpty_WhenListIsNull() {
MyClass test = MyClass(null);
assertTrue(test.arrayListEmpty());
}
@Test
public void arrayListEmpty_WhenListIsEmpty() {
MyClass test = MyClass();
assertTrue(test.arrayListEmpty());
}
@Test
public void arrayListEmpty_WhenListHasOnlyOneNonNullElement() {
List<Integer> sequence = new ArrayList<>();
sequence.add(0);
MyClass test = new MyClass(sequence);
assertFalse(test.arrayListEmpty());
}
@Test
public void arrayListEmpty_WhenListHasOnlyOneNullElement() {
List<Integer> sequence = new ArrayList<>();
sequence.add(null);
MyClass test = new MyClass(sequence);
assertFalse(test.arrayListEmpty());
}
Since doSomething()
adds/clears the sequence, when writing tests for doSomething()
make sure the call and verify the state of the class by checking the return value of arrayListEmpty()
.
For example:
@Test
public void doSomething_WhenInputLessThanZero() {
List<Integer> sequence = new ArrayList<>();
sequence.add(0);
MyClass test = new MyClass(sequence);
test.doSomething(-1);
assertTrue(test.arrayListEmpty());
}
My intention was to show a couple of things:
Hope this helps.
Upvotes: 2