Exception while using spock for java unit testing - java

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'
}

Related

Fail to load ApplicationContext SpringBoot with SpringSecurity and JUnit Jupiter

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);
// ...
}
// ...
}

#MockBean is not returning the expected object

below is my controller:
#RestController
#RequestMapping("/Employee")
public class Employeecontroller {
#Autowired
Employeeservice empservice;
#PostMapping("/addEmployee") // adding emp details into the table
public Employee addemployee(#RequestBody Employee emp)
{
empservice.saveemp(emp);
return emp;
}
}
this is the empservice class:
#Service
public class Employeeservice {
#Autowired
EmployeeRepository emprepo;
public Employee saveemp(Employee emp) {
System.out.println("inside save emp");
return emprepo.save(emp);
}
}
(here i dont want to call to emprepo.save(emp) method, which is a database call, so i used Mockito.when and then methods in below test class)
below is the test class:
#SpringBootTest
#RunWith(MockitoJUnitRunner.class)
#AutoConfigureMockMvc
class RestServiceApplicationTests {
#Autowired
private MockMvc mvc;
#Autowired
ObjectMapper objectMapper;
#MockBean
Employeeservice empservice;
Employee reqemp = new Employee("Bob", "java");
#Test
public void testaddemp() throws Exception {
when(empservice.saveemp(reqemp)).thenReturn(new Employee(1, "Bob", "java"));
RequestBuilder request = MockMvcRequestBuilders.post("/Employee/addEmployee")
.contentType(MediaType.APPLICATION_JSON).content(objectMapper.writeValueAsString(reqemp));
MockHttpServletResponse returnedemp = (MockHttpServletResponse) mvc.perform(request).andExpect(status().isOk())
.andReturn().getResponse();
Employee expectedemp = new Employee(1, "Bob", "java");
assertEquals(objectMapper.writeValueAsString(expectedemp), returnedemp.getContentAsString());
}
}
Testcase failed with:
org.opentest4j.AssertionFailedError: expected: <{"id":1,"name":"Bob","tech":"java"}> but was: <{"id":0,"name":"Bob","tech":"java"}>
at org.junit.jupiter.api.AssertionUtils.fail(AssertionUtils.java:55)
at org.junit.jupiter.api.AssertionUtils.failNotEqual(AssertionUtils.java:62)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:182)
at org.junit.jupiter.api.AssertEquals.assertEquals(AssertEquals.java:177)
at org.junit.jupiter.api.Assertions.assertEquals(Assertions.java:1124)
at com.easytocourse.demo.RestServicewithActuatorApplicationTests.testaddemp(RestServicewithActuatorApplicationTests.java:55)
when i use #Mock or #SpyBean it is returning expected employee object
Please help me in understanding why #MockBean is not working?
Please clarify the below points
1. what is the difference between #Mock, #MockBean, #SpyBean, #InjectMock annotations, when to use these annotations?
#MockBean is a feature of Spring Boot Test.
Thus you need to remove the #RunWith(MockitoJUnitRunner.class) declaration which is using Mockito's JUnit 4 Runner.
If you want to use JUnit 4, you must use the SpringRunner via #RunWith(SpringRunner.class).
If you want to use JUnit Jupiter (part of JUnit 5), you'll need to use the SpringExtension via #ExtendWith(SpringExtension.class).
Depending on the version of Spring Boot Test that you are using, you may be able to exclude the #ExtendWith(SpringExtension.class) declaration, since recent versions of #SpringBootTest automatically register the SpringExtension for you.
Related topic: Difference between #Mock, #MockBean and Mockito.mock()

unit test a interface implementation with mock in Spring Boot

