Fail to load ApplicationContext SpringBoot with SpringSecurity and JUnit Jupiter - java

I'm working on a REST API using Spring Boot. Currently, the V1 of the API is complete. So, I'm implementing Spring Security to manage authentication and authorization.
Since I've implemented Spring Security, my JUnit Jupiter tests does not work (no one works).
I searched a lot a solution on internet, but all answers I found are for JUnit4 and not JUnit5 (so I don't have all required classes).
I got the classical "Fail to load ApplicationContext" error, but I don't know how to solve it.
Can you help me?
Here is my code for one class (UserController):
gradle.build:
plugins {
id 'jacoco'
id 'org.springframework.boot' version '2.6.0'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '11'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web:2.5.6'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa:2.5.6'
implementation 'org.projectlombok:lombok:1.18.22'
annotationProcessor 'org.projectlombok:lombok:1.18.22'
developmentOnly 'org.springframework.boot:spring-boot-devtools:2.5.6'
testImplementation 'org.springframework.boot:spring-boot-starter-test:2.5.6'
implementation 'com.h2database:h2'
runtimeOnly 'com.h2database:h2'
}
test {
systemProperty 'spring.profiles.active', 'test'
useJUnitPlatform()
finalizedBy jacocoTestReport
}
Application:
#SpringBootApplication
public class BackendApplication {
public static void main(String[] args) {
SpringApplication.run(BackendApplication.class, args);
}
}
UserController sample:
#RestController
#RequestMapping("/api/v1/users")
public class UserController extends AbstractCrudController<User, Long> {
#Autowired
public UserController(CrudService<User, Long> service) { super(service); }
#GetMapping("")
#Override
#Secured({ "ROLE_XXXXX" })
public ResponseEntity<ResponseListDto<User, Long>> findAll() {
return super.findAll();
}
// ...
}
MockedUserControllerTest sample:
#SpringBootTest
public class MockedUserControllerTest {
#Mock
private UserService service;
#InjectMocks
private UserController controller;
private static User user;
private static List<User> users;
#BeforeAll
public static void beforeAll() {
user = new User();
user.setId(1L);
user.setUsername("A user name");
user.setFirstname("First-Name");
user.setLastname("Last-Name");
user.setPassword("A Gre4t P4ssw0rd!");
user.setMail("first-name.last-name#mail.com");
user.setBirthDate(Date.valueOf("1980-01-15"));
user.setKey("A-key");
user.setNewsletter(Boolean.TRUE);
users = List.of(user);
}
#Test
public void testFindAll() {
when(service.findAll()).thenReturn(users);
assertEquals(new ResponseEntity<>(new ResponseListDto<>(users, null, null), HttpStatus.OK),
controller.findAll());
}
//...
}
Thank you in advance for looking my problem.

For a #SpringBootTest you should use #MockBean annotation, because the Spring context will load in order to run the tests. The loaded context will create mocked beans from the dependencies being annotated by #MockBean and it will inject them into that service, which is being tested.
For pure unit tests the #SpringBootTest annotation should be skipped and Mockito (#Mock annotation) can be used. Spring context will not load in this case, the test will focus on that specific class you are testing. With the created Mocks, you can control the behaviour of dependencies, you can arrange different scenarios for your test.

After some other basic researches (how to write tests with junit5 and mockito), I solved my problem myself.
Here is the answer which helped me: https://stackoverflow.com/a/40962941/13523752
What I wanted is a class test only for the controller I specified. So I didn't need the ApplicationContext. That oriented my research.
Note: I'll do other test classes to test all the process. In this tests I'll need the ApplicationContext.
On my test class, I removed the annotation #SpringBootTest to replace it by #ExtendWith(MockitoExtension.class).
The next thing I did is in the #BeforeAll method I have. I had MockitoAnnotations.initMocks(MockedUserControllerTests.class) to load the mocks I annotated.
Now my test work. I only have to extend this solution on all other mocked test classes.
A sample of the test class I have now:
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.jupiter.MockitoExtension;
#ExtendWith(MockitoExtension.class)
public class MockedUserControllerTest {
#Mock
UserService service;
#InjectMocks
UserController controller;
// ...
#BeforeAll
public static void beforeAll() {
MockitoAnnotations.initMocks(MockedUserControllerTest.class);
// ...
}
// ...
}

Related

Java / Spring: Cannot test Caching in Unit Test

In my Java application I am trying to test the following service method with a Unit Test:
#Service
#EnableCaching
#RequiredArgsConstructor
public class LabelServiceImpl implements LabelService {
private static final String CACHE_NAME = "demoCache";
private final LabelRepository labelRepository;
#Override
public List<LabelDTO> findByUuid(UUID uuid) {
final Label label = labelRepository.findByUuid(uuid)
.orElseThrow(() -> new EntityNotFoundException("Not found."));
final List<LabelDTO> labelList = labelRepository.findByUuid(uuid);
return labelList;
}
}
Here is my Unit Test:
#Import({CacheConfig.class, LabelServiceImpl.class})
#ExtendWith(SpringExtension.class)
#EnableCaching
#ImportAutoConfiguration(classes = {
CacheAutoConfiguration.class,
RedisAutoConfiguration.class
})
#RunWith(MockitoJUnitRunner.class)
public class CachingTest {
#InjectMocks
private LabelServiceImpl labelService;
#Autowired
private CacheManager cacheManager;
#Mock
private LabelRepository labelRepository;
#Test
void givenRedisCaching_whenFindUuid_thenReturnFromCache() {
//code omitted
LabelDTO labelFromDb = labelService.findByUuid(uuid);
LabelDTO labelFromCache = labelService.findByUuid(uuid);
verify(labelRepository, times(1)).findByUuid(uuid);
}
}
When I use #Autowired for LabelServiceImpl in the Unit Test, I get "Nullpointer exception" error. If I use #InjectMocks annotation for that instance, then there is no error, but no caching is done (it calls labelService.findByUuid 2 times instead of 1).
I think I made a mistake related to the annotations in Unit Test class, but I tried many different combinations and cannot solve the problem.
So, how can I fix the problem?
Update: Finally I fixed the problem by using the following approach. However, I also had to update the mockito-core version from 3.3.3 to 3.7.0 as shown below. Otherwise I was getting "java.lang.NoSuchMethodError: org.mockito.MockitoAnnotations.openMocks(Ljava/lang/Object;)Ljava/lang/AutoCloseable;" error.
pom.xml:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.7.0</version>
<scope>test</scope>
</dependency>
CachingTest:
#ExtendWith(MockitoExtension.class)
#SpringBootTest
public class CachingTest {
#Autowired
#InjectMocks
private LabelServiceImpl labelService;
#MockBean
private LabelRepository labelRepository;
#Test
public void givenRedisCaching_whenFindUuid_thenReturnFromCache() {
//code omitted
LabelDTO labelFromDb = labelService.findByUuid(uuid);
LabelDTO labelFromCache = labelService.findByUuid(uuid);
verify(labelRepository, times(1)).findByUuid(uuid);
assertEquals(labelFromDb, labelFromCache);
}
}
Mockito does not simulate your springt boot application. It just mocks the annotated objects and injects your mock objects into your labelService.
On the other hand to test a spring boot application #Autowired is not enough.
You have to build a Spring Boot Integration Test.
To do this annotate your Test with #SpringBootTest.
This initializes a real Application Context.
try to autowire, LabelService
package com.demo;
import org.springframework.stereotype.Service;
#Service
public class LabelService
{
public String getLabel()
{
return "Test Label " + System.currentTimeMillis();
}
}
package com.demo;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
#SpringBootTest
public class CachingTest
{
#Autowired
private LabelService labelService;
#Test
public void testLabelService()
{
assertNotNull(labelService);
}
}
this works

