I know that if I need to use a precise implementation TestExecutionListener, it will prevent the loading of the default TestExecutionListeners.
If my test class is like
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({MyCustomTestExecutionListener.class})
#ContextConfiguration(locations = { "classpath:test-ApplicationContext.xml" })
public class CabinetMembershipServiceImplTest {
...
}
MyCustomTestExecutionListener will be the only loaded Listener and it makes my tests execution fail.
When I launch my tests whitout specifying any TestExecutionListener and I dig in Spring's logs, I can find :
getDefaultTestExecutionListenerClassNames :
Loaded default TestExecutionListener class names from location [META-INF/spring.factories]:
[org.springframework.test.context.web.ServletTestExecutionListener,
org.springframework.test.context.support.DirtiesContextBeforeModesTestExecutionListener,
org.springframework.test.context.support.DependencyInjectionTestExecutionListener,
org.springframework.test.context.support.DirtiesContextTestExecutionListener,
org.springframework.test.context.transaction.TransactionalTestExecutionListener,
org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener]
So if I want to add my TestExecutionListener, I need to specify (in addition of the wanted implementation) all these default TestExectionListeners on my test class :
#RunWith(SpringJUnit4ClassRunner.class)
#TestExecutionListeners({
ServletTestExecutionListener.class,
DirtiesContextBeforeModesTestExecutionListener.class,
DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class,
SqlScriptsTestExecutionListener.class,
MyCustomTestExecutionListener.class})
#ContextConfiguration(locations = { "classpath:test-ApplicationContext.xml" })
public class CabinetMembershipServiceImplTest {
...
}
Is there a way of just adding one (or more) TestExecutionListener, whithout having to explicitly declare each listener from the default configuration nor "overriding" the default ones ?
import org.springframework.test.context.TestExecutionListeners.MergeMode;
#TestExecutionListeners(value = { MyCustomTestExecutionListener.class }, mergeMode = MergeMode.MERGE_WITH_DEFAULTS)
Is there a way of just adding one (or more) TestExecutionListener,
whithout having to explicitly declare each listener from the default
configuration nor "overriding" the default ones?
Yes, this is possible since Spring Framework 4.1 and is clearly documented both in the Javadoc for #TestExecutionListeners and in the Merging TestExecutionListeners section of the reference manual. The following example is taken directly from the reference manual.
#ContextConfiguration
#TestExecutionListeners(
listeners = MyCustomTestExecutionListener.class,
mergeMode = MERGE_WITH_DEFAULTS
)
public class MyTest {
// class body...
}
Regards,
Sam (author of the Spring TestContext Framework)
Related
I want to use a MongoRepository in a #DataMongoTest. I can't add it in #ContextConfiguration since it's an interface.
I ended up doing the following:
#DataMongoTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {MyService.class})
#EnableMongoRepositories("com.exmaple.path.to.mongorepos")
which works, but I don't like the fact that I need to set this String value of the base package. Interestingly, #EnableMongoRepositories alone doesn't work.
Is there another method to instantiate a MongoRepository in a unit test?
try replacing:
#ContextConfiguration(classes = {MyService.class})
#EnableMongoRepositories("com.exmaple.path.to.mongorepos")
to:
#Import({MyService.class})
I've got a fairly standard spring boot app which is built with gradle from several gradle modules. Here's the directory layout:
- root
- serviceA
- src/main/java
- org.example.serviceA
- ServiceAApplication.java
- serviceB
- serviceC
- common
- src/main/java
- org.example.common
- CommonSecurityConfiguration.java
What I would like to do is to include the CommonSecurityConfiguration class from the shared common module in serviceA. Note that ServiceAApplication and CommonSecurityConfiguration reside in different base packages.
I tried to use #Import(CommonSecurityConfiguration.class) on my ServiceAApplication, but that had no observable effect at all.
The only thing which worked was to annotate ServiceAApplication like so:
#SpringBootApplication(basePackages = { "org.example.serviceA", "org.example.common"})
public class ServiceAApplication { ... }
This approach works, but seems very coarse grained to me - it will import each and every component and configuration it finds in org.example.common.
Is there a better way to do this? Can I include individual classes into the component scan by listing them one by one?
Try to use
#Import(CommonSecurityConfiguration.class) above configuration class. So it would look like this:
#Configuration
#Import(CommonSecurityConfiguration.class)
public class ServiceAConfiguration { ... }
I believe what you are looking for is #CompnentScan("com.example"), this will tell Spring to look at all the files under the specified path recursively. (In this case it would be #ComponentScan("root"))
You find more info here: baeldun.com/spring-component-scanning
Hope this helps.
Since you want to control which components are brought in , we can make an annotation , let's call that annotation PickyComponentImport
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface PickyComponentImport{
}
Then on our SpringBootApplication annotation we can add a new filter which looks for this annotation.
#ComponentScan(basePackages = { "org.example.serviceA",
"org.example.common" }, includeFilters = #Filter(PickyComponentImport.class))
public class ServiceAApplication { ... }
Then we can just add that annotation on any class we want included
#Configuration
#PickyComponentImport
public class CommonSecurityConfiguration {
}
EDIT: I think if you go with this approach you can just componentScan basepackage as root.
I have a project that has two different runtimes in it (one a lambda and one a fargate). I have two different configs but only want one to run.
How do I exclude and include config classes? This didn't seem to work:
#SpringBootApplication(exclude = DynamoConfig.class)
And since they are in the same "path", I can't exclude just the package
com.cat.lakitu.runner
because the "persist" package will be excluded as well.
I would use the #ConditonalOnProperty annotation here on two configs, and add the properties in the main of one of the runtimes , take for example lambda (since you said each run uses a different one)
public static void main(String[] args) {
SpringApplication application = new SpringApplication(DemoApplication.class);
Properties properties = new Properties();
properties.put("lambda.application", "true");
application.setDefaultProperties(properties);
application.run(args);
}
then on the config needed when run time is lambda you can annotate the bean as such
#ConditionalOnProperty(
value="lambda.application",
havingValue = "true")
public class DyanmoConfig {
Then the other bean could have the following conditional properties
#ConditionalOnProperty(
value="lambda.application",
havingValue = "false",
matchIfMissing= true)
public class PersistConfig {
This way you only need to set the properties programatically in one of the two main's
There are different ways of solving this. Which one you choose depends on your specific use case and needs.
Using profiles: https://www.baeldung.com/spring-profiles
Example:
#Component
#Profile("runtime1")
public class DynamoConfig
Using conditional beans (multiple possibilities): https://reflectoring.io/spring-boot-conditionals/
#Component
#ConditionalOnProperty(
value="module.enabled",
havingValue = "true",
matchIfMissing = true)
public class DynamoConfig
In spring boot we may exclude specific auto-configuration classes such that they will never be applied. Example with annotation based configuration:
#SpringBootApplication(exclude = OneConfiguration.class)
What I want is to provide an environment variable with a value that will determine some auto configurations that will be excluded. Any ideas how to achieve that?
You can exclude this configuration class by default:
#SpringBootApplication(exclude = OneConfiguration.class)
then extend it and make it conditonal:
#Configuration
#ConditionalOnProperty(name = "one.configuration.condtion", havingValue = "some-value")
public class OneConfigurationConditional extends OneConfiguration { }
Create class extending Condition checking value of enviroment variable:
public class AutoConfigurationCondition extends Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
return System.getenv("EXCLUDE").equals("yes");
}
}
Then you can use it in Configuration class:
#Configuration
#Conditional(AutoConfigurationCondition.class)
#EnableAutoConfiguration(exclude = {OneConfiguration.class})
class AutoConfigurationExcludingConf {}
I managed to do what I wanted with profiles :-( So I have few files now - e.g. application-artemis.yaml and application-activemq.yaml. In each I've defined corresponding spring.autoconfigure.exclude property. I'm not proud with this solution though it works fine so far and looks more or less neat compared to other things that I did :-) .
What I've tried besides that:
Managing the value with an environment variable, e.g.:
spring:
autoconfigure:
exclude: ${ABC:com.example.Myautoconfiguration}
This does not work and I've even reported it as an issue. But it seems that I can't rely on expression for this property. Strange enough it works for other properties...
I've played around with the suggestion of #Randal Flagg but somehow I couldn't get it up and running - I'm using #SpringBootApplication, the documentation says that I can use EnableAutoConfiguration only once, etc.
I've tried with my own TypeFilter but this is also not an option - autoconfigurations have special treatment during component scan and the design does not seem very extensible there. At least I could not find a nice way to plug in.
All my unit tests have the same header with Spring annotations:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring/spring-master.xml"})
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
#Transactional()
I moved them to base class and all my tests now extend it:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:spring/spring-master.xml"})
#TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = false)
#Transactional()
public class DomainObjectBaseTest {
...
public class SomeTest extends DomainObjectBaseTest {
Now when I'm running all tests I'm getting for the DomainObjectBaseTest:
java.lang.Exception: No runnable methods
My question is how can I avoid it? I can remove #RunWith, but in this case, I'll have to apply the annotation to all other tests. Too many extra code,don't like it.
Probably, as an alternative, I can somehow in Spring group annotations, name it, and refere to the group from my tests, please tell me if it is possible. In this case I'll remove base class at all.
And probably there is a way to tell that this class should not be tested. I'm using JUnit4.
Make your base class abstract. The problem is that I believe it is not abstract, so test runner tries to run it and fails for this (IMHO stupid) reason. But it ignores abstract classes.