I'm trying to write a simple unit test for a service in Spring Boot.
The service calls a method on a repository which returns an instance of User.
I'm trying to mock the repository, because I want to test only the service.
So, the code for Repository:
public interface UserRepository extends MongoRepository<User, String> {
User findByEmail(String email);
}
Service interface:
public interface UserService {
#Async
CompletableFuture<User> findByEmail(String email) throws InterruptedException;
}
Service implementation:
#Service
public class UserServiceImpl implements UserService {
private UserRepository userRepository;
// dependency injection
// don't need Autowire here
// https://docs.spring.io/spring-boot/docs/current/reference/html/using-boot-spring-beans-and-dependency-injection.html
public UserServiceImpl(UserRepository userRepository) {
this.userRepository = userRepository;
}
#Async
public CompletableFuture<User> findByEmail(String email) throws InterruptedException {
User user = userRepository.findByEmail(email);
return CompletableFuture.completedFuture(user);
}
}
Unit Test:
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserServiceTest {
#InjectMocks
UserService userService;
#Mock
UserRepository mockUserRepository;
#Before
public void setUp() {
MockitoAnnotations.initMock(this);
}
#Test
public void mustReturnUser() throws InterruptedException {
String emailTest = "foo#bar.com";
User fakeUser = new User();
fakeUser.setEmail(emailTest);
when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser);
User user = userService.findByEmail(emailTest).join();
assertThat(user).isEqualTo(fakeUser);
verify(mockUserRepository).findByEmail(emailTest);
}
}
When I run this test, I got a MockitoException:
org.mockito.exceptions.base.MockitoException:
Cannot instantiate #InjectMocks field named 'userService'.
...
Caused by: org.mockito.exceptions.base.MockitoException: the type 'UserService' is an interface.
Instead of using the interface, I tried to use the real implementation; changing the test like this:
#InjectMocks
UserServiceImpl userService;
Now, the test passes with success, but this don't appear be right (at least for me).
I like to test the UserService that Spring Boot is using (suppose that in a new version of my system, I implement a new UserServicePostgreSQLImpl - now I'm using MongoDB).
(edit: see the bottom edit in the question)
I changed the Unit Test as follows:
#Autowired
#InjectMocks
UserService userService;
but now I got a test failure:
Expected :model.User#383caf89
Actual :null
For some reason, when I use #Autowired, the UserRepository mock doesn't work.
If I change the emailTest to use a real email in my database,
the test passes.
When I use #Autowired,
the test is using the real UserRepository and not a Mock version of UserRepository.
Any help?
Edit: looking at the answers from #msfoster and #dunni, and thinking better, I believe that the correct way is to test every implementation (in my example, use UserServiceImpl userService).
In order for your UserServiceImpl to be autowired when annotating it with #InjectMocks then it needs to registered as a Spring bean itself. You can do this most simply by annotating your UserServiceImpl class with #Service.
This will ensure it is picked up by the component scan in your Spring boot configuration. (As long as the scan includes the package your service class is in!)
You are running your tests with SpringRunner but for mocks you don't really need spring context. Try following code
// Using mockito runner
#RunWith(MockitoJUnitRunner.class)
public class UserServiceTest {
#Mock
UserRepository mockUserRepository;
// Mockito will auto inject mockUserRepository mock to userService via constructor injection
#InjectMocks
UserService userService;
#Test
public void mustReturnUser() throws InterruptedException {
String emailTest = "foo#bar.com";
User fakeUser = new User();
fakeUser.setEmail(emailTest);
when(mockUserRepository.findByEmail(emailTest)).thenReturn(fakeUser);
User user = userService.findByEmail(emailTest).join();
assertThat(user).isEqualTo(fakeUser);
verify(mockUserRepository).findByEmail(emailTest);
}
}
This is just a variation on the #Yogesh Badke answer.
Although you are using spring at runtime,
there is no need to use spring during the unit test.
Instead,
you can mock all the dependencies and set them to the mocks during test setup
(using reflection or setters, if you have them).
Here is some example code:
import org.springframework.test.util.ReflectionTestUtils;
public class TestUserService
{
private static final String VALUE_EMAIL = "test email value";
private UserService classToTest;
#Mock
private User mockUser;
#Mock
private UserRepository mockUserRepository;
#Before
public void beforeTest()
{
MockitoAnnotations.initMock(this);
classToTest = new UserService();
doReturn(mockUser).when(mockUserRepository).findByEmail(VALUE_EMAIL);
ReflectionTestUtils.setField(
classToTest,
"userRepository",
mockUserRepository);
}
#Test
public void findByEmail_goodEmailInput_returnsCorrectUser()
{
final User actualResult;
actualResult = classToTest.findByEmail(VALUE_EMAIL);
assertSame(
mockUser,
actualResult);
}
}
If interface is implemented by more than one class, then use the qualifier name (example below) in Junit beans.xml file to run the respective Junit test case.
Example:
#Autowired
#Qualifier("animal")
private Animal animals;
In Junit beans.xml
<bean id="animal" class="com.example.abc.Lion"/>
where Lion is the implementation class for the Interface Animal.
You need to #InjectMocks for the implementation class. Not the interface class.
Example:
#RunWith(SpringRunner.class)
#SpringBootTest
public class UserServiceTest {
#Mock
UserRepository mockUserRepository;
#InjectMocks
UserServiceImpl userServiceImpl; ------> This is important
}

Spring Boot Unit Test

I am new to spring boot. Need some suggestions
Here my unit test class
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = DemoApplication.class)
public class EmployeeRepositoryTest {
#Autowired
protected EmployeeRepository employeeRepository;
#Test
public void insertEmploee(){
Employee employee = new Employee();
employee.setEmpName("Azad");
employee.setEmpDesignation("Engg");
employee.setEmpSalary(12.5f);
employeeRepository.save(employee);
}
}
When I run it I get exception as
java.lang.NoSuchMethodError: org.springframework.core.annotation.AnnotatedElementUtils.findMergedAnnotationAttributes(Ljava/lang/reflect/AnnotatedElement;Ljava/lang/String;ZZ)Lorg/springframework/core/annotation/AnnotationAttributes;
at org.springframework.test.util.MetaAnnotationUtils$AnnotationDescriptor.<init>(MetaAnnotationUtils.java:290)
at org.springframework.test.util.MetaAnnotationUtils$UntypedAnnotationDescriptor.<init>(MetaAnnotationUtils.java:365)
at org.springframework.test.util.MetaAnnotationUtils$UntypedAnnotationDescriptor.<init>(MetaAnnotationUtils.java:360)
at org.springframework.test.util.MetaAnnotationUtils.findAnnotationDescriptorForTypes(MetaAnnotationUtils.java:191)
at org.springframework.test.util.MetaAnnotationUtils.findAnnotationDescriptorForTypes(MetaAnnotationUtils.java:198)
at
Process finished with exit code -1
It seems that your problem is solved (mixing the Spring dependency versions) but let me just expand the comment from #g00glen00b on how to write unit tests.
Make sure the following dependency is in your pom.xml:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
As pointed out in the comment, #RunWith(SpringJUnit4ClassRunner.class) causes the unit test to start the whole application and it is used rather for integration testing.
Fortunately, Spring-boot has built in dependency for Mockito which is just what you need for unit tests like this.
Now, your unit test could look something like this:
public class EmployeeRepositoryTest {
#InjectMocks
private EmployeeRepository employeeRepository;
#Mock
private Something something; // some class that is used inside EmployRepository (if any) and needs to be injected
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
#Test
public void insertEmploee(){
Employee employee = new Employee();
employee.setEmpName("Azad");
employee.setEmpDesignation("Engg");
employee.setEmpSalary(12.5f);
employeeRepository.save(employee);
Mockito.verify(...); // verify what needs to be verified
}
}
Nice post about using Mockito can be found, for example, here.
Instead of using #Autowired on EmployeeRepository we can use #MockBean cause we are writing unit tests we don't need to deal with the real data we just need to verify that the function is working fine or not. Check the below code
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = Main.class)//main springboot class
#WebAppConfiguration
public abstract class AbstractBaseTest {
protected MockMvc mvc;
#Autowired
WebApplicationContext webApplicationContext;
protected void setUp() {
mvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
}
public class EmployeeRepositoryTest extends AbstractBaseTest{
#MockBean
protected EmployeeRepository employeeRepository;
#Override
#Before
public void setUp() {
super.setUp();
}
#Test
public void insertEmploee(){
Employee employee = new Employee();
employee.setEmpName("Azad");
employee.setEmpDesignation("Engg");
employee.setEmpSalary(12.5f);
Mockito.doNothing().when(employeeRepository).save(Mockito.any(Employee.class));
employeeRepository.save(employee);
Mockito.verify(employeeRepository, Mockito.times(1)).save(employee);
}
}

Unit testing with MockServletContext

I have set up spring boot application using Gradle. Now I do understand that #EnableAutoConnfiguration configures the application based on dependencies in a class path. I am pretty happy to avoid all of the plumbing but things start happening which I wish wouldn't.
Here are my dependencies:
dependencies {
compile('org.springframework.boot:spring-boot-starter-web:1.2.3.RELEASE')
compile 'org.springframework.hateoas:spring-hateoas:0.17.0.RELEASE'
compile 'org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE'
compile 'org.springframework.boot:spring-boot-starter-data-jpa'
compile 'com.google.guava:guava:18.0'
compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
compile 'commons-beanutils:commons-beanutils:1.9.2'
runtime 'org.hsqldb:hsqldb:2.3.2'
testCompile 'org.springframework.boot:spring-boot-starter-test'
testCompile 'com.jayway.jsonpath:json-path:2.0.0'
}
My application class:
#ComponentScan("org.home.project")
#SpringBootApplication
//#EnableHypermediaSupport(type = EnableHypermediaSupport.HypermediaType.HAL)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
A snippet from UserController:
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
public HttpEntity<ResourceSupport> create(#Valid #RequestBody UserCreateRequest ucr, BindingResult bindingResult) {
if (bindingResult.hasErrors()) throw new InvalidRequestException("Bad Request", bindingResult);
Long userId = userService.create(ucr);
ResourceSupport resource = new ResourceSupport();
resource.add(linkTo(UserEndpoint.class).withSelfRel());
resource.add(linkTo(methodOn(UserEndpoint.class).update(userId, null, null)).withRel(VIEW_USER));
resource.add(linkTo(methodOn(UserEndpoint.class).delete(userId)).withRel(DELETE_USER));
return new ResponseEntity(resource, HttpStatus.CREATED);
}
The UserController.java has two annotations:
#RestController
#RequestMapping(value = "/users", produces = MediaType.APPLICATION_JSON_VALUE)
First of - notice the commented out #EnableHyperdiaSupport annotation - links in the ResourceSupport instance are still serialized to hal+json format despite media type produced and media type set in the request. This happens automatically when 'org.springframework.plugin:spring-plugin-core:1.2.0.RELEASE' is introduced in the dependencies. How would one go about configuring it explicitly ?
Another issue are unit tests.
This passes:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = MockServletContext.class)
#WebAppConfiguration
public class UserControllerTest {
...ommited for brevity...
#InjectMocks
private UserController testObject;
#Before
public void setUp() throws Exception {
initMocks(this);
mockMvc = standaloneSetup(testObject).build();
}
#Test
public void testUserCreatedLinks() throws Exception {
mockMvc.perform(post("/users").contentType(MediaType.APPLICATION_JSON).content(data))
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON)).andExpect(jsonPath("$.links.[*].rel", hasItem("self")));
}
...ommited fro brevity...
}
The post request returns a standard JSON response in the test - not HAL+JSON. Is there a way to reconfigure this so that unit testing #RestController with MockServletContext would produce HAL+JSON or getting back to problem number 1 - how to configure the response format explicitly so that Jackson serializer would not produce hal+json ?
You're running your test using Spring MVC Test's standaloneSetup which uses a bare minimum of configuration to get your controller up and running. That configuration isn't the same as the configuration that will be used when you run the whole application.
If you want to use the same configuration, you could use webAppContextSetup:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class SomeTests {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
}
Alternatively, you can replicate Spring HATEOAS's configuration in the standalone setup. Note that this runs the risk of your tests' configuration deviating from your application's configuration. You'd create the MockMvc instance like this:
TypeConstrainedMappingJackson2HttpMessageConverter messageConverter =
new TypeConstrainedMappingJackson2HttpMessageConverter(ResourceSupport.class);
messageConverter.setSupportedMediaTypes(Arrays.asList(MediaTypes.HAL_JSON));
ObjectMapper objectMapper = messageConverter.getObjectMapper();
objectMapper.registerModule(new Jackson2HalModule());
objectMapper.setHandlerInstantiator(
new Jackson2HalModule.HalHandlerInstantiator(new DefaultRelProvider(), null));
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(testObject)
.setMessageConverters(messageConverter).build();

Categories