Having an issue with Spring Autowiring. I have an Integration Test class declared as follows:
#ContextConfiguration(classes = TestConfig.class, loader = AnnotationConfigContextLoader.class)
public abstract class BaseIntegrationTest
extends AbstractTestNGSpringContextTests {
#Autowired
protected TestProperties properties;
//... more stuff here
}
The Context configuration looks like this:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = {"com.ourcompany.myapp.it"},
excludeFilters = #ComponentScan.Filter(value = com.inin.wfm.it.config.ConfigPackageExcludeFilter.class, type = FilterType.CUSTOM))
public class TestConfig {
private TestProperties testProperties;
private PropertyService propertyService;
//This both creates and registers the bean with Spring
#Bean
public TestProperties getTestProperties() throws IOException {
if (testProperties == null) {
testProperties = new TestProperties(propertyService());
}
return testProperties;
}
#Bean
public PropertyService propertyService() throws IOException {
if (propertyService == null) {
AppAdminConfig config = new AppAdminConfig.Builder(PropertyService.getEnvironment(), TestConfigKey.ApplicationId)
.checkPropertyHasValue(GlobalConfigKey.KafkaBrokerList.key())
.checkPropertyHasValue(GlobalConfigKey.ZookeeperList.key())
.build();
propertyService = new PropertyService(config.getPropertiesConfig());
propertyService.initialize();
}
return propertyService;
}
}
And this is the bean I'm having trouble with:
#Configurable
public class TestProperties {
private PropertyService propertyService;
public TestProperties(PropertyService propertyService) {
this.propertyService = propertyService;
}
public String getCacheUri(){
return propertyService.getPropertyRegistry().getString(TestConfigKey.CacheUri.key(), Default.CACHE_URI);
}
}
I have multiple Test implementation classes that extend BaseIntegrationTest. All of them but one have valid references to their TestProperties field, but exactly one of the test implementation classes is getting a Null Pointer and throwing an NPE when an attempt is made to reference this.
So the question is, why is Spring #Autowire working fine for 3 different classes that extend the same base class, but wiring up null for the fourth? There is no additional configuration logic in any of the implementations.
For MCVE completeness, here's the bare bones of my impl class
public class CacheIT
extends BaseIntegrationTest {
#Test
public void testUserCache() throws InterruptedException, ExecutionException, TimeoutException {
String uri = properties.getCacheUri() //TODO - NPE here
}
}
I know there's not much to go on... I've been working with Spring for a long time and haven't seen it do this kind of thing before. According to everything I can see it should be working.
Related
I have a problem of running my Test class. It returns "org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 't.c.i.s.se.Sfts' available: expected single matching bean but found 2: sftsImpl,sfts" this exception after I run it.
Here's my test class;
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Sfta.class)
public class SftaTests {
#Autowired
ApplicationContext ac;
#Test
public void contextLoads() {
Sfts sfts= ac.getBean(Sfts.class);
assertTrue(Sfts instanceof SftsImpl);
}
}
And my other classes are like;
public interface Sfts {
public void process();
}
#Service
#Component
public class SftsImpl implements Sfts {
#Autowired
private GlobalConfig globalConfig;
#Autowired
private Ftr ftr;
private Fc fc;
#Async
#Scheduled(initialDelayString = "${s.f.t.m}", fixedRateString = "${s.f.t.m}")
public void process() {
int hod = DateTime.now().getHourOfDay();
if (hod != 6){
fc = new Fc(globalConfig, ftr);
fc.control();
}
}
}
Why I get the error after running the test application?
Try to remove #Component annotation from the SftsImpl bean.
#Service is enough to register a bean.
Also if you just want to test your bean - getting it from ApplicationContext maybe is not the best option.
Code example of a unit test without using ApplicationContext:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Sfta.class)
public class SftaTests {
#Autowired
Sfts sfts;
#Test
public void testAsync() {
sfts.process();
// do assertions here
}
}
I am trying to #Autowire a #Configuration class inside a #Service class. basically my #Configuration class contains mapping to my custom .properties file. When i try to autowire my configuration class inside my service class, BeanCreationException occurs. I am not sure what happen. Just followed the guide on creating Property classes from spring. There must be something i missed out.
Also, when i try to autowire #Configuration class to another #Configuration class, it runs smoothly
Currently, i know that, prop is always null because when i remove prop.getUploadFileLocation() call, everything will be fine. There must be something wrong during autowiring.
Here is my Service class
#Service
public class ImageService {
public static Logger logger = Logger.getLogger(ImageService.class.getName());
#Autowired
MyProperties prop;
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
Here is my Configuration class
#Data
#Configuration
#ConfigurationProperties (prefix = "my")
public class MyProperties {
private String resourceLocation;
private String resourceUrl;
public String getUploadFileLocation() {
return getResourceLocation().replace("file:///", "");
}
public String getBaseResourceUrl() {
return getResourceUrl().replace("**", "");
}
}
And here is where i can successfully use MyProperties
#Configuration
public class StaticResourceConfiguration implements WebMvcConfigurer {
#Autowired
MyProperties prop;
#Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler(prop.getResourceUrl())
.addResourceLocations(prop.getResourceLocation());
}
}
The issue is that you are trying to use an autowired field to set the value in an inline field assignment.
That means
private final String FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
is executed before the prop is autowired, meaning it will always be null
The way to mitigate this would be to use constructor injection instead.
#Service
public class ImageService {
//Fine since you are using static method
public static Logger logger = Logger.getLogger(ImageService.class.getName());
//Not needed if you are only using it to set FILE_UPLOAD_LOCATION
//Allows field to be final
private final MyProperties prop;
//Still final
private final String FILE_UPLOAD_LOCATION;
//No need for #Autowired since implicit on component constructors
ImageService(MyProperties prop){
//Again not needed if you aren't going to use anywhere else in the class
this.prop = prop;
FILE_UPLOAD_LOCATION = prop.getUploadFileLocation() +"uploads/images/";
}
public void upload(String base64ImageFIle) throws IOException {
logger.info(FILE_UPLOAD_LOCATION);
}
}
See this question for why constructor is preferred over #autowired in general
If you need MyProperties bean to be created before StaticResourceConfiguration bean, you can put #ConditionalOnBean(MyProperties.class) as following. Spring will make sure MyProperties is there before processing StaticResourceConfiguration.
#Configuration
#ConditionalOnBean(MyProperties.class)
public class StaticResourceConfiguration implements WebMvcConfigurer {
I'm trying to unit test a camel route. The route under test extends a custom abstract RouteBuilder (I know about favouring composition over inheritance - this is maintenance code). I've set up my test as #Roman Vottner did over here. Everything works (is initialized) until I hit the first abstract class up the hierarchy. It has an #Autowired class which wasn't initialized (is null) even though it was mocked and #Autowired when the test started. Any ideas on how to solve my injection problem?
#RunWith(CamelSpringRunner.class)
#BootstrapWith(CamelTestContextBootstrapper.class)
#ContextConfiguration(loader = AnnotationConfigContextLoader.class, classes = {FooRouteTest.ContextConfig.class})
#DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class FooRouteTest {
#Configuration
#PropertySource({"classpath:some.properties", "classpath:environment.properties"})
public static class ContextConfig extends CamelConfiguration {
#Bean
public UserServices userServices() {
return mock(UserServices.class);
} //and many more of the like
}
#Autowired
private UserServices userServices; //and all the others too
#Test
public void testAfoo() throws Exception {
//....
template.setDefaultEndpointUri("direct://getTheData");
template.sendBody(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode));
//...
}
}
in the abstract super class while debugging:
#Autowired
public ClientServices clientServices;
//...
String clientNumber=clientServices.getLoggedInNumber(); //clientServices is null and not mocked!
//...
Solved this by explicitly declaring FooRoute as a bean:
#Bean
public FooRoute fooRoute(){
return new FooRoute();
}
#Override
public List<RouteBuilder> routes() {
final List<RouteBuilder> routes = new ArrayList<>();
routes.add(fooRoute());
return routes;
}
I am struggling with testing #Cacheable within a Spring Boot Integration Test. This is my second day learning how to do Integration Tests and all of the examples I have found use older versions. I also saw an example of assetEquals("some value", is()) but nothing with an import statement to know which dependency "is" belongs to. The test fails at the second
This is my integration test....
#RunWith(SpringRunner.class)
#DataJpaTest // used for other methods
#SpringBootTest(classes = TestApplication.class)
#SqlGroup({
#Sql(executionPhase = ExecutionPhase.BEFORE_TEST_METHOD,
scripts = "classpath:data/Setting.sql") })
public class SettingRepositoryIT {
#Mock
private SettingRepository settingRepository;
#Autowired
private Cache applicationCache;
#Test
public void testCachedMethodInvocation() {
List<Setting> firstList = new ArrayList<>();
Setting settingOne = new Setting();
settingOne.setKey("first");
settingOne.setValue("method invocation");
firstList.add(settingOne);
List<Setting> secondList = new ArrayList<>();
Setting settingTwo = new Setting();
settingTwo.setKey("second");
settingTwo.setValue("method invocation");
secondList.add(settingTwo);
// Set up the mock to return *different* objects for the first and second call
Mockito.when(settingRepository.findAllFeaturedFragrances()).thenReturn(firstList, secondList);
// First invocation returns object returned by the method
List<Setting> result = settingRepository.findAllFeaturedFragrances();
assertEquals("first", result.get(0).getKey());
// Second invocation should return cached value, *not* second (as set up above)
List<Setting> resultTwo = settingRepository.findAllFeaturedFragrances();
assertEquals("first", resultTwo.get(0).getKey()); // test fails here as the actual is "second."
// Verify repository method was invoked once
Mockito.verify(settingRepository, Mockito.times(1)).findAllFeaturedFragrances();
assertNotNull(applicationCache.get("findAllFeaturedFragrances"));
// Third invocation with different key is triggers the second invocation of the repo method
List<Setting> resultThree = settingRepository.findAllFeaturedFragrances();
assertEquals(resultThree.get(0).getKey(), "second");
}
}
ApplicationContext, components, entities, repositories and service layer for tests. The reason why I do it this way is because this maven module is used in other modules as a dependency.
#ComponentScan({ "com.persistence_common.config", "com.persistence_common.services" })
#EntityScan(basePackages = { "com.persistence_common.entities" })
#EnableJpaRepositories(basePackages = { "com.persistence_common.repositories" })
#SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Cache config....
#Configuration
#EnableCaching
public class CacheConfig {
public static final String APPLICATION_CACHE = "applicationCache";
#Bean
public FilterRegistrationBean registerOpenSessionInViewFilterBean() {
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
OpenEntityManagerInViewFilter filter = new OpenEntityManagerInViewFilter();
registrationBean.setFilter(filter);
registrationBean.setOrder(5);
return registrationBean;
}
#Bean
public Cache applicationCache() {
return new GuavaCache(APPLICATION_CACHE, CacheBuilder.newBuilder()
.expireAfterWrite(30, TimeUnit.DAYS)
.build());
}
}
The repository under test....
public interface SettingRepository extends JpaRepository<Setting, Integer> {
#Query(nativeQuery = true, value = "SELECT * FROM Setting WHERE name = 'featured_fragrance'")
#Cacheable(value = CacheConfig.APPLICATION_CACHE, key = "#root.methodName")
List<Setting> findAllFeaturedFragrances();
}
The first problem with SettingRepositoryIT is, the #Mock anotation on the field settingRepository. This is paradox for any normal-test, integration-test or any else.
You should let Spring bring in the dependencies for the class-under-test, which is SettingRepository in your case.
Please look at this example how #Autowired is used for the class-under-test, which is OrderService in this example:
#RunWith(SpringRunner.class)
// ApplicationContext will be loaded from the
// static nested Config class
#ContextConfiguration
public class OrderServiceTest {
#Configuration
static class Config {
// this bean will be injected into the OrderServiceTest class
#Bean
public OrderService orderService() {
OrderService orderService = new OrderServiceImpl();
// set properties, etc.
return orderService;
}
}
#Autowired
private OrderService orderService;
#Test
public void testOrderService() {
// test the orderService
}
}
Go for the documentation with the full example: ยง 15. Integration Testing
The second problem is that you do not have to test #Cachable. You should only test your implementation. Here is a very good example from Oliver Gierke on how you should test it: How to test Spring's declarative caching support on Spring Data repositories?
In my case I wanted to validate the expression in the unless expression in the #Cacheable annotation, so I think it makes perfect sense and I'm not testing Spring's code.
I managed to test it without using Spring Boot, so it is plain Spring test:
#RunWith(SpringRunner.class)
#ContextConfiguration
public class MyTest {
private static MyCacheableInterface myCacheableInterfaceMock = mock(MyCacheableInterface.class);
#Configuration
#EnableCaching
static class Config {
#Bean
public MyCacheableInterface myCacheableInterface() {
return myCacheableInterfaceMock;
}
#Bean
public CacheManager cacheManager() {
return new ConcurrentMapCacheManager("myObject");
}
}
#Autowired
private MyCacheableInterface myCacheableInterface;
#Test
public void test() {
when(myCacheableInterfaceMock.businessMethod(anyString())).then(i -> {
List<MyObject> list = new ArrayList<>();
list.add(new MyObject(new Result("Y")));
return list;
});
myCacheableInterface.businessMethod("test");
verify(myCacheableInterfaceMock).businessMethod(anyString());
myCacheableInterface.businessMethod("test");
verifyNoMoreInteractions(myCacheableInterfaceMock);
}
}
In MyCacheableInterface I have the following annotation:
public interface MyCacheableInterface {
#Cacheable(value = "myObject", unless = "#result.?[Result.getSuccess() != 'Y'].size() == #result.size()")
List<MyObject> businessMethod(String authorization);
}
I have a test class which loads a test spring application context, now I want to create a junit rule which will setup some test data in mongo db. For this I created a rule class.
public class MongoRule<T> extends ExternalResource {
private MongoOperations mongoOperations;
private final String collectionName;
private final String file;
public MongoRule(MongoOperations mongoOperations, String file, String collectionName) {
this.mongoOperations = mongoOperations;
this.file = file;
this.collectionName = collectionName;
}
#Override
protected void before() throws Throwable {
String entitiesStr = FileUtils.getFileAsString(file);
List<T> entities = new ObjectMapper().readValue(entitiesStr, new TypeReference<List<T>>() {
});
entities.forEach((t) -> {
mongoOperations.save(t, collectionName);
});
}
}
Now I am using this rule inside my test class and passing the mongoOperations bean.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringTestConfiguration.class)
public class TransactionResourceTest {
#Autowired
private ITransactionResource transactionResource;
#Autowired
private MongoOperations mongoOperations;
#Rule
public MongoRule<PaymentInstrument> paymentInstrumentMongoRule
= new MongoRule(mongoOperations, "paymentInstrument.js", "paymentInstrument");
....
}
The problem is that Rule is getting executed before application context gets loaded, so mongoOperations reference is passed as null. Is there a way to make rules run after the context is loaded?
As far as I know what you are trying to achieve is not possible in such straight forward way because:
the rule is instantiated prior Spring's Application Context.
SpringJUnit4ClassRunner will not attempt to inject anything on the rule's instance.
There is an alternative described here: https://blog.jayway.com/2014/12/07/junit-rule-spring-caches/ but I think it would fall short in terms of what can be loaded into mongodb.
In order to achieve what you want to achieve, you would probably require a test execution listener that would inject whatever dependencies you require on your rule object.
Here's a solution, using some abstract super class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = SpringTestConfiguration.class)
public abstract class AbstractTransactionResourceTest<T> {
#Autowired
private ITransactionResource transactionResource;
#Autowired
private MongoOperations mongoOperations;
#Before
public void setUpDb() {
String entitiesStr = FileUtils.getFileAsString(entityName() + ".js");
List<T> entities = new ObjectMapper().readValue(entitiesStr, new TypeReference<List<T>>() {});
entities.forEach((t) -> {
mongoOperations.save(t, entityName());
});
}
protected abstract String entityName();
}
then
public class TransactionResourceTest extends AbstractTransactionResourceTest<PaymentInstrument> {
#Override
protected String entityName() {
return "paymentInstrument";
};
// ...
}