I'm looking for a unit test for Controllers using Mockito and PowerMock. Every controller has only private void methods with a single argument and a #Autowired service dependency:
public class ProjectController {
private ProjectServiceImpl service;
#Autowired
public void setInjectedBean(ProjectServiceImpl service) {
this.service = service;
}
private void createProject(String someString) {
// do stuff by calling service.doSomething(someString)
}
}
ProjectServiceImpl is annotated with #Service and has only a public void method with a String as argument. As it is too simple, I didn't bother providing the code.
My last attempt to accomplish that test looks like this:
#RunWith(PowerMockRunner.class)
#ExtendWith(MockitoExtension.class)
#PrepareForTest({ProjectController.class, ProjectServiceImpl.class})
class ProjectControllerTest {
#InjectMocks
private ProjectController controller;
#Mock
private ProjectServiceImplservice;
#Test
void createProject() throws Exception {
controller = PowerMockito.spy(new ProjectController());
PowerMockito.doNothing().when(controller, "createProject", new String(""));
}
}
Question 1: is it possible to make PowerMock work aside of Mockito?
Question 2: using PowerMock, what's the most appropriate way of calling for a private void method with argument?
IMPORTANT: Before marking this post as a duplicate, I personally challenge you finding any content how to do so. It seems like there is just no content showing not only how to make both work together, but make PowerMockito calls for a private void method with one or more arguments.
dependencies used for PowerMock:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
Related
Hi I´m trying to test some legacy code that uses command patron. I couldn´t make work PowerMock propperly and I tryed to make a simple example to test if it could work.
I think that the problem is that the method whenNew() or expectNew() is not intercepting when in the original code a "new" instance is declared.
Here is the code:
ClassToTest
public class ClassToTest {
public ClassToTest() {
}
public ClassInstance getClassInstance() {
ClassInstance a = new ClassInstance(1);
return a;
}
}
ClassInstance
public class ClassInstance {
private int detector;
public ClassInstance(int detector) {
this.detector = detector;
}
public int getDetector() {
return detector;
}
public void setDetector(int detector) {
this.detector = detector;
}
}
The test
#RunWith(PowerMockRunner.class)
#PrepareForTest({ClassToTestTest.class, ClassInstance.class})
public class ClassToTestTest {
#Test
public void test() throws Exception {
ClassInstance n = new ClassInstance(2);
PowerMockito.whenNew(ClassInstance.class).withAnyArguments().thenReturn(n);
ClassToTest c = Mockito.spy(ClassToTest.class);
System.out.println(c.getClassInstance().getDetector());
}
}
And here is the pom.xml.
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.18.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-easymock</artifactId>
<version>2.0.9</version>
<scope>test</scope>
</dependency>
So when I run the test, the expected value if PowerMockito intercepts the new instance should be 2 but the console gives me 1...
I am using junit to test my annotation processor. The test is failing. It seems like never entering the joinpoint, without any exception.
My annotation is like this:
#Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
public #interface EventListener {
boolean isListening() default true;
boolean isDefaultListener() default true;
Class<SomeListener> subscriber() default None.class;
public static class None implements SomeListener {
... ...
}
}
And the processor like this:
#Aspect
public class ListenerProcessor {
#Pointcut("#annotation(com.xxx.common.event.annotation.EventListener)")
public void sign() {}
#Before("sign()")
public void anAdvice(JoinPoint joinPoint) { ***//this has never executed***
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
Method method = signature.getMethod();
EventListener anno = method.getAnnotation(EventListener.class);
if (anno != null) {
if (anno.isListening()) {
if (anno.isDefaultListener())
doDefault();
else {
Class<SomeListener> clazz = anno.subscriber();
doCustomize(clazz);
}
} else {
... ...
}
}
}
... ...
}
My test is like this:
#RunWith(SpringRunner.class)
#ContextConfiguration(locations = "classpath:applicationContext.xml")
public class EventListenerTest {
#Test
public final void test() {
//given
Long bef = countEvents();
//when
TestEntity1 t1 = appWithDefaultListener();
//then
TestEntity1 t2 = getEntityLike(t1);
Long aft = countEvents();
assertThat(t1).isEqualToComparingFieldByField(t2);
assertThat(aft).isEqualTo(bef+1);
}
#Transactional
#EventListener(isListening=true, isDefaultListener=true) ***//this seems does'nt work***
private TestEntity1 appWithDefaultListener() {
TestEntity1 t1 = new TestEntity1(...);
return myRepository.save(t1);
}
#Transactional(readOnly = true)
private TestEntity1 getEntityLike(TestEntity1 t1) {
TestEntity1 t2 = myRepository.findOne(Example.of(t1));
return t2;
}
}
My applicationContext.xml is like this:
<aop:aspectj-autoproxy proxy-target-class="true">
<aop:include name="eventProcessor"/>
</aop:aspectj-autoproxy>
<context:annotation-config />
<bean id="eventProcessor"
class="com.xxx.common.event.process.EventListenerProcessor"
scope="prototype"/>
And My pom has these dependencies:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.13.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.assertj</groupId>
<artifactId>assertj-core</artifactId>
<version>2.6.0</version>
</dependency>
What am I missing? Or any thing goes wrong?
Don't define appWithDefaultListener() and getEntityLike() as private methods in your test class.
Instead, you need to define them as non-private methods in another class that you configure as a bean in the test's ApplicationContext. Then have that bean #Autowired into your test class and invoke the methods via that reference so that your aspect is applied.
I am new to the concept of unit testing with Spring controllers. I'm following some examples I found online and trying to implement their testing strategy. This is my basic controller:
#Controller
public class GreetingController {
#RequestMapping("/greeting")
public String greeting(#RequestParam(value = "name2", required = false, defaultValue = "World2") String name2,
Model model) {
model.addAttribute("name", name2);
return "greeting";
}
}
This is my unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration
#WebAppConfiguration
public class ControllerGreetingTest {
private MockMvc mockMvc;
#Autowired
GreetingController greetingController;
#Test
public void shouldReturnSomething() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greeting"));
}
}
Seems pretty straight forward but I get the following error:
java.lang.IllegalStateException: Neither GenericXmlWebContextLoader
nor AnnotationConfigWebContextLoader was able to detect defaults, and
no ApplicationContextInitializers were declared for context
configuration [ContextConfigurationAttributes#1698539 declaringClass =
'com.practice.demo.ControllerGreetingTest', locations = '{}', classes
= '{}', inheritLocations = true, initializers = '{}', inheritInitializers = true, name = [null], contextLoaderClass =
'org.springframework.test.context.ContextLoader']
I'm assuming I have to add a parameter to the #ContextConfiguration annotation but not sure what to include in there.
EDIT = This is what I have so far:
public class ControllerGreetingTest {
private MockMvc mockMvc;
#Before
public void setup(){
this.mockMvc = standaloneSetup(new GreetingController()).build();
}
#Test
public void shouldReturnDefaultString() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greetings"))
.andExpect(model().attribute("name","World2"));
}
}
It does the job but it doesn't use any of the Spring annotations like I tried to do before.. this approach is not good so trying to figure out why I keep gettings errors whenever I include the annotations in my test file.
My POM:
<dependencies>
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>1.5.7.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<artifactId>hamcrest-core</artifactId>
<groupId>org.hamcrest</groupId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.3.RELEASE</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mongodb/mongo-java-driver -->
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>1.5.7.RELEASE</version>
<scope>test</scope>
</dependency>
</dependencies>
You should use spring-boot-starter-test dependency. It has almost everything for testing.
And for testing controller part of a spring application, you should use #WebMvcTest annotation for your test class. With this annotation spring will load context just for controller part. Plus you don't need to setup method if you use this annotation. You can simply autowire mockMvc. Your test class should be like this:
#RunWith(SpringRunner.class)
#WebMvcTest(GreetingController.class)
public class ControllerGreetingTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private SomeServiceClass someServiceClass;
#Test
public void shouldReturnDefaultString() throws Exception {
mockMvc.perform(get("/greeting"))
.andExpect(status().isOk())
.andExpect(view().name("greetings"))
.andExpect(model().attribute("name","World2"));
}
}
Note: Your controller does not have any autowired fields. In cases that controller has some autowired objects like service or repository objects. you can simply mock them with annotation #MockBean as you can see above code.
See this link for other test slice annotations spring provided
For projects with org.springframework.boot:spring-boot-starter-test can use
#RunWith(SpringRunner.class)
#SpringBootTest(classes = App.class)
public class ControllerGreetingTest {
...
}
Where App.class is you main application class annotated with #SpringBootApplication. But you better read the documentation. And if you don't want to include (classes = App.class) part you also can change folder structure
For simple controllers it is possible to perform simple standalone tests
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = ControllerGreeting.class)
public class ControllerGreetingTest {
#Autowired
private MockMvc mockMvc;
...
}
Just add the #ContextConfiguration annotation and refer one or more XML configuration file locations or one or more configuration classes. Otherwise Spring cannot autowire your controller, which should be tested.
Example: You want to test a controller, which uses MyService via #Autowired:
MyControllerTest: Injects the controller, which should be tested using the MyTestConfig configuration class.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {MyTestConfig.class})
#WebAppConfiguration
public class MyControllerTest {
private MockMvc mockMvc;
#Autowired
private MyController controller;
#Before
public void setUp() {
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
}
// Tests
}
MyTestConfig: Returns all beans, which are required for the test. Use Mockito to mock the depedencies of your controller, because we want to test only the controller and not the service layer.
#Configuration
public class MyTestConfig {
#Bean
public MyService myService() {
return Mockito.mock(MyService.class);
}
#Bean
public MyController myController() {
return new MyController();
}
}
I'm trying to instantiate a mock object with Mockito. I found two examples in the article here, still this article is a very bad example for a newbie like me in Mockito.
Can somebody give me a better example of how it is done with either of the two options?
Simple when doing PowerMockito
public class A {
private final String name;
public A(String name) {
this.name= name;
}
public String sayHello() {
return "Hi " + this.name;
}}
#RunWith(PowerMockRunner.class)
#PrepareForTest(A.class)
public class MockA {
#Test
public void testSayHello throws Throwable {
A a = mock(A.class);
when(a.sayHello()).thenReturn("Hi PowerMockito");
PowerMockito.whenNew(A.class).withArguments(Mockito.anyString()).thenReturn(a);
assertThat(new A("I am mockcked").sayHello(), equalTo("Yes, you are!"));
}
}
Dependencies
<dependencies>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>${powermock.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-all</artifactId>
<version>{mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
We want to implement a REST-Service on a Jetty, that receives a Heartbeat from multiple Clients and these Heartbeats are stored in a Hashmap to work with them in further functionallities.
Thats how the code looks like at the moment:
The class that should be injected.
public class SharedMemory {
private SharedMemory sharedMemory = null;
private HashMap<String, HeartbeatModel> memory = null;
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
public SharedMemory(){
memory = new HashMap<String, HeartbeatModel>();
}
...
}
The binder class:
public class SharedMemoryBinder extends AbstractBinder{
#Override
protected void configure() {
bind(new SharedMemory()).to(SharedMemory.class);
}
}
The feature class:
public class SharedMemoryFeature implements Feature{
#Override
public boolean configure(FeatureContext context) {
context.register(new SharedMemoryBinder());
return true;
}
}
The service class
#Path("/HeartbeatService")
public class GCEHeartbeatService {
#Inject
private SharedMemory sharedMemory;
#POST
#Path("/postHeartbeat")
#Consumes(MediaType.APPLICATION_JSON)
public Response postHeartbeat(Heartbeat heartbeat){
sharedMemory.storeToMemory(heartbeat);
return Response.ok().build();
}
...
}
The application class:
#ApplicationPath("/")
public class Application extends javax.ws.rs.core.Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<>();
classes.add(GCEHeartbeatService.class);
classes.add(SharedMemoryFeature.class);
return classes;
}
}
These are the used dependencies:
<dependencies>
<dependency>
<groupId>org.glassfish.jersey.core</groupId>
<artifactId>jersey-server</artifactId>
<version>2.17</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-servlet</artifactId>
<version>2.17</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4.1</version>
</dependency>
<dependency>
<groupId>de.hszg</groupId>
<artifactId>common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.glassfish.jersey.bundles</groupId>
<artifactId>jaxrs-ri</artifactId>
<version>2.13</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.5.3</version>
</dependency>
</dependencies>
So with this configuration, i always get a 415 unsupported media type exception when i post a heartbeat to the postHeartbeat service, even with another media type.
"...i always get a 415 unsupported media type exception when i post"
This doesn't seem like a problem related to your DI configuration. It looks like you just need to register the JSON provider. With this particular provider
<dependency>
<groupId>com.fasterxml.jackson.jaxrs</groupId>
<artifactId>jackson-jaxrs-json-provider</artifactId>
<version>2.5.3</version>
</dependency>
If you override the registration of resources/provider through classpath scanning (which you have done, simply by overriding getClasses() and returning a non-empty set, then the provider will not be registered.
You can simply register it yourself.
classes.add(JacksonJsonProvider.class);
-- or --
classes.add(JacksonJaxbJsonProvider.class);
If you look in the jar, you will also see an ExceptionMapper you may want to register.
Alternatively, you can use this dependency
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.17</version>
</dependency>
which wraps the above dependency in a auto-discoverable feature, and registers, the provider and the ExceptionMapper. With this dependency, you won't need to register anything. If for some weird reason it isn't register, you can just add it yourself
classes.add(JacksonFeature.class);
As an aside, if portability is not an issue, I would use the ResourceConfig class (which extends Application) instead. It's much easier to work with.
#ApplicationPath("/")
public class AppConfig extends ResourceConfig {
public AppConfig() {
packages("scan.packages.for.resource.and.provider");
register(ResourceOrProvider.class);
register(new MyAppBinder());
}
}
See the ResourceConfig API