PowerMock not stubbing static method properly? - java

I'm trying to write a VERY simple test, using powermock and Robolectric. Here's my code:
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.robolectric.RobolectricTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
#RunWith(RobolectricTestRunner.class)
#PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
#PrepareForTest(BadStaticClass.class)
public class SomeActivityTest {
#Test
public void testSomething() {
PowerMockito.mockStatic(BadStaticClass.class);
Mockito.when(BadStaticClass.dontWantThisMethod()).thenReturn(false);
new SomeActivity().usesStatic();
}
}
Basically, i have a class, "SomeActivity", and it has a method that makes a call to BadStaticClass.dontWantThisMethod(). i want that static method to be stubbed out.
why is my code not working?
i kept getting errors like:
you are trying to stub a final method, you naughty developer!
which, i thought the whole point of PowerMock was to not see that.

According to the PowerMock API you have to include #RunWith(PowerMockRunner.class) for certain versions of JUnit, etc. However you also need to be using #RunWith(RobolectricTestRunner.class) and cannot specify more than one #RunWith on a Class.
So what to do?
I suspect you can keep your code above and introduce a JUnit #Rule (see: PowerMockRule docs) and also described in this similar post. Just be sure you check the versions of the Jars you are using so that they match those described in that other SO post. All too often things just won't work unless you get compatible versions, and it's not always obvious what versions are compatible.

Related

Junit 5 and Mockito 3: UnnecessaryStubbingException not thrown when injecting #Mock's through constructor

Looking at the two test classes below I assume the same behavior from them: to have an UnnecessaryStubbingException thrown. However MyTest2 does not. The MockitoExtension class should have strictness set to strict stubs by default. I can't seem to find any documented information about this behavior and I really want to understand it.
I prefer to write my tests as MyTest2 because I like to have final fields wherever possible, though I also really enjoy the strict stubs check done by Mockito.. Please help my understand the difference between the two tests.
package example;
import java.util.List;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
class MyTest {
#Mock
List<Integer> integerList;
#Test
void test() {
Mockito.when(integerList.size()).thenReturn(10);
System.out.println("Do something not involving list");
Assertions.assertTrue(true);
}
}
#ExtendWith(MockitoExtension.class)
class MyTest2 {
final List<Integer> integerList;
MyTest2(#Mock List<Integer> integerList) {
this.integerList = integerList;
}
#Test
void test() {
Mockito.when(integerList.size()).thenReturn(10);
System.out.println("Do something not involving list");
Assertions.assertTrue(true);
}
}
I found the reason in the mockito documentation:
Mockito JUnit Runner triggers UnnecessaryStubbingException only when none of the test methods use the stubbings. This means that it is ok to put default stubbing in a 'setup' method or in test class constructor. That default stubbing needs to be used at least once by one of the test methods.
If you do not want the exception to be thrown for the MyTest class, you can use Mockito.lenient or MockitoSettings. But I guess it is not possible to activate the exception for the MyTest2 class.

Difference between #TestSubject and #InjectMocks?

While learning Mockito I found two different annotations #TestSubject and #InjectMocks at below references.
#TestSubject Ref#InjectMocks Ref
#InjectMocks annotation is working absolutely fine as explained in tutorial but #TestSubject doesn't work rather its displaying error.
I'm getting TestSubject cannot be resolved to a type error for #TestSubject annotation in the below code snippet however I have done proper setup (including Junit & Mockito jar files in the build path).
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import com.infosys.junitinteg.action.MathApplication;
import com.infosys.junitinteg.service.CalculatorService;
#RunWith(MockitoJUnitRunner.class)
public class MathApplicationTester {
// #TestSubject annotation is used to identify class which is going to use
// the mock object
#TestSubject
MathApplication mathApplication = new MathApplication();
// #Mock annotation is used to create the mock object to be injected
#Mock
CalculatorService calcService;
#Test(expected = RuntimeException.class)
public void testAdd() {
// add the behavior to throw exception
Mockito.doThrow(new RuntimeException("Add operation not implemented")).when(calcService).add(10.0, 20.0);
// test the add functionality
Assert.assertEquals(mathApplication.add(10.0, 20.0), 30.0, 0);
}
}
I have two questions here.
1. Has some one encountered similar issue? If yes then what was the root cause and solution?
2. If it's working fine then what is the difference between #TestSubject and #InjectMocks annotations?
#TestSubject is the annotation of EasyMock that does the same like Mockito's #InjectMocks. If you're using Mockito then you have to use #InjectMocks.

Import for mockito

I am trying to put statement like
when(myDao.fetchTree(anyLong())).thenReturn(myTreeList);
and I have import
import static org.mockito.Mockito.when;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.thenReturn; //not importing
import for thenReturn is not mapping. Is this a version issue? I am using Mockito 1.8.4.
Mockito's when returns an object of class OngoingStubbing. This class has a method thenReturn(), and that's what gets called in your example code. No additional import is needed.
when(myDao.fetchTree(anyLong())).thenReturn(myTreeList);
could be broken up as
OngoingStubbing thing = when(myDao.fetchTree(anyLong()));
thing.thenReturn(myTreeList);
You are just calling the thenReturn method of OngoingStubbing.
It should be enough if you use:
import static org.mockito.Mockito.*;
And remove the rest.
Your question: Is this a version issue?
I'd say NO, that is not a version issue.
As suggested previously, you should
create minimal test with this code in test
when(myDao.fetchTree(anyLong())).thenReturn(myTreeList);
run this code from command-line (not inside in STS or any IDE or something a like)
Q: Why run it from command-line and avoid using IDE-s etc?
A: Because sometimes code parsers and checkers and validators of your favorited IDE reports false positives about some corner-cases in code.