Disable Spring Security config class for #WebMvcTest in Spring Boot

Recently I have added Spring Security to my Spring Boot project using the following class:
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class MySecurityConfig {
}
as result, by default all my URLs are now protected with authentication and a self-generated password.
The problem is that all tests in a #WebMvcTest class that I used for unit-testing a controller:
#RunWith(SpringRunner.class)
#WebMvcTest(SomeController.class)
public class SomeControllerTest {...}
are now failing everywhere because of lack of authorization.
Question: can I tell #Test methods to ignore authorization so they keep succeeding as before?
How can I prevent the #EnableWebSecurity config class from being picked on a specific #WebMvcTest unit testing class?
I would like the tests already in place to be able to still go through and to test the authentication features separately later on.
So far I have tried to use a nested config class in the testing class in order to exclude security configs:
#RunWith(SpringRunner.class)
#WebMvcTest(SomeController.class)
public class SomeControllerTest {
#Configuration
#EnableAutoConfiguration(exclude = { SecurityAutoConfiguration.class})
static class ContextConfiguration { }
....}
but it seems not to work.
NOTE : I am using Spring Boot 1.5.8
For me in Spring Boot 2.2.4 (JUnit5) the below seems to have worked and bypass the security filter.
#ExtendWith(SpringExtension.class)
#WebMvcTest(SomeController.class)
#AutoConfigureMockMvc(addFilters = false)
public class SomeControllerTest {
...
Note: this simply disables any filters in the SpringSecurity configuration. It won't disable the security completely. In other words it will still bootstrap security without loading any filters.
You can set secure=false in the #WebMvcTest annoation.
It will skip the spring security MockMvc auto configuration in your Test
#WebMvcTest(controllers = SomeController.class, secure = false)
public class SomeControllerTest {
Note by the author:
As of 2021, this answer has been obsolete for a few years and it probably won't work for you.
In Spring Boot 2.2.6, #WebMvcTest is meta annotated with #AutoConfigureWebMvc which auto-configure org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration as you can see in spring.factories of spring-boot-test-autoconfigure.jar
So you just have to exclude SecurityAutoConfiguration in your test to disable Spring Security :
#WebMvcTest(excludeAutoConfiguration = SecurityAutoConfiguration.class)
In Spring Boot 2.4 both secure flags were removed and none of the answers here actually work.
I ended up excluding all the security myself and wrapping it around in a custom annotation.
import org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration;
import org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.core.annotation.AliasFor;
import org.springframework.security.config.annotation.web.WebSecurityConfigurer;
import java.lang.annotation.*;
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#WebMvcTest(excludeFilters = {#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = WebSecurityConfigurer.class)},
excludeAutoConfiguration = {SecurityAutoConfiguration.class,
SecurityFilterAutoConfiguration.class,
OAuth2ClientAutoConfiguration.class,
OAuth2ResourceServerAutoConfiguration.class})
public #interface UnsecuredWebMvcTest {
#AliasFor(annotation = WebMvcTest.class, attribute = "controllers")
Class<?>[] value() default {};
#AliasFor(annotation = WebMvcTest.class, attribute = "controllers")
Class<?>[] controllers() default {};
}
With Spring Security 4+, I find #WithMockUser annotation to be very handy. It provides a mock user and password to test spring security methods annotated with #PreAuthorize or #PostAuthorize. All you need to do is annotate the test method with #WithMockUser. The default role for the user is USER. You can override the default username and role too.
//default
#Test
#WithMockUser
public void getProfile() {
//your test here
}
//with username and roles
#Test
#WithMockUser(username = "john", roles={"ADMIN"})
public void getProfile() {
//your test here
}
NOTE: This annotation can be used for classes.
#WithMockUser(username = "john", roles={"ADMIN"})
public class UsersAdminSecurityTest {
}
This worked for me, using spring boot 2.3.1.
#ExtendWith(SpringExtension.class)
#WebMvcTest(SomeController.class)
#AutoConfigureMockMvc(addFilters = false)
public class SomeControllerTest {
}
I understand this is a specific question for Spring Boot 1.5 but seems a bit old. In order to have a successful OAuth2 secured controller unit test running I applied the following steps, kindly notice I used Spring Boot 2.2.6, Gradle 5.x, and JUnit 5. This mechanism should work the same way deprecated ones based on #AutoConfigureMockMvc(secure = false) or #WebMvcTest(controllers = SomeController.class, secure = false)
This is for a REST API project that is secured (OAuth2) using Microsoft's Azure Active Directory, but essentially this testing strategy should work for any OIDC, OAuth2 configuration.
The trick is to have a Controller test file and annotate it with #WebMvcTest annotation, however, the following parameters are required:
#WebMvcTest(
value = YourController.class
// this disables loading up the WebSecurityConfig.java file, otherwise it fails on start up
, useDefaultFilters = false
// this one indicates the specific filter to be used, in this case
// related to the GreetController we want to test
, includeFilters = {
#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = YourController.class
)
}
)
Here the configurations that make the test run successfully.
build.gradle
plugins {
id 'org.springframework.boot' version '2.2.6.RELEASE'
id 'io.spring.dependency-management' version '1.0.9.RELEASE'
id 'java'
}
group = 'com.grailscoder'
version = '0.0.1-SNAPSHOT'
configurations {
compileOnly {
extendsFrom annotationProcessor
}
}
repositories {
mavenCentral()
}
ext {
set('azureVersion', "2.2.4")
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-security'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.microsoft.azure:azure-active-directory-spring-boot-starter'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation('org.springframework.boot:spring-boot-starter-test') {
exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
}
testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.5.2'
testImplementation 'org.junit.jupiter:junit-jupiter-params:5.5.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.5.2'
}
dependencyManagement {
imports {
mavenBom "com.microsoft.azure:azure-spring-boot-bom:${azureVersion}"
}
}
test {
useJUnitPlatform()
}
GreetController.java
package com.grailscoder.controller;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
#RequiredArgsConstructor
#RestController
public class GreetController {
#GetMapping("/greets")
#PreAuthorize("hasRole('ROLE_USER')") // This is validating against Active Directory's User role granted to the
// current user.
#ResponseStatus(HttpStatus.OK)
public String getGreetMessage() {
return "Greets from secret controller";
}
}
WebSecurityConfig.java
package com.grailscoder.config;
import com.microsoft.azure.spring.autoconfigure.aad.AADAppRoleStatelessAuthenticationFilter;
import lombok.RequiredArgsConstructor;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
#RequiredArgsConstructor
#EnableWebSecurity
#EnableGlobalMethodSecurity(prePostEnabled = true)
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
private final AADAppRoleStatelessAuthenticationFilter aadAuthFilter;
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.NEVER);
http.authorizeRequests()
.antMatchers("/", "/index.html", "/public").permitAll()
.anyRequest().authenticated();
http.addFilterBefore(aadAuthFilter, UsernamePasswordAuthenticationFilter.class);
}
}
application.properties
azure.activedirectory.client-id=xxxxx-AD-client-id-goes-here
azure.activedirectory.session-stateless=true
GreetControllerTest.java
package com.grailscoder.controller;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.FilterType;
import org.springframework.security.test.context.support.WithMockUser;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.ResultActions;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
#WebMvcTest(
value = GreetController.class
// this disables loading up the WebSecurityConfig.java file, otherwise it fails on start up
, useDefaultFilters = false
// this one indicates the specific filter to be used, in this case
// related to the GreetController we want to test
, includeFilters = {
#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = GreetController.class
)
}
)
class GreetControllerTest {
#Autowired
MockMvc mockMvc;
#Autowired
ObjectMapper objectMapper;
#BeforeEach
void setUp() {
// add setup stuff here
}
#Test
#WithMockUser
void testGreet() throws Exception {
ResultActions result = mockMvc.perform(get("/greets"))
.andExpect(status().isOk());
System.out.println(result.andReturn().getResponse().getContentAsString());
}
}
I understand in order to have a similar test JUnit 4 based with a completely different approach, the following test could be used as a reference (but I haven't tried): https://github.com/spring-projects/spring-security/blob/master/samples/boot/oauth2resourceserver/src/test/java/sample/OAuth2ResourceServerControllerTests.java
In my case, for Spring Boot version 2.5.4, I'm able to bypass Jwt security by setting useDefaultFilters = false in #WebMvcTest
#WebMvcTest(controllers = YourController.class, useDefaultFilters = false)
public class YourControllerTest {
// Test cases
}
#AutoConfigureMockMvc(addFilters = false)
Just adding addFilters = false resolved this.
I solved the problem by using following annotations and properties:
#WebMvcTest(controllers =
SomeController.class,
excludeAutoConfiguration = {
MySecurityConfig.class,
ManagementWebSecurityAutoConfiguration.class,
SecurityAutoConfiguration.class
}
)
#ContextConfiguration(classes = SomeController.class)
public class SomeControllerTest {
}
NOTE: I' using spring boot 2.6.6, so secure=false didn't work for me!
#AutoConfigureMockMvc(secure = false) does not work because secure is deprecated
what works:
#AutoConfigureMockMvc(addFilters = false)
does not completely disable spring security just bypass its filter chain.
or
#WebMvcTest(excludeAutoConfiguration = {SecurityAutoConfiguration.class})
(and if you are using Spring Boot actuator:
#WebMvcTest(excludeAutoConfiguration = {SecurityAutoConfiguration.class,
ManagementWebSecurityAutoConfiguration.class})
)

