Mockito/PowerMock when....then not being invoked? - junit

When I execute the following test, the original object is being returned and not the mock, so the real method getLevelCriteriaForLevel(level) is being executed (I observed that with the debugger). Why is that so? I'm pretty sure that this already worked yesterday and I didn't change anything there.
I already tried
#PrepareForTest({LevelCriteria.class, LevelGenerator.class})
or used the MockitoJunitRunner as I did before, but this doesn't help either.
Here is the code (generateConcreteLevel is a private method. The expected exception is only thrown when I pass this data from the mock. Otherwise it's not thrown. The test fails because the exception is not thrown, because the test doesn't use the mock object but the real object):
public class LevelGenerator
{
public void createLevel(int level)
{
generateConcreteLevel(levelCriteria.getLevelCriteriaForLevel(level));
}
private void generateConcreteLevel(LevelCriterion levelCriterion)
{
int entryGroupCount = levelCriterion.getEntryGroupCount();
int exitGroupCount = levelCriterion.getExitGroupCount();
int exitsWhileEntries = levelCriterion.getExitsWhileEntries();
int maxGroupSize = levelCriterion.getMaxGroupSize();
List<Question> questions = levelCriterion.getQuestions();
int speed = levelCriterion.getSpeed();
Range blueItemsCount = levelCriterion.getBlueItemsCount();
Range brownItemsCount = levelCriterion.getBrownItemsCount();
Range greenItemsCount = levelCriterion.getGreenItemsCount();
Range redItemsCount = levelCriterion.getRedItemsCount();
Range whiteItemsCount = levelCriterion.getWhiteItemsCount();
Range timespanBetweenGroups = levelCriterion.getTimespanBetweenGroups();
float fractionOfCarAmountToLeave = levelCriterion.getFractionOfCarAmountToLeave();
float fractionOfMinimumItemsAmountInCarParkToStartExits = levelCriterion.getFractionOfMinimumItemsAmountInCarParkToStartExits();
checkItemsFitInEntryGroups(entryGroupCount, maxGroupSize, blueItemsCount, brownItemsCount, greenItemsCount, redItemsCount, whiteItemsCount);
}
private void checkItemsFitInEntryGroups(int entryGroupCount, int maxGroupSize, Range... ranges)
{
int totalRangeCount = 0;
for (Range range : ranges)
{
totalRangeCount += range.getMaximum();
}
if (totalRangeCount > entryGroupCount * maxGroupSize)
{
throw new UnsupportedOperationException("Error in level criterion: Not enough entry groups for Items.");
}
}
}
and the test...
#RunWith(PowerMockRunner.class)
public class LevelGeneratorTest
{
#Mock
private LevelCriteria levelCriteriaMock;
#InjectMocks
private LevelGenerator levelGenerator;
#Test(expected=UnsupportedOperationException.class)
public void tooLessPositionsInEntryGroups()
{
LevelCriterion levelCriterion = new LevelCriterion.LevelCriterionBuilder()
.withBlueItemsCount(new Range(10, 10))
.withEntryGroupCount(3)
.withExitGroupCount(3)
.withMaxGroupSize(3)
.build();
when(levelCriteriaMock.getLevelCriteriaForLevel(anyInt())).thenReturn(levelCriterion);
levelGenerator.createLevel(0);
}
}
By the way: It's not an Eclipse problem, since a Maven build produces the same error.
I know that I can execute the private method with PowerMockito's Whitebox directly what I will do after refactoring, but the question is, why when...then is not working here.

