I want to test a Resourse with JerseyTest. I have created the following test:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:testApplicationContext.xml")
public class ResourceTest extends JerseyTest
{
#Configuration
public static class Config
{
#Bean
public AObject aObject()
{
return mock(AObject.class);
}
}
#Autowired
public AObject _aObject;
#Test
public void testResource()
{
// configouring mock _aObject
Response response = target("path");
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
}
#Override
protected Application configure()
{
return new ResourceConfig(Resource.class).property("contextConfigLocation", "classpath:testApplicationContext.xml");
}
}
My Resource also has an AObject reference with #Autowired annotation.
My problem is that my JerseyTest and the Resource (that is configured by the test) have different instances for the Mock object. In the console I see that the testApplicationContext.xml is loaded twice, once for the test and one for the Resource.
How can I force jersey to use the same mock?
After debugging the jersey-spring3 (version 2.9.1) library it seems that the problem lies in the SpringComponentProvider.createSpringContext
private ApplicationContext createSpringContext() {
ApplicationHandler applicationHandler = locator.getService(ApplicationHandler.class);
ApplicationContext springContext = (ApplicationContext) applicationHandler.getConfiguration().getProperty(PARAM_SPRING_CONTEXT);
if (springContext == null) {
String contextConfigLocation = (String) applicationHandler.getConfiguration().getProperty(PARAM_CONTEXT_CONFIG_LOCATION);
springContext = createXmlSpringConfiguration(contextConfigLocation);
}
return springContext;
}
It checks if a property named "contextConfig" exists in the application properties and if not it initializes the spring application context.
Even if you initialized a spring application context in your tests, jersey will create another context and use that one instead. So we have to somehow pass the ApplicationContext from our tests in the Jersey Application class. The solution is the following:
#ContextConfiguration(locations = "classpath:jersey-spring-applicationContext.xml")
public abstract class JerseySpringTest
{
private JerseyTest _jerseyTest;
public final WebTarget target(final String path)
{
return _jerseyTest.target(path);
}
#Before
public void setup() throws Exception
{
_jerseyTest.setUp();
}
#After
public void tearDown() throws Exception
{
_jerseyTest.tearDown();
}
#Autowired
public void setApplicationContext(final ApplicationContext context)
{
_jerseyTest = new JerseyTest()
{
#Override
protected Application configure()
{
ResourceConfig application = JerseySpringTest.this.configure();
application.property("contextConfig", context);
return application;
}
};
}
protected abstract ResourceConfig configure();
}
The above class will take the application context from our tests and pass it to the configured ResourceConfig, so that the SpringComponentProvider will return the same application context to jersey. We also use the jersey-spring-applicationContext.xml in order to include jersey specific spring configuration.
We cannot inherit from JerseyTest because it initializes the Application in the constructor before the test application context is initialized.
You can now use this base class to create your tests for example
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:testContext.xml")
public class SomeTest extends JerseySpringTest
{
#Autowired
private AObject _aObject;
#Test
public void test()
{
// configure mock _aObject when(_aObject.method()).thenReturn() etc...
Response response = target("api/method").request(MediaType.APPLICATION_JSON).get();
Assert.assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
}
#Override
protected ResourceConfig configure()
{
return new ResourceConfig(MyResource.class);
}
}
In testContext.xml add the following definition in order to inject a mock AObject.
<bean class="org.mockito.Mockito" factory-method="mock">
<constructor-arg value="com.yourcompany.AObject" />
</bean>
I couldn't get the answer https://stackoverflow.com/a/24512682/156477 from #Grigoris working, although his explanation for why it is happening is correct.
In the end I went for the approach below which exposes a special setter to insert the mock object. Not as 'clean' as the approach above, but worth the tradeoff of exposing the apiProvider I wanted to mock so I could write some tests..
public MyAPITest extends JerseyTest {
// Declare instance of the API I want to test - this will be instantiated in configure()
MyAPI myAPI;
#Override
protected ResourceConfig configure()
{
MockitoAnnotations.initMocks(this);
myAPI = new MyAPI();
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(MyAPI).property("contextConfig", new ClassPathXmlApplicationContext("classpath:spring.testHarnessContext.xml"));
return resourceConfig;
}
#Mock
private MyAPIProvider mockAPIProvider;
#Before
public void before() {
myAPI.setMockProvider(mockAPIProvider);
}
#Test
public void test() {
// I can now define the mock behaviours and call the API and validate the outcomes
when(mockAPIProvider....)
target().path("....)
}
}
If anyone is interested in the solution https://stackoverflow.com/a/40591082/4894900 from Kevin for Jersey v1:
public MyAPITest extends JerseyTest {
#InjectMocks
MyAPI myAPI;
#Mock
MyApiService myApiService;
#Override
protected AppDescriptorconfigure()
{
MockitoAnnotations.initMocks(this);
ResourceConfig rc = new DefaultResourceConfig();
rc.getSingletons().add(myAPI);
return new LowLevelAppDescriptor.Builder(rc).contextPath("context").build();
}
#Test
public void test() {
// I can now define the mock behaviours
when(myApiService...)
WebResource webResource = resource().path("mypath");
ClientResponse result = webResource.get(ClientResponse.class);
}
}
Further improvising the accepted solution by removing xml dependency. More details available here.
JerseySpringTest abstracting JerseyTest:
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.glassfish.jersey.logging.LoggingFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
import org.mockito.MockitoAnnotations;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
/** Run JerseyTest with custom spring context with mocks Mimics Spring #WebMvcTest with #MockBean */
public abstract class JerseySpringTest extends JerseyTest {
#Override
protected ResourceConfig configure() {
MockitoAnnotations.openMocks(this);
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
set(TestProperties.CONTAINER_PORT, "0");
final ResourceConfig resourceConfig =
new ResourceConfig()
.property("contextConfig", createSpringContext(getBeanMap()))
.property(LoggingFeature.LOGGING_FEATURE_LOGGER_LEVEL_SERVER, "WARNING")
.register(getResourceClass());
return serverConfig(resourceConfig);
}
/**
* Gives the test class opportunity to further customize the configuration. Like registering a
* MultiPartFeature if required.
*
* #param config
* #return
*/
protected ResourceConfig serverConfig(final ResourceConfig config) {
return config;
}
/**
* Supplies all the bean objects required to be loaded in the application context for the Resource class
* under test
*
* #return
*/
protected List<Object> getBeans() {
return Collections.emptyList();
}
/**
* Supplies all the bean objects with name qualifier required to be loaded in the application context for the Resource class
* under test
*
* #return
*/
protected Map<String, Object> getQualifiedBeans() {
return Collections.emptyMap();
}
private Map<String, Object> getBeanMap() {
final Map<String, Object> result = new HashMap<>();
CollectionUtils.emptyIfNull(getBeans())
.forEach(obj -> result.put(StringUtils.uncapitalize(obj.getClass().getSimpleName()), obj));
result.putAll(MapUtils.emptyIfNull(getQualifiedBeans()));
return result;
}
/**
* Resource class under test
*
* #return
*/
protected abstract Class<?> getResourceClass();
/**
* Creates & returns a Spring GenericApplicationContext from the given beans with qualified names
*
* #param beans
* #return
*/
public static ApplicationContext createSpringContext(Map<String, Object> beans) {
final DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
MapUtils.emptyIfNull(beans).forEach((k, obj) -> beanFactory.registerSingleton(k, obj));
final GenericApplicationContext context = new GenericApplicationContext(beanFactory);
context.refresh();
return context;
}
}
Sample Resource With Test:
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
import java.util.List;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import lombok.RequiredArgsConstructor;
import org.glassfish.jersey.client.ClientConfig;
import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
#Path("/rest")
#Component
#RequiredArgsConstructor
class RestResource {
private final ServiceXYZ serviceXYZ;
private final ServiceABC serviceABC;
#Qualifier("greeter")
private final String greeterName;
#GET
#Path("/serviceXYZ/greet/{name}")
public Response greetByServiceXYZ(#PathParam("name") final String name) {
return Response.ok(serviceXYZ.greet(name) + ", Regards: " + greeterName).build();
}
#GET
#Path("/serviceABC/greet/{name}")
public Response greetByServiceABC(#PathParam("name") final String name) {
return Response.ok(serviceABC.greet(name)+ ", Regards: " + greeterName).build();
}
}
#Service
class ServiceXYZ {
public final String greet(final String name) {
return "Welcome " + name + " to Hello World!";
}
}
#Service
class ServiceABC {
public final String greet(final String name) {
return "Welcome " + name + " to Hello Universe!";
}
}
class ResourceTest extends JerseySpringTest {
#InjectMocks private RestResource subject;
#Mock private ServiceXYZ serviceXYZ;
#Mock private ServiceABC serviceABC;
// only required to override for custom server config, say if the Resource accepts file input
#Override
protected ResourceConfig serverConfig(final ResourceConfig config) {
return config.register(MultiPartFeature.class);
}
#Override
protected Map<String, Object> getQualifiedBeans() {
return Map.of("greeter", "Amith Kumar");
}
#Override
protected List<Object> getBeans() {
return List.of(subject, serviceXYZ, serviceABC);
}
#Override
protected Class<?> getResourceClass() {
return RestResource.class;
}
// only required to override for custom client config, say if the Resource accepts file input
#Override
protected void configureClient(ClientConfig config) {
config.register(MultiPartFeature.class);
}
#Test
void testServiceXYZGreets() {
// ARRANGE
when(serviceXYZ.greet("foo")).thenReturn("Hello foo");
// ACT
Response output = target("/rest/serviceXYZ/greet/foo").request().get();
// ASSERT
assertAll(
() -> assertEquals(Status.OK.getStatusCode(), output.getStatus()),
() -> assertEquals("Hello foo, Regards: Amith Kumar", output.readEntity(String.class)));
}
#Test
void testServiceABCGreets() {
// ARRANGE
when(serviceXYZ.greet("boo")).thenReturn("Hola boo");
// ACT
Response output = target("/rest/serviceABC/greet/boo").request().get();
// ASSERT
assertAll(
() -> assertEquals(Status.OK.getStatusCode(), output.getStatus()),
() -> assertEquals("Hola boo, Regards: Amith Kumar", output.readEntity(String.class)));
}
}
Related
I am using jpos Q2 Server with transaction manager from my spring boot application, however when i try to implement DI in my class which is implementing from the Jpos TransactionParticipant interface it is giving me the null pointer exception.
I have tried all the option that may be there in spring boot for IoC as per my understanding. It seems that TransactionParticipant third party library i am not able to register it in Spring IoC/DI module.
package com.fonepay.iso;
#Service("processIsoTxn")
public class ProcessIsoTxn implements TransactionParticipant{
#Autowired
private CbsTxnService cbsTxnService;
#Override
public int prepare(long id, Serializable context) {
Context ctx = (Context) context;
try{
ISOMsg request = (ISOMsg) ctx.get("REQUEST");
//Call local processing Message
//CbsTxnService cbsTxnService = new CbsTxnServiceImpl();
ISOMsg response = cbsTxnService.createFinancialTxn(request);
ctx.put("RESPONSE", response);
return PREPARED;
}catch(Exception ex){
System.out.println("Process Iso Txn | error | "+ex);
}
return 0;
}
}
package com.fonepay.service.impl;
#Service("cbsTxnService")
#Transactional
public class CbsTxnServiceImpl implements CbsTxnService{
public ISOMsg createFinancialTxn(ISOMsg isoMsg) {...}
}
#SpringBootApplication
#ComponentScan("com.fonepay")
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class JposserverApplication {
public static void main(String[] args) {
SpringApplication.run(JposserverApplication.class, args);
}
}
I am constantly getting java.lang.NullPointerException in line
ISOMsg response = cbsTxnService.createFinancialTxn(request);
Try this one , replace #Autowired annotation
Try to use constructor
#Service("processIsoTxn")
public class ProcessIsoTxn implements TransactionParticipant{
private CbsTxnService cbsTxnService;
public ProcessIsoTxn (CbsTxnService cbsTxnService) {
this.cbsTxnService = cbsTxnService;
}
If anyone is interested, this is how i achieved the workaround, please refer : https://confluence.jaytaala.com/display/TKB/Super+simple+approach+to+accessing+Spring+beans+from+non-Spring+managed+classes+and+POJOs
private CbsTxnService getCbsTxnService() {
return SpringContext.getBean(CbsTxnService.class);
}
ISOMsg response = getCbsTxnService().createFinancialTxn(request);
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
#Component
public class SpringContext implements ApplicationContextAware {
private static ApplicationContext context;
/**
* Returns the Spring managed bean instance of the given class type if it exists.
* Returns null otherwise.
* #param beanClass
* #return
*/
public static <T extends Object> T getBean(Class<T> beanClass) {
return context.getBean(beanClass);
}
#Override
public void setApplicationContext(ApplicationContext context) throws BeansException {
// store ApplicationContext reference to access required beans later on
SpringContext.context = context;
}
}
I want to be able to autowire a singleton bean (foo)
#Component
public class FooUser {
#Autowire Foo foo;
}
created by another singleton's method (FooFactory.createFoo)
#Service
public class FooFactory {
public Foo createFoo() {...}
}
with xml it's simply factory-method. How can i do it with annotation?
Try Java #Configuration instead:
#Configuration
public class Config {
#Bean
public FooUser fooUser() {
return new FooUser(foo());
}
#Bean
public FooFactory fooFactory() {
return new FooFactory();
}
#Bean
public Foo foo() {
return fooFactory().createFoo();
}
}
You need java-config - the #Bean annotation.
Define your class as #Configuration and your method as #Bean
Spring Components can also define factory methods. A snipped from the documentation:
#Component
public class FactoryMethodComponent {
#Bean #Qualifier("public")
public TestBean publicInstance() {
return new TestBean("publicInstance");
}
public void doWork() {
// Component method implementation omitted
}
}
use the FactoryBean Spring interface. then u will be able to autowire T itself
EDIT:
The BeanFactory is an interface in Spring where if you implement it You can make a factory of the object such as :
public class FooFactoryBean implements FactoryBean<Foo>{
..................
}
then you can initialize the bean :
#Bean
public FooFactoryBean foo(){
return new FooFactoryBean();
}
then if you autowired Foo it Spring will understand the FooFactoryBean is the desired factory
#Autowired
Foo foo;
Spring Boot: factory method
import java.util.HashMap;
import java.util.Map;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;
enum ParsersConst {
bofa, jpm, wellsforgo
}
interface Parser {
String readFromFile(String file);
}
class JPM implements Parser {
#Override
public String readFromFile(String file) {
System.out.println("From JPM Parser");
//LOGIC to read file data
return "JPM";
}
}
class Bofa implements Parser {
#Override
public String readFromFile(String file) {
System.out.println("From Bofa Parser");
//LOGIC to read file data
return "BOFA";
}
}
class WellsForgo implements Parser {
#Override
public String readFromFile(String file) {
System.out.println("From Wellsforgo Parser");
//LOGIC to read file data
return "WellsForgo";
}
}
class ParserCreator {
private Map<ParsersConst, Parser> parserMap;
public Parser createParser(ParsersConst parsConst) {
Parser parser = parserMap.get(parsConst);
if (parserMap.get(parsConst) != null) {
return parser;
}
throw new IllegalArgumentException("Unknown Parser");
}
public void setParserMap(Map<ParsersConst, Parser> parserMap) {
this.parserMap = parserMap;
}
}
#Configuration
class ParserConfig {
#Bean
public ParserCreator parserCreatorFactory() {
ParserCreator factory = new ParserCreator();
Map<ParsersConst, Parser> map = new HashMap<ParsersConst, Parser>();
map.put(ParsersConst.bofa, new Bofa());
map.put(ParsersConst.wellsforgo, new WellsForgo());
map.put(ParsersConst.jpm, new JPM());
factory.setParserMap(map);
return factory;
}
#Bean
public Parser bofa() {
return parserCreatorFactory().createParser(ParsersConst.bofa);
}
#Bean
public Parser wellsforgo() {
return parserCreatorFactory().createParser(ParsersConst.wellsforgo);
}
#Bean
public Parser jpm() {
return parserCreatorFactory().createParser(ParsersConst.jpm);
}
}
#Component
public class StaticFacotryDemo implements CommandLineRunner {
#Autowired
private ApplicationContext context;
#Override
public void run(String... args) throws Exception {
Parser parser = (Parser) context.getBean(ParsersConst.jpm.toString());
System.out.println(parser.readFromFile("jan_stmt.pdf"));
}
}
I want to provide a base configuration class that will handle creating beans with a generic type that gets defined when you extend the class as seen below. But it never calls the #Bean methods.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.client.RestTemplate;
import java.lang.reflect.Constructor;
#RunWith( SpringJUnit4ClassRunner.class )
#ContextConfiguration( classes = { TestGenericBean.MyClientCreator.class } )
public class TestGenericBean
{
/* This throws an error saying no bean provided */
#Autowired
private TestClient client;
public static class ClientConfig<T>
{
private Class<T> classCreator;
public ClientConfig(Class<T> classCreator)
{
this.classCreator = classCreator;
}
/* This is never called */
#Bean
public T createClient(RestTemplate restTemplate) throws Exception
{
Constructor<T> constructor = classCreator.getConstructor(
RestTemplate.class
);
return constructor.newInstance( restTemplate );
}
/* This is never called */
#Bean
public RestTemplate restTemplate()
{
return new RestTemplate();
}
}
#Configuration
public static class MyClientCreator extends ClientConfig<TestClient>
{
public MyClientCreator()
{
super( TestClient.class );
}
}
public static class TestClient
{
public RestTemplate restTemplate;
public TestClient(RestTemplate restTemplate)
{
this.restTemplate = restTemplate;
}
}
#Test
public void testBean()
{
System.out.print( client.restTemplate );
}
}
Not sure why, but it requires a bean of type MappingJackson2HttpMessageConverter. With that, it works.
#RunWith( SpringJUnit4ClassRunner.class )
#ContextConfiguration( classes = { TestGenericBean.MyClientCreator.class } )
public class TestGenericBean
{
#Autowired
private TestClient client;
public static class ClientConfig<T>
{
private Class<T> classCreator;
public ClientConfig(Class<T> classCreator)
{
this.classCreator = classCreator;
}
/**** THIS IS REQUIRED - WHY? ****/
#Bean
public MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter()
{
return new MappingJackson2HttpMessageConverter();
}
#Bean
public T createClient(AsyncRestTemplate asyncRestTemplate) throws Exception
{
Constructor<T> constructor = classCreator.getConstructor(
AsyncRestTemplate.class
);
return constructor.newInstance( asyncRestTemplate );
}
#Bean
public AsyncRestTemplate asyncRestTemplate()
{
return new AsyncRestTemplate();
}
}
#Configuration
public static class MyClientCreator extends ClientConfig<TestClient>
{
public MyClientCreator()
{
super( TestClient.class );
}
}
public static class TestClient
{
public AsyncRestTemplate asyncRestTemplate;
public TestClient(AsyncRestTemplate asyncRestTemplate)
{
this.asyncRestTemplate = asyncRestTemplate;
}
}
#Test
public void testBean()
{
System.out.print( client.asyncRestTemplate );
}
}
I haven't used Spring / Spring boot a lot lately but i have a lot of experience with annotation preprocessors. The way spring boot is using them cannot allow any generic method to be used as a bean provider for #autowire. This is because it cannot safely resolve the java class < T > and therefore map it with the field. I would suggest you try using a wrapper class such as Getter< T > or List< T > but can't guarantee that any generic implementation will work.
we are trying to do an intergration test our interceptors in our spring boot application using spring boot version 1.4.0, but not sure how; here is our application setting
#Configuration
#EnableAutoConfiguration()
#ComponentScan
public class Application extends SpringBootServletInitializer {
#Override
protected SpringApplicationBuilderconfigure(SpringApplicationBuilder application) {
return application.sources(Application.class);
}
we then customed out webmvc by extending WebMvcConfigurerAdapter
#Configuration
public class CustomServletContext extends WebMvcConfigurerAdapter{
#Override
public void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(testInterceptor).addPathPatterns("/testapi/**");
}
}
so we wanna to test the interceptor, but we don't wanna really start the application, cause there are many dependency beans that need to read a externally defined property files to construct
we have tried the following
#SpringBootTest(classes = CustomServletContext.class)
#RunWith(SpringRunner.class)
public class CustomServletContextTest {
#Autowired
private ApplicationContext applicationContext;
#Test
public void interceptor_request_all() throws Exception {
RequestMappingHandlerMapping mapping = (RequestMappingHandlerMapping) applicationContext
.getBean("requestMappingHandlerMapping");
assertNotNull(mapping);
MockHttpServletRequest request = new MockHttpServletRequest("GET",
"/test");
HandlerExecutionChain chain = mapping.getHandler(request);
Optional<TestInterceptor> containsHandler = FluentIterable
.from(Arrays.asList(chain.getInterceptors()))
.filter(TestInterceptor.class).first();
assertTrue(containsHandler.isPresent());
}
}
but it alters org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'requestMappingHandlerMapping' is defined
Do we need to create a bean of requestMappingHandlerMapping to test the interceptors? is there any magical way to do this in spring boot ?
You can create a test like this :
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = { MyIncludedConfig.class })
#ActiveProfiles("my_enabled_profile")
public class BfmSecurityInterceptorTest2 {
public static final String TEST_URI = "/test";
public static final String RESPONSE = "test";
// this way you can provide any beans missing due to limiting the application configuration scope
#MockBean
private DataSource dataSource;
#Autowired
private TestRestTemplate testRestTemplate;
#Test
public void testInterceptor_Session_cookie_present_Authorized() throws Exception {
ResponseEntity<String> responseEntity = testRestTemplate.getForEntity(TEST_URI, String.class);
assertThat(responseEntity.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(responseEntity.getBody()).isEqualTo(RESPONSE);
}
#SpringBootApplication
#RestController
public static class TestApplication {
#GetMapping(TEST_URI)
public String test() {
return RESPONSE;
}
}
}
Notes
Interceptors only work if you set SpringBootTest.WebEnvironment.RANDOM_PORT
You have to provide enough configuration so your interceptors are executed
To speed up the test you can exclude not wanted beans and configurations, see examples
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.web.servlet.HandlerExecutionChain;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertTrue;
#SpringBootTest()
class LoggingInterceptorConfigurationTest {
#Autowired
private RequestMappingHandlerMapping mapping;
#Test
public void LoggingInterceptorShouldBeApplied() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/api/example");
HandlerExecutionChain chain = mapping.getHandler(request);
assert chain != null;
Optional<HandlerInterceptor> LoggingInterceptor = chain.getInterceptorList()
.stream()
.filter(LoggingInterceptor.class::isInstance)
.findFirst();
assertTrue(LoggingInterceptor.isPresent());
}
#Test
public void LoggingInterceptorShouldNotBeAppliedToHealthURL() throws Exception {
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/health");
HandlerExecutionChain chain = mapping.getHandler(request);
assert chain != null;
Optional<HandlerInterceptor> LoggingInterceptor = chain.getInterceptorList()
.stream()
.filter(LoggingInterceptor.class::isInstance)
.findFirst();
assertTrue(LoggingInterceptor.isEmpty());
}
}
I am playing with Dagger on Android. I created a model UserPreference, a module called PreferenceModule and another class UserPreferenceTest which is a test of the PreferenceModule. I have below 3 java files
UserPreference.java
package com.sigicn.preference;
import javax.inject.Inject;
import com.sigicn.commonmodels.Application;
public class UserPreference {
public String name, weiboAccount;
#Inject
public Application[] frequentlyUsedApps;
}
Then PreferenceModule.java
package com.sigicn.preference;
import javax.inject.Singleton;
import com.sigicn.commonmodels.Application;
import com.sigicn.utils.MiscUtils;
import dagger.Module;
import dagger.Provides;
#Module(library = true, complete = true)
public class PreferenceModule {
#Provides #Singleton UserPreference provideUserPreference() {
UserPreference userPreference = new UserPreference();
userPreference.frequentlyUsedApps = provideApplications();
return userPreference;
}
#Provides #Singleton Application[] provideApplications() {
return new Application[]{
new Application(
MiscUtils.generateUUID(), "Youtube"),
new Application(
MiscUtils.generateUUID(), "Pixi")
};
}
}
Then UserPreferenceTest.java
package com.sigicn.test.preference;
import javax.inject.Inject;
import com.sigicn.preference.PreferenceModule;
import com.sigicn.preference.UserPreference;
import dagger.Module;
import dagger.ObjectGraph;
import android.test.AndroidTestCase;
public class UserPreferenceTest extends AndroidTestCase {
#Module(injects = {UserPreference.class, UserPreferenceTest.class},
includes = PreferenceModule.class)
static class TestModule {
}
ObjectGraph objectGraph;
#Inject
UserPreference userPreference;
#Override
protected void setUp() throws Exception {
if (objectGraph == null) {
objectGraph = ObjectGraph.create(new TestModule());
}
super.setUp();
}
public void testFrequentlyUsedApps()
{
UserPreference localUserPreference = objectGraph.get(UserPreference.class);
assertNotNull(localUserPreference);
assertEquals(localUserPreference.frequentlyUsedApps.length, 2);
objectGraph.inject(this);
assertNotNull(userPreference);
assertEquals(userPreference.frequentlyUsedApps.length, 2);
assertSame(localUserPreference, userPreference);
assertSame(localUserPreference.frequentlyUsedApps, userPreference.frequentlyUsedApps);
}
}
But don't know why, that the frequentlyUsedApps of UserPreference is not injected as expected. Any idea why?
Update:
I think I have figured out the reason. It's because that I manually create UserPreference and use it in the provider. If I remove the Provider for UserPreference, and let Dagger to wire it automatically, then the field frequentlyUsedApps does get injected. So it is my fault of not understanding Dagger well.
I think you need to add some ObjectGraph#inject calls.
In each class where you have an #Inject annotation, you will also need a call to the inject method of the ObjectGraph you created.
I have had been struggling with this for a while also. I think the basic pattern is:
Annotate your fields to indicate you want to inject them
Create a module to "provide" the instances for those #Injects
Create the graph somewhere (seems like most people are doing that in
the Application class)
In the classes you want to inject stuff from your module, get an
instance of the graph and call inject(this).
I started using a singleton rather than the Application class, because at least for now I have some places were I want to inject the app itself.
So here is what I am currently doing, which seems to work pretty weill
public class Injector {
private static Injector mInjector;
private ObjectGraph mObjectGraph;
private MyApp mApp;
private Injector() {
}
public static Injector getInstance() {
if (mInjector == null) {
mInjector = new Injector();
}
return mInjector;
}
protected List<Object> getModules() {
return Arrays.asList(
new ApplicationModule(mApp),
new AndroidModule(mApp)
);
}
public void inject(Object object) {
getObjectGraph().inject(object);
}
public ObjectGraph getObjectGraph() {
return mObjectGraph;
}
public void initialize(MyApp app) {
mApp = app;
mObjectGraph = ObjectGraph.create(getModules().toArray());
System.out.println(String.format("init object graph = %s",mObjectGraph.toString()));
}
}
Then in my application class I have a constructor like this:
public MyApp() {
System.out.println("myapp construtor");
Injector.getInstance().initialize(this);
Injector.getInstance().inject(this);
}
Then when I want to inject something I do this
#Inject Bus mBus;
public GcmBroadcastReceiver() {
Injector.getInstance().inject(this);
}
I have two modules , one for production and one for test
The production one has this
#Provides #Singleton
public Bus provideBus () {
return BusProvider.getInstance();
}
and the test one has this
#Provides #Singleton
public Bus provideBus () {
return mock(Bus.class);
}