Unit Testing Rest Services with Spring Boot and JUnit - rest

I have a basic SpringBoot app. using Spring Initializer, JPA, embedded Tomcat, Thymeleaf template engine, and package as an executable JAR file. I've defined this Rest method to get a User
#GetMapping(path = "/api/users/{id}",
consumes = "application/json",
produces = "application/json")
public ResponseEntity<User> getUser
(HttpServletRequest request,
#PathVariable long id) {
User user = checkAccess(request, id);
return ResponseEntity.ok(user);
}
I've created this Junit to test it
#ContextConfiguration(classes={TestSystemConfig.class})
#RunWith(SpringRunner.class)
#WebMvcTest(UserResourceController.class)
public class UserResourceControllerTests {
#Autowired
private MockMvc mvc;
#MockBean
private UserResourceController UserResourceController;
#Test
public void getUser() throws Exception {
mvc.perform(get("/api/users/1")
.with(user("pere.peris#gmail.com").password("password"))
.contentType(APPLICATION_JSON))
.andExpect(status().isOk());
}
}
But I got this error when I run the test:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.IllegalArgumentException: Name for argument type [long] not available, and parameter name information not found in class file either.
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at org.springframework.test.web.servlet.TestDispatcherServlet.service(TestDispatcherServlet.java:71)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.springframework.mock.web.MockFilterChain$ServletFilterProxy.doFilter(MockFilterChain.java:166)
at org.springframework.mock.web.MockFilterChain.doFilter(MockFilterChain.java:133)
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:320)

The reason is because you are mocking your controller. This is not necessary when you have #WebMvcTest(UserResourceController.class)
This should work.
#ContextConfiguration(classes={TestSystemConfig.class})
#RunWith(SpringRunner.class)
#WebMvcTest(UserResourceController.class)
public class UserResourceControllerTests {
#Autowired
private MockMvc mvc;
#Test
public void getUser() throws Exception {
mvc.perform(get("/api/users/1")
.with(user("pere.peris#gmail.com").password("password"))
.contentType(APPLICATION_JSON))
.andExpect(status().isOk());
}
}

Related

Using embedded MongoDB in Spring JUnit #WebMvcTest

