How to write a mockito test case for ResourceAssembler with in Spring Hateos? - spring-mvc

I am trying to write a unit test for the below Assembler but i keep getting Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler?. I wanted to know how i can mock out the resource creation?
#Component
public class LoginResourceAssembler extends ResourceAssemblerSupport<User, ResourceSupport> {
public LoginResourceAssembler() {
super(User.class, ResourceSupport.class);
}
#Override
public ResourceSupport toResource(User user) {
ResourceSupport resource = new ResourceSupport();
final String id = user.getId();
resource.add(linkTo(MyAccountsController.class).slash(id).slash("accounts").withRel("accounts"));
return resource;
}
}

Instead of changing from a plain unit test to a IMO integration test (given dependency of the spring framework) you could do something like:
#RunWith(MockitoJUnitRunner.class)
public class LoginResourceAssemblerTest {
#InjectMocks
private LoginResourceAssembler loginResourceAssembler;
#Before
public void setup() {
RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest()));
}
#Test
public void testToResource() {
//...
}
}

I was seeing the error Could not find current request via RequestContextHolder. Is this being called from a Spring MVC handler because my test class was annotated with #RunWith(MockitoJUnitRunner.class) and this was not injecting the controller.
To fix this error, i annotated my test case with
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
A working test case in my case
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebAppConfiguration
public class LoginResourceAssemblerTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#InjectMocks
private LoginResourceAssembler loginResourceAssembler;
#Before
public void setUp() {
initMocks(this);
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context).build();
}
#Test
public void testToResource() {
User user = new User();
user.setId("1234");
ResourceSupport resource = loginResourceAssembler.toResource(user);
assertEquals(1,resource.getLinks().size());
assertEquals("accounts",resource.getLinks().get(0).getRel());
assertTrue(resource.getLinks().get(0).getHref().contains("accounts"));
}
}

Related

Spring redis unable to autowire repository