Assuming your question is:
Why is the real method getLevelCriteriaForLevel(level) executed when I expect to have the mocked implementation?
I will try to demonstrate that everything works as expected fine if you reproduce it is a simple example (switch from your current workspace if you need to).
I do not have you LevelCriterion.LevelCriterionBuilder() so I simplified your test by just instantiating a LevelCriterion (empty object). This is not relevant because in my implementation I will always return an UnsupportedOperationException.
Here is how my LevelGenerator looks like. The UnsupportedOperationException is as simple as possible LevelGenerator#generateConcreteLevel(LevelCriterion).
If implemented like this:
public class LevelGenerator
{
LevelCriteria levelCriteria;
public void createLevel(int level)
{
generateConcreteLevel(levelCriteria.getLevelCriteriaForLevel(level));
}
private void generateConcreteLevel(LevelCriterion levelCriteriaForLevel)
{
throw new UnsupportedOperationException("xxx");
}
}
The test is green, using a #RunWith(MockitoJUnitRunner.class) on top of LevelGeneratorTest.
I did not used #PrepareForTest or PowerMockRunner. I have Mockito version 1.9.5.
I am sure that I have not used the real getLevelCriteriaForLevel(level) implementation, because it looks like this:
public class LevelCriteria {
public LevelCriterion getLevelCriteriaForLevel(int level) {
throw new IllegalStateException("Unexpected call");
}
}
If the real getLevelCriteriaForLevel(int) is excuted the test is red (because an IllegalStateException is thrown).
And the test is still green...
This works with pure-Mockito (version 1.9.5) approach.
You should remove all other mocking library (PowerMock, EasyMock...)
Are you sure that your static call of when(..).thenReturn(..) is the Mockito one?
Have you tried: Mockito.when(..).thenReturn(..)
I hope it helps.
PS: I have seen that you updated the question. It is a little bit unfair for me, but I am not going to update my answer once again.
I do not need the implementation detail of generateConcreteLevel(..) to illustrate that the when(..) call is working. Read my answer again. Try it in a separate workspace and you will see by yourself.
PS-2: I am using Eclipse too... Why should it be a problem? Eclipse is a great IDE.

I figured out what's wrong. Unfortunately you don't see the real problem in this question since I simplified the classes:
My real implementation of LevelGenerator got a parameterized constructor today. That's the reason why it worked yesterday but not anymore. Obviously there are mock injection problems with Mockito when not using a standard constructor.
Result: The when...then works as excpected.

Related

JUnit what asserts should I use

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)
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:
What happens if numericalSequence is null?
What if numericalSequence has 1 element?
What if 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.
The numericalSequence member is not final and hence the code that sets it to null is valid inside the doSomething method
The arrayListIsEmpty method does not check if numericalSequence is null
If I want to test how arrayListIsEmpty 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:
There are two constructors; the default invokes the one that takes a list of integers as the sequence, so that it reuses any logic that both would need to share. There are no logic in this example, but hopefully you'll come across one soon.
The 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().
Since 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:
Structure your tests cases to be small and concise
Design your class to be easily testable
Hope this helps.

How to make a valid test using the mockito library?