Spring Boot Integration Testing with mocked Services/Components

Situation and Problem: In Spring Boot, how can I inject one or more mocked classes/beans into the application to do an integration test? There are a few answers on StackOverflow, but they are focused on the situation before Spring Boot 1.4 or are just not working for me.
The background is, that in the code bellow the implementation of Settings relies on third party servers and other external systems. The functionality of Settings is already tested in a unit test, so for a full integration test I want to mock out the dependency to these servers or system and just provide dummy values.
MockBean will ignore all existing bean definitions and provide a dummy object, but this object doesn't provide a method behavior in other classes that inject this class. Using the #Before way to set the behavior before a test doesn't influence the injected object or isn't injected in other application services like AuthenticationService.
My question: How can I inject my beans into the application context?
My test:
package ch.swaechter.testapp;
import ch.swaechter.testapp.utils.settings.Settings;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.test.context.junit4.SpringRunner;
#TestConfiguration
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApplication.class})
public class MyApplicationTests {
#MockBean
private Settings settings;
#Before
public void before() {
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");
}
#Test
public void contextLoads() {
String applicationsecret = settings.getApplicationSecret();
System.out.println("Application secret: " + applicationsecret);
}
}
And bellow a service that should use the mocked class, but doesn't receive this mocked class:
package ch.swaechter.testapp;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
#Service
public class AuthenticationServiceImpl implements AuthenticationService {
private final Settings settings;
#Autowired
public AuthenticationServiceImpl(Settings settings) {
this.settings = settings;
}
#Override
public boolean loginUser(String token) {
// Use the application secret to check the token signature
// But here settings.getApplicationSecret() will return null (Instead of Application Secret as specified in the mock)!
return false;
}
}
Looks like you are using Settings object before you specify its mocked behavior.
You have to run
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");
during configuration setup. For preventing that you can create special configuration class for test only.
#RunWith(SpringRunner.class)
#SpringBootTest(classes = {MyApplication.class, MyApplicationTest.TestConfig.class})
public class MyApplicationTest {
private static final String SECRET = "Application Secret";
#TestConfiguration
public static class TestConfig {
#Bean
#Primary
public Settings settingsBean(){
Settings settings = Mockito.mock(Settings.class);
Mockito.when(settings.getApplicationSecret()).thenReturn(SECRET);
Mockito.doReturn(SECRET).when(settings).getApplicationSecret();
return settings;
}
}
.....
}
Also I would recommend you to use next notation for mocking:
Mockito.doReturn(SECRET).when(settings).getApplicationSecret();
It will not run settings::getApplicationSecret
When you annotate a field with #MockBean, spring will create a mock of the annotated class and use it to autowire all beans of the application context.
You must not create the mock yourself with
Settings settings = Mockito.mock(Settings.class);
this would create a second mock, leading to the described problem.
Solution :
#MockBean
private Settings settings;
#Before
public void before() {
Mockito.when(settings.getApplicationSecret()).thenReturn("Application Secret");
}
#Test
public void contextLoads() {
String applicationsecret = settings.getApplicationSecret();
System.out.println("Application secret: " + applicationsecret);
}

