I am creating a application that should be able to create an account using a REST api. When I wrote the AccountController.java class everything worked fine. I used postman to verify that I got the right response.
So my AccountController.java works correctly but the unit test I wrote with it don't work. When the unit test is executed it returns a NullPointerException which results in a InternalServerError within the REST API. While debugging i found out that if I remove the use of UriInfo in AccountController.java and return Response.ok() instead of created the test is successfull.
So my question is. How do i properly test/mock a method that returns and URI?
Bellow are all relevant classes and dependencies. The Account.java class is in another package. But both the main application as the project that contains Account.java are part of the same parent pom. I am not sure if this is relevant to my question.
AccountController.java
#Stateless
#Path("/accounts")
public class AccountController {
#Inject
private AccountService accountService;
#Context
private UriInfo uriInfo;
#POST
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response create(Account account) {
final Account newAccount = accountService.create(account);
System.out.println("Pre uriinfo message");
//Removing this line and returning Response.ok() removes the exception
final URI uri = uriInfo.getAbsolutePathBuilder().path(newAccount.getId() + "").build();
System.out.println("Post uriinfo message");
return Response.created(uri).entity(newAccount).build();
}
}
AccountControllerTest.java
#RunWith(MockitoJUnitRunner.class)
public class AccountControllerTest extends JerseyTest {
#Mock
private AccountService service;
#Override
protected Application configure() {
final ResourceConfig config = new ResourceConfig(AccountController.class);
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(service).to(AccountService.class);
}
});
return config;
}
#Test
public void createUserTest() throws Exception {
final Account testAccount = new Account("testName", "test#mail.nl");
when(service.create(testAccount))
.thenReturn(testAccount);
System.out.println("Create account test");
//Create a new Account
final Response correctResult = target("/accounts")
.request(MediaType.APPLICATION_JSON)
.post(Entity.json(testAccount));
Assert.assertEquals(HTTP_CREATED, correctResult.getStatus());
Assert.assertEquals(testAccount, correctResult.readEntity(Account.class));
}
}
Account.java
#Entity
#XmlRootElement
public class Account {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(nullable = false)
private String fullName;
#Column(unique = true, nullable = false)
private String mail;
private boolean active;
public Account() { }
public Account(String fullName, String mail) {
this.fullName = fullName;
this.mail = mail;
}
//Contains getters&setters
}
Test dependencies
<!-- jersey version for "Jersey Media Json Jackson" & "Jersey Test Framework Provider Jetty" -->
<properties>
<jersey.version>2.26-b03</jersey.version>
</properties>
<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.9.0</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.test-framework.providers/jersey-test-framework-provider-jetty -->
<dependency>
<groupId>org.glassfish.jersey.test-framework.providers</groupId>
<artifactId>jersey-test-framework-provider-jetty</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.glassfish.jersey.media/jersey-media-json-jackson -->
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>${jersey.version}</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.jboss.resteasy/resteasy-jaxrs -->
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
<version>3.1.4.Final</version>
<scope>test</scope>
</dependency>
So I finally figured it out.
Somewhere on this line in AccountController.java a NullPointerException was thrown:
final URI uri = uriInfo.getAbsolutePathBuilder().path(newAccount.getId() + "").build();
This only happened when using my JerseyUnitTest so i guess Jersey must change some behavior of UriInfo.
Eventually I solved the problem by overriding equals in Account.java.
Related
I am trying to implement a reactive architecture to my Spring+MongoDB+GraphQL app, have followed some tutorials on that, but whenever I return a Publisher<Page> from my GraphQLQueryResolver - I get this error:
2022-11-17 09:18:56.316 ERROR 14166 --- [nio-8081-exec-1]
g.servlet.DefaultGraphQLErrorHandler : Error executing query
(ExceptionWhileDataFetching): Exception while fetching data (/getPages/id) : Expected
source object to be an instance of 'com.example.jobscraperspringserver.types.Page' but
instead got 'reactor.core.publisher.FluxJust'
I've played with the returned object (tried Flux<Page>, Publisher<List<Page>> and other configurations) as well as a type in the schema.graphqls, but none of them work. Besides, that's what supposed to be working in the majority of tutorials as well as some other SO issues, so I suppose that's either a bug somewhere in my code or an architectural flaw (i.e. some faulty dependency that doesn't support the type convertion).
Here is my code:
PageQuery.java
#Component
public class PageQuery implements GraphQLQueryResolver {
#Autowired
PageService pageService;
public Publisher<Page> getPages() {
return pageService.getPages();
}
}
PageService.java
#Service
public class PageService {
public Flux<Page> getPages() {
return Flux.just(new Page(12));
}
}
schema.graphqls
type Page {
id: Int!
host: String
}
type Query {
getPages: Page
}
pom.xml
...
<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!-- GraphQL -->
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-java-tools</artifactId>
<version>5.2.4</version>
</dependency>
<!-- GraphQL subscriptions -->
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<!-- MongoDB -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongodb-driver-reactivestreams</artifactId>
</dependency>
<!-- h2 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt</artifactId>
<version>0.9.1</version>
</dependency>
<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<!-- WebFlux -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.6.4</version>
</dependency>
</dependencies>
...
UPDATE
Apparently com.graphql-java doesn't handle WebFlux, so I've replaced
<dependency>
<groupId>com.graphql-java</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>5.0.2</version>
</dependency>
with
<dependency>
<groupId>org.springframework.experimental</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>1.0.0-M3</version>
</dependency>
but now I am getting a null instead of a Page...
UPDATE 2
I've also tried the
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-spring-boot-starter</artifactId>
<version>11.1.0</version>
</dependency>
<dependency>
<groupId>com.graphql-java-kickstart</groupId>
<artifactId>graphql-kickstart-spring-webflux</artifactId>
<version>11.1.0</version>
</dependency>
but got the same result. I figured it supports a CompletableFuture<List<Page>>, but this is still blocking and I've seen multiple tutorials on returning Mono/Flux from a graphql resolver...
UPDATE 3
I've updated the all the Spring modules' versions to 2.6.3 to be consistent and I've also removed the spring-boot-starter-web dependency, as the spring-boot-starter-webflux should be sufficient; but neither of those fixes have solved my issue.
UPDATE 4
This is the entity I want to return wrapped in a Flux:
#Document("pages")
public class Page {
#Id
private int id;
private String host;
private String path;
private String jobAnchorSelector;
private String jobLinkContains;
private int numberOfPages;
private int interval;
private Date lastScrapePerformed;
private String userUuid;
public Page() {
}
public Page(int id) {
this.id = id;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getJobAnchorSelector() {
return jobAnchorSelector;
}
public void setJobAnchorSelector(String jobAnchorSelector) {
this.jobAnchorSelector = jobAnchorSelector;
}
public String getJobLinkContains() {
return jobLinkContains;
}
public void setJobLinkContains(String jobLinkContains) {
this.jobLinkContains = jobLinkContains;
}
public int getNumberOfPages() {
return numberOfPages;
}
public void setNumberOfPages(int numberOfPages) {
this.numberOfPages = numberOfPages;
}
public String toString() {
return "Page [" + host + path + "]";
}
public int getInterval() {
return interval;
}
public void setInterval(int interval) {
this.interval = interval;
}
public Date getLastScrapePerformed() {
return lastScrapePerformed;
}
public void setLastScrapePerformed(Date lastScrapePerformed) {
this.lastScrapePerformed = lastScrapePerformed;
}
public String getUserUuid() {
return userUuid;
}
public void setUserUuid(String userUuid) {
this.userUuid = userUuid;
}
}
It's a MongoDB document and I'm using a ReactiveMongoRepository to fetch that, however for the sake of simplifying this issue I was trying to return just a Flux.just(new Page(2)).
First of all, if you're using Spring Boot, I would recommend that you use spring-boot-starter-graphql instead of graphql-spring-boot-starter, and you need to add spring-boot-starter-webflux to implement reactive data fetchers. Also if you want to return Flux, you should define returned type as an array in the schema (e.g. getPages: [Page!]!), otherwise, you should return Mono.
This will allow you to implement your query like this:
#Controller
public class PageController {
// ...
#QueryMapping
public Flux<Page> getPages() {
return pageService.getPages();
}
}
But you should note that by default Mono and Flux are adapted to a CompletableFuture where Flux values are aggregated and turned into a List. If you want to stream responses, then you should also use GraphQL subscription instead of a query.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
Class for which I m writing Junits:
public class AImpl implements AInterface {
public String method1(String id) throws Exception {
String s = Service.Factory.getInstance().generate(id);
return s;
}
}
Interface to be instantiated using its Inner class:
public interface Service {
String generate(String var1) throws Exception;
public static final class Factory {
private static Service instance = null;
public Factory() {
}
public static final Service getInstance() {
if (instance == null) {
instance = (Service)EnterpriseConfiguration.getInstance().loadImplementation(Service.class);
}
return instance;
}
}
}
I have tried powerMockito but it is not working.
#Test
public void generateTest() throws Exception {
Service.Factory innerClassMock = mock(Service.Factory.class);
String id= "id";
whenNew(Service.Factory.class).withArguments(anyString()).thenReturn(innerClassMock);
whenNew(innerClassMock.getInstance().generate("hjgh")).withAnyArguments().thenReturn(id);
id= AImpl.generate("hjgh");
Assert.assertEquals("id", id);
}
If I understand well your not cleary code you need this junit:
#RunWith(PowerMockRunner.class)
#PrepareForTest({Service.Factory.class})
public class AImplTest {
private Service serviceMock;
#Before
public void setUp() {
PowerMockito.mockStatic(Service.Factory.class);
serviceMock = Mockito.mock(Service.class);
PowerMockito.when(Service.Factory.getInstance()).thenReturn(serviceMock);
}
#Test
public void generateTest() throws Exception {
Mockito.doReturn("mockid").when(serviceMock).generate(Mockito.anyString());
Assert.assertEquals("mockid", new AImpl().method1("aaa"));
}
}
Here my dependencies:
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>2.28.2</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-module-junit4 -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.4</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito2 -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.4</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-core -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>2.0.4</version>
<scope>test</scope>
</dependency>
Having said that I would change your Service.Factory class: here you need a private constructor in order to implement correctly the singleton pattern.
I am trying to test one test case using controller class with service interface and interface impl class. But it's always failing and returning either null pointer exception or not found custom exception even mocking the method to return mock values.
Controller.class
#RestController
public class RestAPIController {
#Autowired
RestAPIService restAPIService;
#PostMapping(path = "list/{id}")
public ResponseEntity<List<Employee>> findByID(#PathVariable(value = "id") String id)
throws RestAPIException {
List<Employee> list = restAPIService.findByID(id);
if (list == null || list.isEmpty()) {
throw new IDNotFoundException(id);
}
return new ResponseEntity<>(list, HttpStatus.OK);
}
Service Interface:
#Service
public interface RestAPIService {
public List<Employee> findByID(#Param("id") String id) throws RestAPIException;
}
Service impl class:
#Service
public class RestAPIServiceImpl implements RestAPIService {
#Override
public List<Employee> findByID(String id) throws RestAPIException {
return serviceUtils.transformationObj(repository.findByID(id));
}
}
Test Class
#Mock
RestAPIService restAPIServiceImpl;
#InjectMocks
RestAPIController restAPIController;
#Test
public void testListByIDController() throws RestAPIException {
Mockito.when(restAPIServiceImpl.findByID("1")).thenReturn(baseDTOData());
ResponseEntity<List<Employee>> expectedList = restAPIController.findByID("1");
assertEquals(1, expectedList.getBody().size());
}
private List<Employee> baseDTOData() {
List<Employee> list = new ArrayList<>();
Employee e = new Employee();
e.setId("2");
list.add(e);
return list;
}
pom.xml (using JUnit5)
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<!-- exclude junit 4 -->
<exclusions>
<exclusion>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Junit 5 -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.platform</groupId>
<artifactId>junit-platform-launcher</artifactId>
<scope>test</scope>
</dependency>
<!-- Mockito extention -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
The above code is failing and enters into IDNotFoundException but mock return is not working.
Can anyone please help on this.
I have a problem with sqlite3, hibernate and spring.In fact, if I start the program and call the createType () method that creates a type and after that I call the getAllTypes () method I have the values of the object I just created appear in the browser.But if I check with a "select * from type" in my table type to see if the object was added in my table, I have no record. In addition, as soon as I restart the program and I call the method getAllTypes () to view the data of my table I have nothing that appears in my browser.Again, for each startup looks like the ID is reset since it starts at 1 (for example if after starting my program I try to insert 2 objects with the method createType () I have: ID = 1 and ID = 2 which appears in my browser). So if my data is not saved in my table where have they been? I made 2 recordings manually in my table and I also can not get these 2 records with the methods getAllTypes () which is supposed to send me all the records of my table. I'm told that there may be a communication problem between my program and my database.Thank you in advance for your help!!!
This is how I call my methods in browsers:
http://localhost:8080/types/getAllTypes
http://localhost:8080/types/insert
application.yml
spring:
profile:dev
jpa:
hibernate:
hbm2ddl.auto:update
properties:
hibernate:
dialect:org.hibernate.dialect.SQLiteDialect
datasource:
url:jdbc:sqlite:C:\Users\user pc\Desktop\PDF\testrest.db
username:user
password:password
driverClassName:org.sqlite.JDBC
My dependency in my pom.xml for the configuration of sqlite,hibernate and spring :
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
<type>pom</type>
</parent>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.2.12.Final</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.20.1</version>
</dependency>
<dependency>
<groupId>com.zsoltfabok</groupId>
<artifactId>sqlite-dialect</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.196</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.2.12.Final</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
<version>4.0</version>
</dependency>
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.0</version>
<dependency>
My table type:
CREATE TABLE IF NOT EXISTS type(
idType integer NOT NULL PRIMARY KEY AUTOINCREMENT,
description varchar(256) NOT NULL
)
My entities type:
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
#Entity
#Table(name="typedifficulte")
public class Type {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long idType;
private String description;
public Type() {
}
public Type(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public long getIdType() {
return idType;
}
public void setIdType(long idType) {
this.idType = idType;
}
}
My interface TypeService :
public interface TypeService {
Collection<Type> getAllTypes();
Type createType(Type type);
}
My TypeServiceImpl :
import javax.annotation.Resource;
import org.apache.commons.collections4.IteratorUtils;
import org.springframework.stereotype.Service;
#Service(value="typeService")
public class TypeServiceImpl implements TypeService {
#Resource
private TypeRepository typeRepository;
#Override
public Collection<Type> getAllTypes() {
return IteratorUtils.toList(this.typeRepository.findAll().iterator());
}
#Override
public Type createType(Type type) {
return this.typeRepository.save(type);
}
public TypeRepository getTypeRepository() {
return typeRepository;
}
public void setTypeRepository(TypeRepository typeRepository) {
this.typeRepository = typeRepository;
}
}
My TypeRepository :
public interface TypeRepository extends CrudRepository<Type,Long> {
Type findByDescription(String description);
}
My Controller :
#RestController
#RequestMapping(value ="/types")
public class TypeController {
#Resource
private TypeService typeService;
#RequestMapping(value= "/getAllTypes", method = RequestMethod.GET)
public Collection<Type> getAllTypes() {
return this.typeService.findAll();
}
#RequestMapping(value = "/insert",method = RequestMethod.GET)
public Type createType() {
return this.typeService.createType(new Type("test"));
}
}
And finally my startup class :
#Configuration
#EnableAutoConfiguration
#ComponentScan
public class MainLauncher {
public static void main(String[] args) {
SpringApplication.run(MainLauncher.class, args);
}
}
Posting x-www-form-urlencode data to Spring.
This works:
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void sumbitFUD(#RequestBody final MultiValueMap<String, String> formVars) {
}
This on the other hand, gives an exception:
Content type 'application/x-www-form-urlencoded;charset=UTF-8' not
supported
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void sumbitFUD(#RequestBody Fud fud) {
}
And this results in all fields on the bean being null:
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public void sumbitFUD(Fud fud) {
//not using #RequestBody
}
FUD:
public class Fud {
public String token_id;
public String team_doamin;
public String channel_id;
public String user_id;
public String user_name;
public String command;
public String text;
public String response;
}
Form data:
token%abc=&team_id%3DT0001=&team_domain%3Dexample=&channel_id%3DC2147483705=&channel_name%3Dtest=&user_id%3DU2147483697=&user_name%3DSteve=&command%3D%2Fweather=&text%3D94070=&response_url%3Dhttps=%2F%2Fhooks.slack.com%2Fcommands%2F1234%2F5678
POM:
<dependencies>
<dependency>
<groupId>net.gpedro.integrations.slack</groupId>
<artifactId>slack-webhook</artifactId>
<version>1.3.0</version>
</dependency>
<!-- Spring Frameworks for Web, MVC and Mongo -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<scope>test</scope>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
I see two problems here.
The use of the #RequestBody annotation. It, or more precisely its handler - a subclass of the HttpMessageConverter, can't handle these cases. You should deal with the #ModelAttribute instead.
The absence of setters. Spring can't set values, that come in, to a target instance without setters. I do not know whether there is a property to operate directly with fields, but I would recommend avoiding that. Make the fields private.