How to reuse my configurations and exclude another one in SpringBoot Test? - spring-boot

How can I reuse my main configurations but exclude some..?
Like:
#SpringBootApplication
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class,
ConfigOne.class,
ConfigTwo.class,
ConfigThree.class //I want to exclude the ConfigThree in my tests and use my own ConfigThreeTest.class
).run(args);
}
}
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class MyTests {
//test...
}
#Configuration
public class ConfigThreeTest {
//config...
}
In the sample above I want to exclude ConfigThree.class and use ConfigThreeTest.class

In your MyTests class, you can specify which class(es) to be used for the configuration.
#SpringBootTest(classes = {ConfigOne.class, ConfigTwo.class},webEnvironment = WebEnvironment.RANDOM_PORT)

Try using this
#SpringBootApplication(exclude = {demo.class})

Related

How can I exclude a specific #Configuration class from my Spring Boot application?

Spring Boot 2.3.12 (I can't update to a newer version for reasons out of my control).
I have defined my main application class with specific scan base packages like this:
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
}
)
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}
What I'm trying to accomplish is both:
A) include a package outside the application's base package (hence the org.otherpackage.ComponentScanMarker.class reference in the #SpringBootApplication annotation)
and
B) exclude the HateoasConfiguration class completely*.
I've also tried this:
#SpringBootApplication
#ComponentScan(
basePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
That results in HateoasConfiguration being loaded despite the excludeFilters.
Another option I tried:
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
exclude = HateoasConfiguration.class
)
That results in an exception at startup with the message:
The following classes could not be excluded because they are not auto-configuration classes:
- org.springframework.hateoas.config.HateoasConfiguration
I can't get it to work, no matter what combination of annotation properties I try. Either HateoasConfiguration gets loaded despite the attempt to exclude it, or #Components in org.otherpackage don't get loaded. I've looked at a few different similar questions and answers, but none of them include the need for both goals.
How can I accomplish both needs, to include multiple base packages for component scanning, and exclude a specific #Configuration class that's on the classpath?
* This question really has nothing to do with Spring HATEOAS, it's just an example of a #Configuration class that is on the classpath but I want Spring Boot to ignore. Here are the annotations present on that class (source code here):
#Configuration(proxyBeanMethods = false)
#EnablePluginRegistries({ LinkDiscoverer.class })
public class HateoasConfiguration {
Have you tried this ?
#SpringBootApplication(scanBasePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
exclude = { HateoasConfiguration.class }
)
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
}
)
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}
I've been looking for a solution for a similar problem myself. Dropping it here for future developers.
The underlying problem is that #SpringBootApplication performs a full component scan itself, so your own #ComponentScan annotation does not have the desired effect.
The solution for me was to eject #SpringBootApplication and just do what it does internally (with our desired modifications) as it's just a convenience shorthand.
For your case, try:
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(
basePackageClasses = {
MyApplication.class,
org.otherpackage.ComponentScanMarker.class
},
excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = HateoasConfiguration.class)
})
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.run(args);
}
}

Spring boot + cucumber in main scope. Can I autowire a step definition?