I'm currently using MongoDB in my Spring application. Since I added Mongo my endpoint tests no longer work due to the following error:
No qualifying bean of type 'xxx' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
The repository which I Autowire in a controller is as follows:
private final RuleRepository ruleRepository;
#Autowired
public TestController(RuleRepository ruleRepository) {
this.ruleRepository = ruleRepository;
}
I assume that this has to do with Mongo and the fact that I currently use AutoConfiguration for it. For the test I added the Flapdoodle Embed Mongo dependency, which seems to be used for testing purposes in a lot of examples, to my pom.xml with the scope set to test:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>2.0.3</version>
<scope>test</scope>
</dependency>
My test class looks like this:
RunWith(SpringRunner.class)
#WebMvcTest(value = RouteController.class, secure = false)
#ActiveProfiles("test")
public class TestControllerEndpointTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private RuleRepository ruleRepository;
#Before
public void setupTests() {
//Setup for the tests
}
//Actual tests
}
I also created a Configuration class for the Mongo Test Database, but I don't know how to correctly register it:
#Configuration
#Profile("test")
public class TestMongoConfig {
#Autowired
private MongoProperties properties;
#Autowired(required = false)
private MongoClientOptions options;
#Bean(destroyMethod = "close")
public Mongo mongo(MongodProcess mongodProcess) throws IOException {
Net net = mongodProcess.getConfig().net();
return new MongoClient(net.getServerAddress().getHostName(), net.getPort());
}
#Bean(destroyMethod = "stop")
public MongodProcess mongodProcess(MongodExecutable mongodExecutable) throws IOException {
return mongodExecutable.start();
}
#Bean(destroyMethod = "stop")
public MongodExecutable mongodExecutable(MongodStarter mongodStarter, IMongodConfig iMongodConfig) throws IOException {
return mongodStarter.prepare(iMongodConfig);
}
#Bean
public IMongodConfig mongodConfig() throws IOException {
return new MongodConfigBuilder().version(Version.Main.PRODUCTION).build();
}
#Bean
public MongodStarter mongodStarter() {
return MongodStarter.getDefaultInstance();
}
}
How do I get an endpoint test that is annotated with #WebMvcTest to use the embedded Mongo database?
After banging our heads for a while we found the #AutoConfigureDataMongo annotation.
import org.springframework.boot.test.autoconfigure.data.mongo.AutoConfigureDataMongo;
#RunWith(SpringRunner.class)
#WebMvcTest(value = SampleController.class, secure = false)
#AutoConfigureDataMongo
public class SampleControllerTest {
Just annotate your controller with it and you should see org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongo logs when running this controller tests.

ApplicationContext Exception in Test with #WebMvcTest

I Have an Spring Boot Application (1.5.10.RELEASE) which contains a main (SpringBootApplication) like this:
#SpringBootApplication
#Configuration
#EntityScan(basePackages = { "db.modell", "db.modell.base" })
#ComponentScan(basePackages = { "de.gui.test" })
public class SpringBootConsoleApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(SpringBootConsoleApplication.class, args);
}
}
and two REST controllers like the following:
#RestController
#RequestMapping("/as")
public class AController {
#Autowired
private ARepository aRepository;
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<Collection<A>> getAs() {
return new ResponseEntity<>(orgtFarbeRepository.findAll(), HttpStatus.OK);
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
public ResponseEntity<A> getA(#PathVariable long id) {
A a = ARepository.findOne(id);
if (party != null) {
return new ResponseEntity<>(ARepository.findOne(id), HttpStatus.OK);
} else {
return new ResponseEntity<>(null, HttpStatus.NOT_FOUND);
}
}
}
Furthermore I have a single test like this:
#RunWith(SpringRunner.class)
#WebMvcTest(AController.class)
public class AControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private ARepository ARepository;
#Test
public void firstTest() throws Exception {
A a = new aFarbe();
a.set....
when(ARepository.findAll()).thenReturn(Collections.singleton(a));
mvc.perform(
get("/as")
.accept(MediaType.APPLICATION_JSON_UTF8_VALUE)
)
.andExpect(status().isOk());
}
}
The repositories look like this:
public interface ARepository extends CrudRepository<A, Long>
{
Collection<A> findAll();
}
public interface BRepository extends CrudRepository<B, Long>
{
Collection<B> findAll();
}
A and B them self are JPA annotated classes. The whole application contains access to a database..
Furthermore I have a Service like this:
#Service
public class XService {
private static final Logger LOGGER = LoggerFactory.getLogger(XService.class);
#Autowired
private ARepository aRepository;
#Autowired
private BRepository bRepository;
...
}
The XService is not used via #Autowire or so (Just need to remove that):
So I try to run the AControllerTest I get the following error:
java.lang.IllegalStateException: Failed to load ApplicationContext at
org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
.. .. at
org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:206)
Caused by:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'XService': Unsatisfied dependency
expressed through field 'BRepository'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'BRepository' available: expected at least 1
bean which qualifies as autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
.. .. at
org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:120)
at
org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:98)
at
org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:116)
... 26 more Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'BRepository' available: expected at least 1
bean which qualifies as autowire candidate. Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1493)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
at
org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
at
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
... 44 more
My assumption is that during the test more context is started than it should. The question is how can I prevent that? Which means only to start the context for the AControler and nothing more? I thought that based on the #WebMvcTest(AController.class) it should be limited already which looks like that it was not the case...
The referenced answer does not really answered my question but a in the context a hint gave me the solution. This means in consequence to add the following to my test:
so I have to add #OverrideAutoConfiguration(enabled=true):
#RunWith(SpringRunner.class)
#WebMvcTest(OrgtFarbenController.class)
#OverrideAutoConfiguration(enabled=true)
public class AControllerTest {
...
}

Inject EntityManager in SwitchYard Junit implementation

I am trying to implement Junit in SwitchYard Application.
i am using JPA , without using Camel. i have persistence.xml with the following details. And i am using resource producer pattern to expose EntityManager.
But when i am testing a service, i am getting null Invocation for EntityManager in DAO layer.
Is there any way , i can mock or inject EntityManager in SwitchYard Junit
#RunWith(SwitchYardRunner.class)
#SwitchYardTestCaseConfig(config = SwitchYardTestCaseConfig.SWITCHYARD_XML, mixins = {
CDIMixIn.class, HTTPMixIn.class, NamingMixIn.class })
public class SalesModuleServiceTest {
private SwitchYardTestKit testKit;
private CDIMixIn cdiMixIn;
private HTTPMixIn httpMixIn;
private static NamingMixIn namingMixIn;
private TransformerRegistry transformerRegistry;
#ServiceOperation("SalesModuleService")
private Invoker service;
//------ JUnit test with REST binding fails if no resteasy properties defined ------
#BeforeDeploy
public void setProperties()
{
System.setProperty("org.switchyard.component.resteasy.standalone.port", "8081");
System.setProperty("org.switchyard.component.resteasy.standalone.path", "");
}
#Test
public void testUpdateCustomerStatus() throws Exception {
SalesDetailsRequest message = null;
BudgetResponse<?> result = service.operation("updateCustomerStatus")
.sendInOut(message).getContent(SalesResponse.class);
// validate the results
Assert.assertTrue("Implement me", false);
}
}

Spring restful service test case fail HTTP status is 500