The context
I have a simple method that I'm testing using the mockito library.
The problem
I have a error:
"[MockitoHint] ReceiveServiceTest.testGetFileDto (see javadoc for MockitoHint):
[MockitoHint] 1. Unused... -> at .ReceiveServiceTest.testGetFileDto(ReceiveServiceTest.java:46)
[MockitoHint] ...args ok? -> at ReceiveService.getFileDto(ReceiveService.java:28)
I dont understand way.
The code
#RunWith(MockitoJUnitRunner.class)
public class ReceiveServiceTest {
private List<File> filePaths = new ArrayList<>();
#InjectMocks
private ReceiveService receiveService;
#Mock
private FindFiles findfiles;
#Mock
private ReadByte readByte;
#Before
public void before() {
filePaths.add(new File("d://folder//test1_message_received"));
filePaths.add(new File("d://folder//test2_message_received"));
filePaths.add(new File("d://folder//test3_message_received"));
}
#Test
public void testGetFileDto() throws IOException {
// Given
byte[] resultByteArr = new byte[1028];
when(findfiles.getPathFiles()).thenReturn(filePaths);
when(readByte.readByteArrFromFile(new File("d://folder//test3_message_received"))).thenReturn(resultByteArr);
List<MessageDTO> result = receiveService.getFileDto();
//some assert
}
method
#Autowired
private FindFiles findFiles;
#Autowired
private ReadByte readByte;
public List<MessageDTO> getFileDto() throws IOException {
List<MessageDTO> fileDtos = new ArrayList<>();
for (File file : findFiles.getPathFiles()) {
fileDtos.add(new MessageDTO(Base64.getEncoder().encode(readByte.readByteArrFromFile(new File(file.getPath()))),
file.getName(), "zip", null));
}
return fileDtos;
}
I think mocks are not being initialized. Please initialize the mocks in the #Before method.
#Before
public void init() {
initMocks(this);
}
This should solve the problem I guess.
Here is solution for my problem. I added foreach loop. Now the mock works, but byte [] is different than what it should return.
// Given
byte[] mockByteArr = new byte [2048];
when(findfiles.getPathFiles()).thenReturn(filePaths);
for (File filePath : filePaths) {
when(readByte.readByteArrFromFile(new File(filePath.getPath()))).thenReturn(mockByteArr);
}
//When
List<MessageDTO> result = receiveService.getFileDto();
//Then
assertEquals(3, result.size());
assertEquals(mockByteArr, result.get(1).getContent());
Your problem is, that you create a new object in the following line:
when(readByte.readByteArrFromFile(new File("d://folder//test3_message_received"))).thenReturn(resultByteArr);
Mockito needs to know which real object is passed to the method so that it can return the appropriate thenReturn-value. So if you pass the actual reference into it, your code will work, but also only if you specify all the values which are listed. Otherwise you may get a NullPointerException.
By the way, calling new File(file.getPath()) seems redundant to me. You can just use file instead.
So with the following your code might work better:
when(readByte.readByteArrFromFile(filePaths.get(0)).thenReturn(resultByteArray);
but then you need to specify it for all entries.
Alternatively, use a Matcher instead:
when(readByte.readByteArrFromFile(ArgumentMatchers.any(File.class))).thenReturn(resultByteArr);
or specify the actual argument matching you require as matchers can be very powerful in that regard.
Previously the answer contained the following, which is still true, but not as concise as the answer above:
It's been a long time since I last used mocks (and I am even proud of it ;-)).
The message already states that one should consult the javadoc and there I found the following:
Those are hints - they not necessarily indicate real problems 100% of the time.
Nonetheless, I believe the problem is with the following statement:
when(readByte.readByteArrFromFile(new File("d://folder//test3_message_received"))).thenReturn(resultByteArr);
I think you need to specify a return for every entry in the filePaths or make the call more generic using Matchers.any() (or any other appropriate Matcher).

Manipulating a mocked Calendar object to return specific days

I'm using a Calendar object to determine whether or not to increase the workload of a system based on current day/hour values. Given that this object uses static methods, I'm using PowerMock to mock the static methods with the following annotations:
#RunWith(PowerMockRunner.class)
#PrepareForTest({ Calendar.class })
The code under test is pretty simple (though my logic needs work, I know):
public void determineDefaultMaximumScans() throws ParseException{
parseTime();
Calendar cal = Calendar.getInstance();
int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
System.out.println(cal.get(Calendar.DAY_OF_WEEK));
if(dayOfWeek == (Calendar.SATURDAY) || dayOfWeek == (Calendar.SUNDAY)){
setDefaultMax(calculateNewDefaultMax(getDefaultMax()));
System.out.println("defaultMax increased by 20%");
} else {
if(currentTime.after(afterHoursBegin) && currentTime.before(afterHoursEnd)){
System.out.println("Not afterhours. Maintaining current maximum.");
setDefaultMax(defaultMax);
System.out.println("Current Maximum number of scans: " + getDefaultMax());
}
}
}
My test case reads as follows:
#SuppressWarnings("static-access")
#Test
public void testDetermineMaximumScans() throws ParseException{
PowerMock.mockStatic(Calendar.class);
String beginningTime = "18:00";
String endingTime = "05:00";
mockAfterHoursBegin = parser.parse(beginningTime);
mockAfterHoursEnd = parser.parse(endingTime);
mockCurrentTime = parser.parse(parser.format(new Date()));
EasyMock.expect(Calendar.getInstance()).andReturn(mockCalendar);
EasyMock.expect(mockCalendar.get(Calendar.DAY_OF_WEEK)).andReturn(6);
EasyMock.replay(mocks);
offHourMaximumCalculator.determineDefaultMaximumScans();
EasyMock.verify(mocks);
}
As of now, all of my attempts to return a specific value result in the following assertion error. Now I vaguely understand why it's returning the default but I do not see why I can't force the value or how to get around this expectation. Mocks in general are still a frustrating mystery to me. What am I missing?
java.lang.AssertionError:
Expectation failure on verify:
Calendar.get(7): expected: 1, actual: 0
Mocks are fairly simple. But wanting to mock static methods is a big running after complexity. I generally do not recommend to mock something like a Calendar. If you do weird and complex thing with it, just encapsulate in something you can test and mock easily.
And in fact, we pretty much never use Calendar.getInstance(). It returns something according to the locale. But it's rare that you don't want a specific calendar i.e. GregorianCalendar. So just do new GregorianCalendar.
But anyway, add a protected method doing
protected Calendar newCalendar() {
return Calendar.getInstance(); // or new GregorianCalendar()
}
will take 2 minutes and then a simple partial mock will do the trick.
Finally, I also don't recommend to use Calendar. You have a much nicer API in java.util.date in Java 8.
All this said, here is how you should do it. Calendar is a system class, so you need to follow a real specific path which is explained here.
#RunWith(PowerMockRunner.class)
#PrepareForTest(Calendar.class)
public class MyTest {
#Test
public void testDetermineMaximumScans() throws ParseException {
PowerMock.mockStatic(Calendar.class);
Calendar calendar = mock(Calendar.class);
EasyMock.expect(Calendar.getInstance()).andReturn(calendar);
EasyMock.expect(calendar.get(Calendar.DAY_OF_WEEK)).andReturn(6);
// really important to replayAll to replay the static expectation
PowerMock.replayAll(calendar);
assertThat(Calendar.getInstance().get(Calendar.DAY_OF_WEEK)).isEqualTo(6);
// and verifyAll is you want to verify that the static call actually happened
PowerMock.verifyAll();
}
}

Suppress JOptionPane.showInputDialog in junit test

I am using the JOptionPane.showInputDialog call in my code.
When the junit tests run it pops up the window.
Is there a way to suppress the pop-up?
Wold mocking it help?
Please help me on this.
I know - this question is ancient. But maybe sometimes someone will have the same problem...
Remember: It's your code, isn't it? So you can easily refactor from
public boolean myMethod() {
String value = "NOTHING";
if(this.someCondition) {
value = JOptionPane.showInputDialog(...);
}
return "NOTHING".equals(value);
}
to
public boolean myMethod() {
String value = "NOTHING";
if(this.someCondition) {
value = getValueFromDialog();
}
return "NOTHING".equals(value);
}
protected getValueFromDialog() {
return JOptionPane.showInputDialog(...)
}
This done, you can write a test mocking away the actual invocation of JOptionPane (Example uses Mockito syntax)
#Test
public void test_myMethod() {
MyClass toTest = mock(MyClass.class);
//Call real method we want to test
when(toTest.myMethod()).doCallRealMethod();
//Mock away JOptionPane
when(toTest.getValueFromDialog()).thenReturn("HELLO JUNIT");
//Perform actual test code
assertFalse(toTest.myMethod());
}
All done - now add tests simulating all the funny stuff that might happen as a result of JOptionPane.showInputDialog() (returning null, returning unexpected values...) by simply adding test cases and different values for
when(toTest.getValueFromDialog()).thenReturn(...);

Accessing non public Method of Object in JUNIT

Hi I am new to unit testing. Is it possible to access methods that are private?
A very simple example
ObjectA
----------
File file;
private void setupFile (){
//do something
file = "C:\file.dat"
}
In TestCase
File sth = ObjectA.setupFile();
assertNotNull(sth);
I am unable to test whether the file variable is null in method ObjectA.setup()
as I cannot run ObjectA.setupFile()
I am not sure about whether doing like this make sense in terms of unit testing.
So is that a better practice to write every method returning sth and set them public for easier unit testing?
Thanks in advance
In general, you should avoid changing the access of a method/field to enable testing. If you do this then you risk developers using the method directly.
However, if you do need to, then making it protected as Deco says is a good way, so it's accessible from the JUnit tests. If you do this, make sure that it is well documented that this is an method for internal use.
A better way is to test the behaviour of the public methods; you shouldn't care about internal implementation details of a class, so you should only be testing public methods. It's hard to tell from your code, but presumably, the setupFile() has effects later on other methods, so you can test those effects, not the fact that file is not null.
External dependencies (such as dependencies on file system, environment variables) can be worked around in your tests, or injected directly into the class. For the general principle, see my answer to How to test code dependent on environment variables using JUnit?
If it is not absolutely necessary to have the method as private, you can have it as package private (i.e. default access) so that you can call it directly in a JUnit test.
Package private methods can only be used in the package that they are declared, and do not become part of the API of the class. You declare a method package private by putting no modifier on it's declaration.
Here's an example to demonstrate:
public class MyClass() {
int foo;
public MyClass() {
this.foo = 0;
}
void notSoComplexCalculationMethod(int a) {
foo = a * 2;
}
//Other methods here . . .
}
public class MyClassTest extends TestCase {
private MyClass myClass;
protected void setUp() {
super.setUp();
myClass = new MyClass();
}
public void testNotSoComplexCalculationMethod() {
int a = 2;
assertEquals(4, myClass.notSoComplexCalculationMethod(a));
//Unit test passes, yay! Now you've tested a package private method.
}
}