I believe this is a very particular case, but I am building some cucumber tests for some third-party applications we use.
Since I am not really testing my own application, I created a maven project and configured cucumber to run in the main folder (not the test folder).
This is my entrypoint class:
#SpringBootApplication
public class ExecutableMain implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(ExecutableMain.class, args);
}
#Override
public void run(String... args) {
// args logic...
JUnitCore.runClasses(MyCucumberTest.class);
}
}
And my test class:
#RunWith(Cucumber.class)
#CucumberOptions(
plugin = {"pretty", "html:target/cucumber", "json:target/cucumber/cucumber.json"},
glue = {"cucumber.app", "cucumber.steps"}
)
public class MyCucumberTest {
#AfterClass
public static void tearDown(){
// quit the browser
}
}
This currently works fine, but I want to add spring features to my tests.
Specifically, I want to autowire something in my cucumber steps.
Stepdefs:
public class MyStepdefs {
#Autowired
private ConfigProperties properties;
#Given("^Something")
public void example() {
//...
}
I searched around and found people saying I should add the ContextConfiguration annotation in the steps. I did it like so:
#ContextConfiguration(classes = ExecutableMain.class, loader = SpringBootContextLoader.class)
public class MyStepdefs {
But this resulted in a loop during start up.
Can I achieve what I need?
Ok, so I got it to work following https://stackoverflow.com/a/37586547/1031162
Basically I changed:
#ContextConfiguration(classes = ExecutableMain.class, loader = SpringBootContextLoader.class)
To:
#ContextConfiguration(classes = ExecutableMain.class, initializers = ConfigFileApplicationContextInitializer.class)
I am not 100% sure how/why it worked, but it did.

How to write #SpringBootTest for app with #ComponentScan and JPA repositories

I find it extremely hard to write #SpringBootTest if you use #ComponentScan and Jpa repositories. Can someone advice? This should be super-trivial stuff, but it's not documented anywhere.
#SpringBootApplication
#ComponentScan(
excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) },
basePackageClasses = {Main.class, Other.class})
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
and one of discovered Configuration classes has:
#Configuration
#EnableJpaRepositories("jpa")
Now I want to create test, which will enable ideally just a subset of JPA repositories AND exactly nothing else, unless I tell so. Namely, no configuration from production source code. This seems to be close to impossible to express. This is where I was able to get:
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(TestIT.TestConfig.class)
public class TestIT {
#Configuration
#EnableJpaRepositories("jpa")
#AutoConfigureDataJpa
#AutoConfigurationPackage
public static class TestConfig {
//here will be beans for test.
}
so this configuration produces error
java.lang.IllegalArgumentException: Not a managed type ...my jpa repository class
which probably means, that jpa package isn't among autoconfigured packages. No idea how to add it if it's even possible.
OK, another approach. Some sources recommends this:
#RunWith(SpringRunner.class)
#SpringBootTest
#Import(TestIT.TestConfig.class)
public class TestIT {
EnableJpaRepositories("jpa")
#EntityScan(basePackages = "jpa.entities")
//#TestPropertySource("classpath:application.properties")
#EnableTransactionManagement
public static class TestConfig {
//here will be beans for test.
}
but this one fails with caused by: java.lang.IllegalArgumentException: At least one JPA metamodel must be present!
Any hints?
First of all, there is no specific need to add #ComponentScan in your main application startup file. #SpringBootApplication is sufficient.
Now regarding the test cases:
I use powermockito all my test cases usually look like this:-
#RunWith(PowerMockRunner.class)
#PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
#PrepareForTest({ ContextProvider.class, ConfigurationUtils.class, Utils.class })
#FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class TestIT {
IN the clause #PrepareForTest mention all the classes which u would mention with the annotation #Mock.
for e.g.
No need to mention any of the repo(DAO layer interfaces where we write our queries) interfaces. So ur repo declaration would be like:
private SoftwareRepo softwareRepo;
And u would use it in while execution is
softwareInventoryRepo = PowerMockito.mock(SoftwareRepo.class);
OK, after a lot of searching (in vain) and even more trying, I think I have answer.
To recap:
you have main class (thanks #Bhushan Shinde):
#SpringBootApplication(scanBasePackageClasses = {Main.class, Other.class})
public class Main {
public static void main(String[] args) {
SpringApplication.run(Main.class, args);
}
}
and some configuration:
#Configuration
#EnableJpaRepositories("jpa")
public class Config
So to use SpringBootTest and configure everything from scratch for test, ignoring production configurations, you go:
#RunWith(SpringRunner.class)
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.NONE,
classes = TestIT.TestConfig.class)
//#Import(TestIT.TestConfig.class)
public class TestIT {
#Configuration
#AutoConfigureDataJpa
#EnableJpaRepositories("jpa") //fake package names, obviously
#EntityScan(basePackages = "jpa.entities")
// #TestPropertySource("classpath:application.properties")
#EnableTransactionManagement
public static class TestConfig {
//test related beans & config.
}
//tests here.
}
maybe there is something extra here, but after day of googling & trying this is good enough for me.

Spring Junit and annotation based autowiring

I added a junit test to a simple spring example but it fails to autowire the json service that I wrote.
What is needed to get autowiring to work in a spring JUnit tests?
To try the failing project out do ...
git clone https://bitbucket.org/oakstair/spring-boot-cucumber-example
cd spring-boot-cucumber-example
./gradlew test
Thanks in advance!
Application
#SpringBootApplication
#ComponentScan("demo")
public class DemoApplication extends SpringBootServletInitializer {
Service interface
#Service
public interface JsonUtils {
<T> T fromJson(String json, Class<T> clazz);
String toJson(Object object);
}
Service implementation
#Component
public class JsonUtilsJacksonImpl implements JsonUtils {
Test
#ContextConfiguration()
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan("demo")
public class JsonUtilsTest {
#Autowired
private JsonUtils jsn;
In your JsonUtilsTest you can't put a #ComponentScan on the class level here since it isn't a #Configuration class. With a #ContextConfiguration annotation like you are using here it is first looking for a static inner #Configuration class so add one of those with the #ComponentScan and it should work:
#ContextConfiguration()
#RunWith(SpringJUnit4ClassRunner.class)
public class JsonUtilsTest {
#Autowired
private JsonUtils jsn;
#Test
// Note: This test is not tested since I haven't got autowiring to work.
public void fromJson() throws Exception {
Integer i = jsn.fromJson("12", Integer.class);
assertEquals(12, (int) i);
}
#Test
// Note: This test is not tested since I haven't got autowiring to work.
public void toJson() throws Exception {
assertEquals("12", jsn.toJson(new Integer(12)));
}
#Configuration
#ComponentScan("demo")
public static class TestConfiguration {
}
}
EDIT: Or you can make Spring boot do the work for you by using the #SpringBootTest annotation with a SpringRunner instead:
#RunWith(SpringRunner.class)
#SpringBootTest
public class JsonUtilsTest {
Adding this to the test class fixed my problems!
#ContextConfiguration(classes = {DemoApplication.class})
Add #SpringBootTest
On your test class
And provide your SpringBootApplication class and Json utils class to the classes field of #SpringBootTest
It should look like this
#ContextConfiguration()
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes={<package>.DemoApplication.class, <package>.JsonUtil.class } )
#ComponentScan("demo")
public class JsonUtilsTest {

How to override spring's import annotation

I have a spring boot application. In the main class annotated with #SpringBootApplication, I have imported some configurations, using the import annotation.
#SpringBootApplication
#Import({ MyConfiguration.class })
public class MySpringBootApp {
public static void main(String[] args) {
new SpringApplicationBuilder(MySpringBootApp.class).build().run(args);
}
}
Now when I run my junit test class, annotated with "#RunWith(SpringRunner.class)", it loads the application, and the imported configuartion classes in the main class, are also loaded (that is MyConfiguration).
#RunWith(SpringRunner.class)
public class MyTest {
....
}
Is there a way to override the import, so that MyConfiguration is not loaded while running tests.
I understand you need to use a SpringRunner but you want to use a different configuration. In that case you simply annotate your Test class with #ContextConfiguration(classes = SomeConfigurationClass.class)
So it would look like this:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = SomeConfigurationClass.class)
public class MyTest {
....
}
If you do not want any Spring Container, just remove #RunWith(...)

Resources