I'm using custom crudrespository to persist data in redis. However, I'm unable to autowire custom repository.
All the configuration seems correct and redis is running on my local.
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface CustomRepository extends CrudRepository<String,
Long> {
String get(String key);
void put(String key, String value);
}
//////////
public class StorageServiceImpl implements IStorageService {
#Autowired
private CustomRepository respository;
#Override
public void saveParameter() {
this.respository.put("key1","value1");
}
#Override
public String getParameter() {
return this.respository.get("key1");
}
/////
#Service
public interface IStorageService {
void saveParameter();
String getParameter();
}
///////
#SpringBootApplication(scanBasePackages = {"com.example.cache"})
#EnableRedisRepositories(basePackages = {"com.example.cache.repository"})
public class ApplicationConfiguration {
public static void main(String[] args){
SpringApplication.run(ApplicationConfiguration.class, args);
new StorageServiceImpl().saveParameter();
System.out.println(new StorageServiceImpl().getParameter());
}
}
When I try running this application using gradle bootRun, I get
Exception in thread "main" java.lang.NullPointerException
at com.example.cache.impl.StorageServiceImpl.saveParameter(StorageServiceImpl.java:16)
at com.example.cache.ApplicationConfiguration.main(ApplicationConfiguration.java:17)
Not sure what's wrong?
You can't use new on any bean, you need to #Autowire it. The annotations only work with spring managed beans at every level.
Add a new bean with a a storage service and a method that makes your call after it is created.
Also, I can't remember if the spring-boot creates the bean if there is only one implementation but I believe your StorageServiceImpl needs the #Service annotation, not the interface.
Delete this from your ApplicationConfiguration class.
new StorageServiceImpl().saveParameter();
System.out.println(new StorageServiceImpl().getParameter());
Then add this new class.
#Service
public class Startup {
#Autowired
IStorageService storageService;
#PostConstruct
public void init(){
storageService.saveParameter();
System.out.println(storageService().getParameter());
}
}
And you need a config
#Configuration
#EnableRedisRepositories
public class ApplicationConfig {
#Bean
public RedisConnectionFactory connectionFactory() {
return new JedisConnectionFactory();
}
#Bean
public RedisTemplate<?, ?> redisTemplate() {
RedisTemplate<byte[], byte[]> template = new RedisTemplate<byte[], byte[]>();
return template;
}
}

#Value not visible inside #component filter during tests

I created filter which logging and saving all requests, this is part of this:
#Component
public class RequestFilter extends OncePerRequestFilter {
#Value("${app.endpoint}")
private String requestMapping;
private final RequestRepository requestRepository;
#Autowired
public RequestFilter(RequestRepository requestRepository) {
this.requestRepository = requestRepository;
}
....
}
When app is running requestMapping is properly readed from spring context, but
when I created test for that filter requestMapping is null
#SpringBootTest
#RunWith(SpringRunner.class)
#AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
#ContextConfiguration(classes = {MyApplication.class})
#AutoConfigureMockMvc
#ActiveProfiles("test")
public class FilterTest {
#Autowired
private WebApplicationContext webApplicationContext;
#Autowired
private RequestRepository requestRepository;
#Autowired
protected MockMvc mockMvc;
#Before
public void setup() {
RequestFilter rpmRequestFilter = new RequestFilter(this.requestRepository);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(invalidVpmRequestFilter)
.build();
}
}
And of course in application-test.properties I have configured this property:
app.endpoint=/log/save
Does someone know where the problem can be? Why this is doesnt work in tests?
As M. Deinum pointed out, the problem is that you are creating an instance of RequestFilter and if you want Spring to inject components (#Autowired) or propoerties (#Value) in it, you have to let Spring handle the instantiation as follow :
#....
public class FilterTest {
....
#Autowired
RequestFilter requestFilter;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.addFilter(requestFilter)
.build();
}
}

Test Spring Mvc controller and inject static class

The following code is the standard method to write a JUnit test for a Mvc controller.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = ApplicationTestCassandra.class)
#WebAppConfiguration
public class TestControllerTests {
#Autowired
private WebApplicationContext webApplicationContext;
private MockMvc mockMvc;
#Before
public void setup() throws Exception {
this.mockMvc = webAppContextSetup(webApplicationContext).build();
}
#Test
public void testupTimeStart() throws Exception {
this.mockMvc.perform(get("/uptime"))
.andExpect(status().isOk());
}
}
This works fine, but I would like to replace an autowired class with a special class for testing. The class CassandraSimpleConnection is injected via #Autowired in my controller.
I have tried several approaches, but no luck.
The following code fails because of an Mvc 404 error, because I guess my application with the REST interface is not running at all.
#RunWith(SpringJUnit4ClassRunner.class)
//ApplicationTestCassandra is SpringBoot application startpoint class with #SpringBootApplication annotation
//#ContextConfiguration(classes = ApplicationTestCassandra.class, loader = AnnotationConfigContextLoader.class)
#ContextConfiguration(loader = AnnotationConfigWebContextLoader.class)//, classes = {ApplicationTestCassandra.class})
#WebAppConfiguration
public class TestControllerTests {
#Service
#EnableWebMvc
#ComponentScan(basePackages={"blabla.functionalTests"})
static class CassandraSimpleConnection {
public Metadata testConnection(TestConfiguration configuration) {
Metadata metadata = null;
// return metadata;
throw new RuntimeException("Could not connect to any server");
}
}
If I use
#ContextConfiguration(loader = AnnotationConfigWebContextLoader.class, classes = {ApplicationTestCassandra.class})
CassandraSimpleConnection is not replaced with my static class.
Could somebody help me please? The documentation about the annotations is quite confusing.
Read the comments and here is the solution:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = { MyApplication.class })
public class MyTests {
#MockBean
private MyBeanClass myTestBean;
#Before
public void setup() {
...
when(myTestBean.doSomething()).thenReturn(someResult);
}
#Test
public void test() {
// MyBeanClass bean is replaced with myTestBean in the ApplicationContext here
}
}

Apache Camel Integration test results in OutOfMemory

I have a simple Camel route that sends data to an external REST interface, integrated with Spring MVC.
#RestController
public class MyController {
#Autowired
private camelService camelService;
#RequestMapping(method = RequestMethod.POST, value = "/test")
#ResponseStatus(HttpStatus.CREATED)
public TestModel createEV(#Valid #RequestBody TestModel testModel) {
camelService.publishTestCase(testModel);
return testModel;
}
}
#Service
public class CamelService {
#Produce(uri = "direct:test")
private ProducerTemplate producerTemplate;
#Override
public void publishTestCase(TestModel testModel) {
producerTemplate.sendBody(testModel);
}
}
#Component
public class TestRouter extends SpringRouteBuilder
#Override
public void configure() throws Exception {
errorHandler(loggingErrorHandler(log).level(LoggingLevel.ERROR));
from("direct:test")
.routeId("testRoute")
.beanRef("mapper", "map")
.to("velocity:test.vm")
.setHeader(Exchange.HTTP_METHOD, simple("POST"))
.to("log:test?level=DEBUG&showAll=true&multiline=true&maxChars=100000")
.to("cxfrs:http://url.here");
}
}
Then there's an integration test for the rest endpoint with mockMvc that mocks the external camel endpoint.
#ContextConfiguration(loader = SpringApplicationContextLoader, classes = Application)
#WebIntegrationTest
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
class MyControllerIntegrationTest extends Specification {
#Autowired
private CamelContext camelContext
MockMvc mockMvc
#Autowired
WebApplicationContext wac
#Shared
def setupOnceRun = false
#Shared
def validInput = new File(JSON_DIR + '/valid.json').text
def setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build()
// no way to use setupSpec as Autowired fields can't be Shared
if (!setupOnceRun) {
ModelCamelContext mcc = camelContext.adapt(ModelCamelContext);
camelContext.getRouteDefinition("testRoute).adviceWith(mcc, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
mockEndpointsAndSkip("cxfrs:http://url.here")
}
});
setupOnceRun = true
}
}
def 'valid scenario'() {
setup:
MockEndpoint mockEndpoint = (MockEndpoint) camelContext.hasEndpoint("mock:cxfrs:http://url.here")
mockEndpoint.expectedMessageCount(1)
when:
def response = mockMvc.perform(post('/test').content(validInput)).andReturn()
then:
response.getResponse().getStatus() == 201
mockEndpoint.assertIsSatisfied()
}
}
The test is passing if you run it by itself but once it's included in the build together with other integration tests it's constantly producing OutOfMemory. MaxPermSize is 4G, it doesn't look like that's an issue. I am new to Camel so my guess is that I am wiring the test in a wrong way. Would appreciate any suggestions.