Exception while using spock for java unit testing

I am trying to use spock for testing my java based web application. Earlier we used to use, Junit along with Mockito for testing.
I am trying to use #Collaborator and #Subject to autowire my dependencies like we used to use #Mock and #InjectMocks in Mockito.
But it is not working. I am getting the following error.
java.lang.NullPointerException
at com.blogspot.toomuchcoding.spock.subjcollabs.NonConstructorBasedInjector.instantiateSubjectAndSetOnSpecification(NonConstructorBasedInjector.groovy:22)
at com.blogspot.toomuchcoding.spock.subjcollabs.PropertyInjector.tryToInject(PropertyInjector.groovy:16)
at com.blogspot.toomuchcoding.spock.subjcollabs.SubjectsCollaboratorsInterceptor.tryToInjectCandidatesIntoSubject_closure2(SubjectsCollaboratorsInterceptor.groovy:74)
at com.blogspot.toomuchcoding.spock.subjcollabs.SubjectsCollaboratorsInterceptor.tryToInjectCandidatesIntoSubject(SubjectsCollaboratorsInterceptor.groovy:74)
at com.blogspot.toomuchcoding.spock.subjcollabs.SubjectsCollaboratorsInterceptor.intercept_closure1(SubjectsCollaboratorsInterceptor.groovy:69)
at groovy.lang.Closure.call(Closure.java:426)
at groovy.lang.Closure.call(Closure.java:442)
at com.blogspot.toomuchcoding.spock.subjcollabs.SubjectsCollaboratorsInterceptor.intercept(SubjectsCollaboratorsInterceptor.groovy:69)
at org.spockframework.runtime.extension.MethodInvocation.proceed(MethodInvocation.java:87)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Below is my test code.
class UserServiceImplSpec extends Specification {
#Collaborator
UserDAO userDAO = Mock()
#Subject
UserService userService
def "should delete a user"(){
given: "given a user to delete"
User userToDelete = make(a(UserMaker.User));
when:
userService.deleteUser(userToDelete.getId());
then:
true
}
}
Below is the URL for the extension I am using for autowiring.
https://github.com/marcingrzejszczak/spock-subjects-collaborators-extension
Below are my dependencies.
testCompile 'org.codehaus.groovy:groovy-all:2.4.5'
testCompile 'org.spockframework:spock-core:1.0-groovy-2.4'
testCompile 'org.spockframework:spock-spring:1.0-groovy-2.4'
testCompile "cglib:cglib:2.2"
testCompile 'org.objenesis:objenesis:2.2'
testCompile 'com.blogspot.toomuchcoding:spock-subjects-collaborators-extension:1.1.0'
Below is my UserService and UserServiceImpl classes.
public interface UserService {
public void deleteUser(Long userId);
}
#Service("userService")
#Transactional(readOnly = true)
public class UserServiceImpl implements UserService {
#Autowired
private UserDAO userDAO;
#Autowired
private SecurityUtil securityUtil;
#Override
#Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackForClassName = {"java.lang.Exception" })
public void deleteUser(Long userId) {
log.debug("Deleting an user entry with the user id: {}", userId);
User user = userDAO.findById(userId);
userDAO.delete(user);
}
}
The problem was related to the fact that #Subject annotated class should be an implementation and not an interface.
It's enough to change UserService into UserServiceImpl
you have a problem with inject annotations #Collaborator, #Subject
import com.blogspot.toomuchcoding.spock.subjcollabs.Collaborator
import com.blogspot.toomuchcoding.spock.subjcollabs.Subject
Add dependency:
<dependency>
<groupId>com.blogspot.toomuchcoding</groupId>
<artifactId>spock-subjects-collaborators-extension</artifactId>
<version>1.1.0</version>
<scope>test</scope>
</dependency>
OR
dependencies {
testCompile 'com.blogspot.toomuchcoding:spock-subjects-collaborators-extension:1.1.0'
}

