Modify the bean created in main application context during Integration test - spring-boot

In my springboot application I am performing Integration tests using the following class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = AccountLoadApplication.class,
loader = SpringApplicationContextLoader.class)
#WebIntegrationTest(randomPort = true)
public class LoaderTest {
AccountLoadApplication.class is a spring boot main class and the actual application has a bean defined like below:
#Bean
public ResourceLoader recapMvsFileResourceLoader() {
return new RemoteFileResourceLoader(remoteHostProperties(), new SFTPRemoteFileService());
}
Also I have a Test Configuration class like below
#Configuration
public class AtddTestConfig {
#Bean
public ResourceLoader mvsFileResourceLoader() {
ResourceLoader recapMvsFileResourceLoader =
new RemoteFileResourceLoader(remoteHostProperties(), new FakeSFTPRemoteFileService());
return recapMvsFileResourceLoader;
}
My Idea is that I want to override the bean created in the main application using the new bean defined in the test Configuration file.
But during integration tests the main application bean is considered instead of the bean defined in the test application context?
Is There any other way to achieve what i am trying to achieve ?
Additional Info:
Here are the beans defined in my Application configuration class
#Bean
public RemoteFileService remoteFileService() {
return new SFTPRemoteFileService();
}
#Bean
public ResourceLoader recapMvsFileResourceLoader() {
return new RemoteFileResourceLoader(remoteHostProperties(), remoteFileService());
}
Here are the beans defined in my Test configuration class
#Bean
#Profile("local")
#Primary
public RemoteFileService remoteFileService() {
return new FakeSFTPRemoteFileService();
}
Still the production bean is only created instead of this primary bean.

Use #Profile annotation to enable testing bean only in test context
Use #Primary annotation on testing bean, so that spring would use test bean instead of production one.
Here is my Github repository with working example using this mechanism.

Maybe when you add your test configuration as parameter for #ContextConfiguration it resolves problem, e.g.
#ContextConfiguration(classes = {AccountLoadApplication.class, AtddTestConfig.class},
loader = SpringApplicationContextLoader.class)

Along with the other changes suggested by #luboskrnac, you have to declare #ActiveProfiles; otherwise, your local profile is simply ignored.
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles("local")
#SpringApplicationConfiguration(AccountLoadApplication.class)
#WebIntegrationTest(randomPort = true)
public class LoaderTest { /* ... */ }
Note that the above assumes that your AtddTestConfig class gets picked up via component scanning by your AccountLoadApplication class.

Related

#ConfigurationProperites not binding to property source after upgrading to Spring Cloud Hoxton.SR7

I have a #ConfigurationProperties class that is no longer binding to a YML property source that gets resolved via Spring Cloud Config after upgrading to Hoxton.SR7. This code works fine using Hoxton.SR4 with the latest Spring Boot 2.2.9.RELEASE. Now, my properties are not bound and I'm receiving NPEs when I try to reference them. Following is a snapshot of my code:
#Configuration
public class MyConfiguration {
#Bean
public MyPropertiesBean myPropertiesBean() {
return new MyPropertiesBean();
}
}
#ConfigurationProperties(prefix = "com.acme.properties")
#Validated
public class MyPropertiesBean {
...
}
In src/main/resources/META-INF/spring.factories:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.acme.MyConfiguration
Any ideas why my #ConfigurationProperties class doesn't bind after upgrading Spring Cloud to Hoxton.SR7?
You're mixing two ways of binding properties: class and method.
Using a method and #Bean annotation:
#Configuration
public class MyConfiguration {
#Bean
#ConfigurationProperties(prefix = "com.acme.properties")
#Validated
public MyPropertiesBean myPropertiesBean() {
return new MyPropertiesBean();
}
}
This will create MyPropertiesBean and store it inside the application context for you to inject.
Class level bean declaration also creates a bean for you:
#Configuration
#ConfigurationProperties(prefix = "com.acme.properties")
#Validated
public class MyPropertiesBean {
...
}
This will also store a bean.
Although, you should be getting a runtime error when you try to inject MyPropertiesBean as now in your case there's two beans of the same type and Spring cannot resolve with only the type.

How to get #Configuration files in Spring to run

I'm writing a micro service using dependency injection, and I have a Java class annotated with #Configuration which creates all my beans. However, my autowired fields are not detecting the beans. How do I get my #Configuration class to run before the application starts?
I tried annotating the classes with the Autowired fields as #ContextConfiguration(classes = Config.class), but this didn't work.
My spring configuration file:
#Configuration
public class Config {
#Bean
public AmazonDynamoDB amazonDynamoDB() {
return ...
}
#Bean
public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB) {
return ...
}
}
I expect the Configuration file to be run and the beans injected, but the beans are not being detected.
There's no main method, since I'm writing this in a service which is created using dependency injection in another service. I'm not sure where I'd tell my application to use my Config file.
probably place a #EnableConfigurationProperties( {Config.class}) above your #SpringBootApplication main class.

Spring annotation to initialize a bean during Autowire

Do we have a spring annotation that provides an option to initialize a bean (not a component) if not available through default constructor while autowiring?
If yes, that will be awesome. I am tired of initializing beans in some configuration class using default constructor and it occupies space.
I am doing this currently
#Bean
public Test test() {
return new Test();
}
Expecting:
Sometime like:
#Autowire(initMethodType=constructor)
private Test test:
If no, was there no real need of such annotation or any technical limitation?
You have to use #Bean annotation inside an #Configuration class.
Check the following link
https://docs.spring.io/spring-javaconfig/docs/1.0.0.M4/reference/html/ch02s02.html
#Configuration
public class AppConfig {
#Bean
public TransferService transferService() {
return new TransferServiceImpl();
}
}
You could annotate your class with #Component, like this:
#Component
public class Test {
...
}
Whereever you need that class, you could then simply autowire it:
#Autowired
private Test test;
You would just have to make sure, that you use #ComponentScan to pick up that bean. You can find more information on that here.