Spring MVC - How to create a proper Service layer?

I'm using SpringBoot and I am trying to create a service layer for my web application but i cant make it work.
My classes look like this
ServiceFactory
#Service
public class ServiceFactory {
#Autowired
public static EncuestaService getEncuestaService()
{
return new EncuestaServiceImpl();
}
}
EncuestaService
public interface EncuestaService {
void crearEncuesta(Encuesta encuesta, Map<String,String> parametros);
}
EncuestaServiceImpl
#Service
public class EncuestaServiceImpl implements EncuestaService {
#Override
public void crearEncuesta(Encuesta encuesta, Map<String, String> parametros) {
CrearEncuesta nueva = new CrearEncuesta(encuesta,parametros);
nueva.execute();
}
}
CrearEncuesta
#Service
public class CrearEncuesta {
private Encuesta encuesta;
private Map<String,String> parametros;
#Autowired
private RespuestasRepository respuestasRepository;
#Autowired
private EncuestasRepository encuestasRepository;
public CrearEncuesta(Encuesta encuesta, Map<String,String> parametros) {
super();
this.encuesta = encuesta;
this.parametros = parametros;
}
public void execute()
{
encuestasRepository.save(encuesta);
}
}
Everytime I call ServiceFactory.getEncuestasService().crearEncuesta() from any Controller it returns me a NullPointerException.
From what I have been reading I should not be creating a new EncuestsaServiceImpl() in my ServiceFactory but I don't really know the correct way to do so. I would appreciate if anyone could help me out :P.
Edit:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
Controller
#Controller
public class EncuestaController {
#RequestMapping(value ="registrarEncuesta", method = RequestMethod.POST)
private String formularioEncuesta(#Valid #ModelAttribute("formEncuesta") EncuestaForm formEncuesta, BindingResult bindingResult,#RequestParam Map<String,String> allRequestParams)
{
if (bindingResult.hasErrors()) {
return "nuevaEncuesta";
}
try {
Encuesta nueva = formEncuesta.toEncuesta();
ServiceFactory.getEncuestaService().crearEncuesta(nueva,allRequestParams);
} catch (DataIntegrityViolationException e) {
return "nuevaEncuesta";
}
return "redirect:/encuestas";
}
}
You will have to read a little bit more about dependency injection. The central principle in Spring Framework is dependency injection which should be used to avoid referencing beans (service implementations, repository implementations etc...) statically. Spring container also servers as a bean factory that will instantiate and inject (autowire) implementations to beans that need them.
Because Spring will instantiate service interface implementations for you, you don't need ServiceFactory. In your controller you need to add a reference (a field) to EncuestaService and annotate it as Autowired and Spring will wire in the implementation. And then you can just use it in your controller.
#Controller
public class EncuestaController {
#Autowired
EncuestaService encuestaService;
#RequestMapping(value ="registrarEncuesta", method = RequestMethod.POST)
private String formularioEncuesta(#Valid #ModelAttribute("formEncuesta") EncuestaForm formEncuesta, BindingResult bindingResult,#RequestParam Map<String,String> allRequestParams)
{
if (bindingResult.hasErrors()) {
return "nuevaEncuesta";
}
try {
Encuesta nueva = formEncuesta.toEncuesta();
encuestaService.crearEncuesta(nueva,allRequestParams);
} catch (DataIntegrityViolationException e) {
return "nuevaEncuesta";
}
return "redirect:/encuestas";
}
}

Resources