Espresso 2.0 - Method annotated with #Test inside class extending junit3 testcase

I got a weird warning Method annotated with #Test inside class extending junit3 testcase when using the new ActivityInstrumentationTestCase2 class shipped with Espresso 2.0.
My class looks just like the one that Google provided as an example:
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.LargeTest;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static android.support.test.espresso.matcher.ViewMatchers.assertThat;
import static org.hamcrest.Matchers.notNullValue;
#RunWith(AndroidJUnit4.class)
#LargeTest
public class MyCoolActivityTests extends ActivityInstrumentationTestCase2<MyCoolActivity> {
private MyCoolActivity mActivity;
public MyCoolActivityTests() {
super(MyCoolActivity.class);
}
#Before
public void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
mActivity = getActivity();
}
#Test
public void checkPreconditions() {
assertThat(mActivity, notNullValue());
// Check that Instrumentation was correctly injected in setUp()
assertThat(getInstrumentation(), notNullValue());
}
#After
public void tearDown() throws Exception {
super.tearDown();
}
}
I've added all necessary things to the build.gradle:
android {
defaultConfig {
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
}
dependencies {
androidTestCompile 'com.android.support.test.espresso:espresso-core:2.0'
androidTestCompile 'com.android.support.test:testing-support-lib:0.1'
}
Is there any way to get this warning away?
ActivityInstrumentationTestCase2 is a JUnit 3 test case because it extends from TestCase.
#Test annotation is a replacement for the test-prefix naming convention used in JUnit 3. JUnit 4 test classes no longer require to extend TestCase or any of its subclasses. In fact JUnit 4 tests cannot extend TestCase, otherwise AndroidJUnitRunner will treat them as JUnit 3 tests.
http://developer.android.com/tools/testing-support-library/index.html#AndroidJUnitRunner
You could either migrate to ActivityTestRule provided by com.android.support.test:rules:0.4 (or later), or stick with JUnit 3.
Another option is InstrumentationRegistry, provided by Espresso 2, which has getInstrumentation(), getContext(), getTargetContext() (and more). These methods provide access to the current instrumentation, test context, and target context in a static manner. This makes it possible to write your own static utility methods for use in JUnit 4 test case classes. These utilities would mimic functionality that is currently only available in the base JUnit 3 test case classes. (This is no longer necessary.)

Categories