BeanNotOfRequiredTypeException in Spring boot test runner and #Rule

I am writing an integration test using spring runner, and have created a TestRule implementation and used it through #Rule. But I try to create a bean of that implementation, I get BeanNotOfRequiredTypeException.
Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'localDynamoDB' is expected to be of type 'com.wickes.dynamo.local.LocalDynamodb' but was actually of type 'com.sun.proxy.$Proxy114'
My test class is:
#ComponentScan(basePackages = "com.wickes.stock")
#Configuration
public class TestConfig {
#Bean
public LocalDynamodb localDynamoDB() {
return new LocalDynamodb();
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestConfig.class)
public class StockListenerTest {
#Rule
#Autowired
public LocalDynamodb localDynamodb;
#Test
public void test() {
}
}
and my config is:
My LocalDynamodb is
public class LocalDynamodb extends ExternalResource {
}
M. Deinum is correct: LocalDynamodb is being proxied by interfaces, which you do not want.
Thus, you have two options:
Convert LocalDynamodb to an interface, implement the interface, and register the implementation as the bean.
Switch from dynamic interface-based proxies (the default) to class-based proxies. How you perform the switch depends on how the proxies are being created, but since we can't see the rest of your Spring configuration we don't know how to advise you there.
Regards,
Sam (author of the Spring TestContext Framework)
I had a similar problem when trying to inject an AspectJ proxied object, then I found that you can add the #Scope annotation to LocalDynamodb:
#Component
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ...
More details here

How do I post-process beans of #Configuration classes that define more #Beans in JavaConfig?

In Spring XML, I can define a bean that instantiates a class annotated with #Configuration. When I do, that bean is post-processed. Any methods inside that class with #Bean are also added to the container. How do I perform a similar post-processing in JavaConfig?
Here's the XML version:
<bean id="test" class="com.so.Test">
<property name="prop" value="set before instantiating #Beans defined in Test"/>
</bean>
The associated Test class:
#Configuration
class Test {
private String prop;
void setProp(final String prop) {
this.prop = prop;
}
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
If I use Spring XML Config, both test and needThisBeanToo are available in the container. needThisBeanToo is added via a BeanPostProcessor, though I can't recall which one. If I use JavaConfig, only test is available in the container. How do I make needThisBeanToo available to the container? #Import would work, except that prop being set is required for needThisBeanToo to be initialized correctly.
The part that makes all of this complicated is that Test is vended from a library I'm consuming. I don't control Test, nor can I change it. If I drive it from JavaConfig, it would look like this:
#Configuration
class MyConfiguration
{
#Bean
Test test() {
Test test = new Test();
test.setProp("needed to init `needThisBeanToo` and others");
return test;
}
}
The JavaConfig example does not instantiate needThisBeanToo despite it being defined in Test. I need to get needThisBeanToo defined, preferably without doing it myself, since I don't want to copy code I don't own. Delegation isn't attractive, since there are a number of subsequent annotations/scopes defined on needThisBeanToo (and others defined inside Test).
Your problem is is that you're ignoring the #Configuration annotation completely. Why is that?
When code reaches this line Test test = new Test(); it just doesn't do anything with #Configuration. Why? Because annotation is not something that a constructor is aware of. Annotation only marks some meta-data for the class. When spring loads classes it searches for annotations, when you call a constructor of a class, you don't. So the #Configuration is just ignored because you instantiate Test with new Test() and not through spring.
What you need to do is to import Test as a spring bean. Either via XML as you showed in your question OR using #Import. You problem with prop is that the setter isn't called because that's just not the way to do it. What you need to be doing is either do something like that:
#Configuration
class Test {
private String prop = "set before instantiating #Beans defined in Test";
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
Or to create a property in spring (this is a different subject) and inject the value:
#Configuration
class Test {
#Autowired
#Value("${some.property.to.inject}") // You can also use SPeL syntax with #{... expression ...}
private String prop;
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
You can also create a bean of type String and inject it as follows:
#Configuration
class Test {
#Autowired
#Qualifer("nameOfBeanToInject")
private String prop;
#Bean
NeedThisBean needThisBeanToo() {
return new NeedThisBean(prop);
}
}
In the last case you can define your original MyConfiguration with this bean:
#Configuration
#Import(Test.class)
class MyConfiguration
{
#Bean(name = "nameOfBeanToInject")
String test() {
return "needed to init `needThisBeanToo` and others";
}
}
In any case you have to import Test either using #Import or as a normal XML bean. It won't work by calling the constructor explicitly.
Here's a way to handle vended #Configuration classes that require some properties to be set prior to creating their #Beans:
Vended #Configuration class:
#Configuration
class Test {
private String property;
public setProperty(final String property) {
this.property = property;
}
#Bean
PropertyUser propertyUser() {
return new PropertyUser(property);
}
#Bean
SomeBean someBean() {
// other instantiation logic
return new SomeBeanImpl();
}
}
Here's the consuming #Configuration class:
#Configuration
class MyConfig {
#Bean
static String myProperty() {
// Create myProperty
}
/**
* Extending Test allows Spring JavaConfig to create
* the beans provided by Test. Declaring
* Test as a #Bean does not provide the #Beans defined
* within it.
*/
#Configuration
static class ModifiedTest extends Test {
ModifiedTest() {
this.setProperty(myProperty());
}
#Override
#Bean
SomeBean someBean() {
return new SomeBeanCustomImpl(this.propertyUser());
}
}

Resources