How to override spring's import annotation - spring-boot

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(...)

Related

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.

#EnableAutoConfiguration on AbstractIntegrationTest possible?

Having lots of Integration-Test Implementations like this:
// no #Annotations at all
class SomeIntegrationTest extends AbstractIntegrationTest {
...
}
using (Spring Boot 1.5, JUnit 5)
#SpringBootTest(classes = {CoreConfiguration.class, RestTemplateAutoConfiguration.class, JacksonAutoConfiguration.class})
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#Transactional
public abstract class AbstractIntegrationTest {
...
}
this is always failing with
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'javax.persistence.EntityManagerFactory' available
unless I annotate every IntegrationTest-Implementation with
#EnableAutoConfiguration
class SomeIntegrationTest extends AbstractIntegrationTest {
...
}
I wonder why I cannot #EnableAutoConfiguration the AbstractIntegrationTest and be done with it.
(When doing so, it fails with IllegalArgumentException: No auto-configuration attributes found. Is package.SomeIntegrationTest annotated with EnableAutoConfiguration?)
Our normal Apps look like this:
#SpringBootApplication
#Import({CoreConfiguration.class, OtherConfiguration.class})
public class WebApp {
here the #SpringBootApplication obviously implies #EnableAutoConfiguration but I would like to avoid annotating each and every *IntegrationTest with this and instead configure it once on the AbstractIntegrationTest.
Is this fighting against spring-boot in any way or is there some way to achieve this? Thanks.
You could create update your AbstractIntegrationTest abstract class to have a small inner configuration class e.g. TestConfiguration which is loaded using the #Import(TestConfiguration.class) annotation.
#SpringBootTest(classes = {CoreConfiguration.class, RestTemplateAutoConfiguration.class, JacksonAutoConfiguration.class})
#ExtendWith(SpringExtension.class)
#AutoConfigureMockMvc
#Transactional
#Import(AbstractIntegrationTest.TestConfiguration.class) // <---- import the configuration
public abstract class AbstractIntegrationTest {
#EnableAutoConfiguration
// Any other applicable annotations e.g. #EntityScan
static class TestConfiguration {
}
....
}

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 {

Execute CommandLineRunner outside #SpringBootApplication

This is based on https://spring.io/guides/gs/accessing-data-jpa/
I tried to move demo() in a different class in a different package (Application still on top of the filesystem hierarchy)
How do I make demo() run when i boot the project?
Application.java
package com.company.app
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
CommandLineRunner.java
package com.company.app.runner
public class Test {
#Bean
public CommandLineRunner demo() {
System.out.print("Run 1");
return (args) -> {
System.out.print("Run 2");
};
}
}
Add #Configuration to the Test class so that it is picked up when the classpath is scanned.
I haven't seen a Lambda for the CommandLineRunner before. Very nifty and saves having to create a class that specifically implements the CommandLineRunner.
Otherwise, you could implement CommandLineRunner to test and annotate as #Component.
#Component
public class ApplicationLoader implements CommandLineRunner {
#Override
public void run(String... strings) throws Exception {
System.out.print("Run 2");
}
}
* Update *
Answering the question in the comment with this update as I have more room to type...
#SpringBootApplication composes those other annotations as you indicated but those annotations are only applied to the specific class that it is defined on. The #Configuration annotation is telling Spring that the class defines beans that should be managed by the application context. The #ComponentScan tells spring to look through the classpath for classes that have specific annotations (e.g. #Component, #Service, #Configuration) and then act on those classes based on the type of annotation. the #EnableAutoConfiguration is the magic that loads appropriate beans based on the project dependencies (e.g. if mongo driver is on the classpath then create a MongoTemplate).

Spring junit error single matching bean but found 2

I have a test class, and have created a java config class to use with this class.. But im having issues as other tests seem to throw up found two instances of bean in configuration...
my test class:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=TestConfiguration.class)
public class ListenerTest {
// various tests.. just basic stuff..
}
#Configuration
public class TestConfiguration {
#Bean
public MyListsner ListenerImpl() {
return Mockito.mock(MyListsner .class);
}
}
Now for this test class passes fine when i use a mock as above. My other test classes seem to fail and they are as follows:
test class which fails...
This class throws the error
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes=GeneratorTestConfiguration.class)
#Transactional
public class GeneratorTest {
// various tests
}
Main config
#Configuration
#Import({
BaseConfiguration.class,
CoreBaseConfiguration.class
})
#Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
#EnableTransactionManagement(proxyTargetClass = true)
#EnableJpaRepositories(basePackages={
"com.persistence.repository"
})
#ComponentScan({ // where the components are
"com.tests"
})
public class GeneratorTestConfiguration {
}
I dont know why, when i add listener mock to the above class ListenerTest, the toher tests fail, as im being specific in those classes to use the relevant configuration when autowiring.
Seems the bean was defined twice.

Resources