Unit testing annotations?

I'm asking myself how deep should I go in (unit) testing my classes.
As example, I have following simple class .
import javax.annotation.security.PermitAll;
import javax.ejb.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
#Path(value = "ping")
#Singleton
#PermitAll
public class PingRestService {
#GET
#Produces(MediaType.TEXT_PLAIN)
public String pingMethod(){
return "pong";
}
}
I wrote following unit test:
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import javax.annotation.security.PermitAll;
import javax.ejb.Singleton;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.junit.Test;
public class PingRestServiceTest {
PingRestService prs = new PingRestService();
#Test
public void testClassAnnotations(){
assertEquals(3, prs.getClass().getAnnotations().length);
assertTrue(prs.getClass().isAnnotationPresent(PermitAll.class));
assertTrue(prs.getClass().isAnnotationPresent(Singleton.class));
assertTrue(prs.getClass().isAnnotationPresent(Path.class));
assertEquals("ping", prs.getClass().getAnnotation(Path.class).value());
}
#Test
public void testPingMethodAnnotations() throws SecurityException, NoSuchMethodException{
Method method = prs.getClass().getDeclaredMethod("pingMethod");
assertEquals(2, method.getAnnotations().length);
assertTrue(method.isAnnotationPresent(GET.class));
assertTrue(method.isAnnotationPresent(Produces.class));
assertEquals(1, method.getAnnotation(Produces.class).value().length);
assertEquals(MediaType.TEXT_PLAIN, method.getAnnotation(Produces.class).value()[0]);
}
#Test
public void testPingMethod() {
assertEquals("pong", prs.pingMethod());
}
}
does it make sense?
Or should I only test the returning string ("pong", testPingMethod), skipping all annotations tests (testClassAnnotations,testPingMethodAnnotations) ?
I think some annotations are part of a business logic (e.g. PermitAll), and therefore should be tested.
Most of the time one tests the functionality of the code and not the way it is implemented. This is called Black Box Testing (see: http://en.wikipedia.org/wiki/Black-box_testing).
When implementing a test you should ask yourself: "What are the possible input values of the unit to test and what are the expected results?"
Now in the test you call your code with the input values and check the result with the expected one to make sure your code behaves the way you want it.
Over time you might optimize the code without wanting to change the functionality. Then you should not need to change your test. But you can re-run it to make sure it still behaves the same way. Even if it is implemented differently. Or you might make change implementation details that have side effects to the functionality you tested. Also in this case you don't need to change the test but you just need to re-run it.
In your simple case you have no input and one static output so you can just call the method and check if "pong" is returned. But real life cases that are tested are rarely that simple.
Edit: You can see the security that #PermitAll configures and the URL path that '#Path' configures as inputs and also test them in an integration test the way 'Boris the Spider' and 'Avi' suggested. But the other annotations are implementation specific.
In my opinion those annotations are aspects of your class and not the essence of it, its real purpose, so shouldn't be unit tested.
Maybe tomorrow you will use Spring MVC instead of JAX-RS, but your class would have the same behavior so the unit test should be the same

Using assertArrayEquals in unit tests

My intention is to use assertArrayEquals(int[], int[]) JUnit method described in the API for verification of one method in my class.
But Eclipse shows me the error message that it can't recognize such a method. Those two imports are in place:
import java.util.Arrays;
import junit.framework.TestCase;
Did I miss something?
This would work with JUnit 5:
import static org.junit.jupiter.api.Assertions.*;
assertArrayEquals(new int[]{1,2,3},new int[]{1,2,3});
This should work with JUnit 4:
import static org.junit.Assert.*;
import org.junit.Test;
public class JUnitTest {
/** Have JUnit run this test() method. */
#Test
public void test() throws Exception {
assertArrayEquals(new int[]{1,2,3},new int[]{1,2,3});
}
}
This is the same for the old JUnit framework (JUnit 3):
import junit.framework.TestCase;
public class JUnitTest extends TestCase {
public void test() {
assertArrayEquals(new int[]{1,2,3},new int[]{1,2,3});
}
}
Note the difference: no Annotations and the test class is a subclass of TestCase (which implements the static assert methods).
This could be useful if you want to use just assertEquals without depending on your Junit version
assertTrue(Arrays.equals(expected, actual));
Try to add:
import static org.junit.Assert.*;
assertArrayEquals is a static method.
If you are writing JUnit 3.x style tests which extend TestCase, then you don't need to use the Assert qualifier - TestCase extends Assert itself and so these methods are available without the qualifier.
If you use JUnit 4 annotations, avoiding the TestCase base class, then the Assert qualifier is needed, as well as the import org.junit.Assert. You can use a static import to avoid the qualifier in these cases, but these are considered poor style by some.

Categories