I want to implement test case for spring restful web services which return a json
MY controller test class is :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebAppContext.class,JpaTestConfiguration.class
})
#WebAppConfiguration
public class DominProfileRestControllerTest {
private MockMvc mockMvc;
#Autowired
private WebApplicationContext webApplicationContext;
private MediaType contentType = new MediaType(MediaType.APPLICATION_JSON.getType(),
MediaType.APPLICATION_JSON.getSubtype(),
Charset.forName("utf8"));
#Before
public void setUp() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
#Test
public void testGetDomainProfile() throws Exception {
String profileId = domainProfile.getId().toString();
System.out.print("testing restful"+profileId);
mockMvc.perform(get("/service/domainprofile/get/{id}", profileId) )
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.city", is("Chandigrah")));
/* mockMvc.perform(get("/service/domainprofile/get/{id}",profileId).accept(MediaType.TEXT_PLAIN))
.andExpect(status().isOk())
.andExpect(content().contentType("text/plain;charset=ISO-8859-1"))
.andExpect(content().string("hello Prashant"));
*/
}
My Controller class Is :
#RestController
#RequestMapping("/service/domainprofile")
public class DominProfileRestController {
#Autowired
private JpaDomainProfileRepository jpaDomainProfileRepository;
#RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
public DomainProfileResource getDomainProfile(#PathVariable String id) {
JpaDomainProfile domainProfile = jpaDomainProfileRepository.findOne(Long.valueOf(id));
DomainProfileResource domainProfileResource = new DomainProfileResource();
System.out.println("domainProfile.getCity()*************" + domainProfile.getCity());
System.out.println("domainProfile.getAddress()*************" + domainProfile.getAddress());
domainProfileResource.setCity(domainProfile.getCity());
domainProfileResource.setAddress(domainProfile.getAddress());
// return new ResponseEntity<DomainProfileResource>(domainProfileResource, HttpStatus.OK);
return domainProfileResource;
// return domainProfile;
}
}
When I run test case I got An error while we got values in domainprofile.city and domainprofile.address.
Error Is :
java.lang.AssertionError: Status
Expected :200
Actual :500
It Is Working Fine When I return a plain text
can you do this
mockMvc.perform(get("/service/domainprofile/get/{id}", profileId) )
.andDo(print());
this will print the full response with exception , now if you see HttpMessageNotWritableException which was the issue I was facing , you should try to serialize your object using jackson and see if it works (spring internally uses Jackson ). For example , If any of your fields are null the Serialization will fail.

Inject Spring bean within RESTEasy Resource at Test time

Within a Unit/Integration Test, I'm trying to use the RESTEasy embedded server TJWSEmbeddedJaxrsServer or POJOResourceFactory inorder to simulate through a MockHttpRequest.get("/data") a resource call for test purpose.
My problem is that based on the use of the server or the Resource factory I'm not able to have a non null instance of spring beans which are injected normally within my resources.
Here's some code for clarification, thanks in advance.
Spring application context :
<context:annotation-config />
<context:component-scan base-package="com.cdcfast.service" />
<bean id="simpleResource" class="com.cdcfast.rest.SimpleResource" />
SimpleResource.java :
#Component
#Path("/data")
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class SimpleResource {
#Autowired
private SimpleService service;
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Data> getData() {
return MockDataBase.getInstance().getRows();
}
Unit Test :
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:/test/spring/testApplicationContext.xml" })
public class FakeTest {
private Dispatcher dispatcher;
#Before
public void before() {
dispatcher = MockDispatcherFactory.createDispatcher();
POJOResourceFactory noDefaults = new POJOResourceFactory(SimpleResource.class);
dispatcher.getRegistry().addResourceFactory(noDefaults);
}
#Test
public void aTestThatAlwaysPass() throws URISyntaxException {
MockHttpRequest request = MockHttpRequest.get("/data");
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
Assertions.assertThat(response.getStatus()).isEqualTo(HttpServletResponse.SC_OK);
Assertions.assertThat(response.getContentAsString()).isNotNull().isNotEmpty();
}
}
I've had this before because the RESTEasy factories create the POJO rather than Spring so they don't get wired up which can be worked around in the full container but is less easy in a test. The best way around this is to get a handle to your POJO once the factory creates it and then do something similar to this:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(myPojo);
I personally ended up having Spring create the RESTEasy beans using the RESTEasy-Spring plugin and then launching my tests using Jetty, not sure if that is an option for you though.
I exeprienced same problem and i'have solved in similar way as James did:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring-context-test.xml" })
public class TestMyService {
Dispatcher dispatcher;
private String username = "user";
#Autowired
ApplicationContext context;
#Before
public void setUp() {
MyService g = new MyService(); //rest service with #autowired spring beans
context.getAutowireCapableBeanFactory().autowireBean(g);
dispatcher = MockDispatcherFactory.createDispatcher();
dispatcher.getRegistry().addSingletonResource(g);
}
#Test
public void TestRest() {
MockHttpRequest request;
try {
request = MockHttpRequest.get("/rest/service").header("LOGON_USER", username);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertTrue("Error, unexpected status code: " + response.getStatus(), response.getStatus() == 200);
LoggerFactory.getLogger(this.getClass()).info("********** " + response.getContentAsString());
} catch (URISyntaxException e) {
Log.error(e.getMessage(), e);
fail(e.getMessage());
}
}
}