How to use #componentScan in #springBootTest class - java

I am creating a test class for my spring application. The main application has following configurations
#SpringBootApplication(scanBasePackages = {"com.graphql.demo"})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I need to scan that graphql package for my tests. I tried by added the #ComponentScan in my test class.
#ExtendWith({SpringExtension.class,MockitoExtension.class})
#ComponentScan(basePackages = {"com.graphql.demo"})
#SpringBootTest
public class SampleTestClass {
// my code
}
But it seems it is not reading that graphql packages which I need. How can I scan the base packages in spring boot test?

A single #SpringBootTest should be enough. #SpringBootTest will find the nearest #SpringBootApplication (looking up in the packages hierarchy), and use all of it's configuration.
Make sure that your test resides in the sub-package of the main class (MyApplication) package.
If your MyApplication resides in com.graphql.demo, your test should be in com.graphql.demo.**, otherwise the #SpringBootTest won't find the main class and it's configuration.

Related

Springboot loading wrong config despite being explicit

I have the following Configuration classes, one in the main package and one in the test package.
Main
#Configuration
public class DynamoConfiguration {
Test
#TestConfiguration
public class DynamoTestConfiguration {
Unit Test
#ActiveProfiles(profiles = "test")
#ContextConfiguration(classes = {DynamoTestConfiguration.class})
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest
public class DynamoClientTest {
Yet, it's still loading DynamoConfiguration and causing failures when I only want the DynamoTestConfiguration to be loaded. How can I ensure that happens?
When using #SpringBootTest, then your application is started, along with any #Configuration classes on the classpath. Spring has no idea that DynamoConfiguration is special and you don't want to load it.
As a way around this, you can use profiles:
#Profile("prod")
#Configuration
public class DynamoConfiguration {
and in your test, add !prod to your #ActiveProfiles:
#ActiveProfiles(profiles = "!prod,test")
#ContextConfiguration(classes = {DynamoTestConfiguration.class})
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest
public class DynamoClientTest {
This should avoid that DynamoConfiguration gets loaded in the test.

Spring Boot scanBasePackages unable to find beans from dependency

I have following:
#SpringBootApplication(scanBasePackages = {"com.my.package","com.my.package.mylibrary"})
#EnableAsync
#EnableSwagger2
#ServletComponentScan
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
public class MySpringBootApplication {....}
This application has package com.my.package, and it also has a library dependency containing spring beans I want to autowire in this application, and those beans are in package com.my.package.mylibrary inside library.
So I have put both for scanBasePackages. But Spring is not able to find beans from the library?
Edit:
From library, I have:
package com.my.package.mylibrary.repository;
....
public interface MyRepository extends JpaRepository<..., ....> {....}
In application, I have:
package com.my.package.controller;
....
#RestController
public class MyController {....}
MySpringBootApplication resides in com.my.package.
Error:
Exception in thread "main" java.lang.NoClassDefFoundError: com/my/package/mylibrary/repository/MyRepository
at com.my.package.MySpringBootApplication.main(MySpringBootApplication.java:32)
Caused by: java.lang.ClassNotFoundException: com.my.package.mylibrary.repository.MyRepository
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
I added #EnableJpaRepositories for repository package. Now I see error related to entity MyEntity which MyRepository is based upon.
"java.lang.TypeNotPresentException: Type com.my.package.mylibrary.domain.MyEntity not present
So I added #EntityScan for "com.my.package.mylibrary.domain", but that makes application stuck infinitely.
First of all, you don't need to add scanBasePackages attribute in #SpringBootApplication as the base package is com.my.package.
If package is totally different, then you could have added it.
Spring Boot will automatically pick up the bean if the base package is same.
There is something called as separation of concerns that you should follow when you are writing code.
Update your MySpringBootApplication class to this :
#SpringBootApplication
#ServletComponentScan
public class MySpringBootApplication {....}
Create a separate config for asynchronous method execution.
#Configuration
#EnableAsync
public class AsynchronousConfig {.....}
Create a separate config class for Swagger 2.
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {....}
Create separate config to exclude configuration.
#Configuration
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
public class ExcludeConfigurationFile {....}
Note: Spring boot auto configuration will automatically pick up these #Configuration files
This should work.
You might want to scan the packages seperately and change your JpaRepository to CrudRepository. The configuration to seperate the layer is as follow.
#SpringBootApplication(scanBasePackages = {"com.my.package.controller"})
#EnableJpaRepositories(basePackages = {"com.my.package.mylibrary.repository"})
#EntityScan(basePackages = {"com.my.package.mylibrary.domain"})
public class MySpringBootApplication {
public static void main(String[] args) {
MySpringBootApplication.run(MySpringBootApplication.class, args);
}
}

Testing Spring Boot Library Modules

I got a multi module project where not every module is actually an application but a lot of them are libs. Those libs are doing the major work and I want to test them where they are implemented. The current dependencies of the libs:
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
In the main source is a class with #Configuration and a single bean:
#Bean public String testString() { return "A Test String"; }
I got 2 test classes:
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles({"default", "test"})
public class Test1 {
#Test
public void conextLoaded() {
}
}
-
#RunWith(SpringRunner.class)
#SpringBootTest
#ActiveProfiles({"default", "test"})
public class Test2 {
#Autowired
private String testString;
#Test
public void conextLoaded() {
}
}
The first test works. The second does not. There is not #SpringBootApplication anywhere in that project so in the same package as the Tests I added a test configuration:
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan("com.to.config")
public class LibTestConfiguration {
}
And it does not work. Same for classes that are #Service. They are not in the context. How can I make it behave like a normal Spring boot application without it actually beeing one and load the configs and contexts from the configurations files I need? The default and test profile share most of their properties (for now) and I want them to be loaded like I would start a tomcat.
I switched to JUnit 5 and made it kinda work... So if you want to test Database stuff:
#DataMongoTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles({"default", "test"})
class BasicMongoTest { ... }
Lets you autowire all repositories and mongo template
Initializes with apllicaton.yml config
Does NOT initialize or configure interceptors
Full application context test if you have a class with #SpringBootApplication in your classpath (Can be an empty test main in your test context)
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles({"default", "test"})
public class FullContextTest { ... }
Initializes the full context with all configs and beans
Should not be done if not necessary as it loads all the application context and kinda defeats the purpose of unit tests to only activate whats needed.
Test only specific components and configs:
#SpringBootTest(classes = {Config1.class, Component1.class})
#EnableConfigurationProperties
#ExtendWith(SpringExtension.class)
#ActiveProfiles({"default", "test"})
public class SpecificComponentsTest { ... }
Initializes the context with only the Config1 and Component1 classes. Component1 and all beans in Config1 can be autowired.

Spring boot test configuration not being picked

I am writing an integration test for my application, and want to use a custom webmvc configuration for my tests
I have three classes in my base package com.marco.nutri:
Application(which is annotated with #SpringBootApplication)
MvcConfig(#Configuration and #EnableWebMVC)
SecurityConfig(#Configuration and #EnableWebSecurity)
My test is in the package br.com.marco.nutri.integration.auth:
#RunWith(SpringRunner.class)
#SpringBootTest(classes={Application.class, WebMvcTestConfiguration.class, SecurityConfig.class})
public class ITSignup {
//Test code
}
I have a test config class in the package com.marco.nutri.integration:
#TestConfiguration
#EnableWebMvc
public class WebMvcTestConfiguration extends WebMvcConfigurerAdapter {
//Some configuration
}
But when I run my test, the MvcConfig.class is picked instead of WebMvcTestConfiguration.class
What am I doing wrong?
you can annotate your test configuration with #Profile("test") and your real one with #Profile("production")
and in your properties file put the property spring.profiles.active=production and in your test class put #Profile("test"). So when your application starts it will use "production" class and when test stars it will use "test" class.
from documentation
Unlike regular #Configuration classes the use of #TestConfiguration
does not prevent auto-detection of #SpringBootConfiguration.
Unlike a nested #Configuration class which would be used instead of a
your application’s primary configuration, a nested #TestConfiguration
class will be used in addition to your application’s primary
configuration.

Best way to exclude unit test #Configurations in Spring?

In my Spring Boot project, I have a main class annotated with #SpringBootConfiguration. I also have some unit tests that use #SpringApplicationConfiguration that points to an inner class that defines a Spring context for usage in my unit test (using some mocks).
I now want to write an integration test that starts the full context of my application. However, this does not work as it also picks up the Spring contexts that are defined as inner classes in other unit tests.
What would be the best way to avoid that? I did see the exclude and excludeName properties on #SpringBootConfiguration, but I am unsure how to use them.
UPDATE:
Some code to explain the problem more:
My main class:
package com.company.myapp;
#SpringBootApplication
#EnableJpaRepositories
#EnableTransactionManagement
#EntityScan(basePackageClasses = {MyApplication.class, Jsr310JpaConverters.class})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I have a unit test for Spring REST Docs:
package com.company.myapp.controller
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration
#WebAppConfiguration
public class SomeControllerDocumentation {
#Rule
public final RestDocumentation restDocumentation = new RestDocumentation("target/generated-snippets");
// Some test methods here
// Inner class that defines the context for the unit test
public static class TestConfiguration {
#Bean
public SomeController someController() { return new SomeController(); }
#Bean
public SomeService someService() { return new SomeServiceImpl(); }
#Bean
public SomeRepository someRepository() { return mock(SomeRepository.class);
}
So the unit test uses the context defined in the inner class. Now I want a different test that tests if the "normal" application context of the app starts up:
package com.company.myapp;
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(MyApplication.class)
#WebAppConfiguration
public class MyApplicationTest {
#Autowired
private ApplicationContext applicationContext;
#Test
public void whenApplicationStarts_thenContextIsInitialized() {
assertThat(applicationContext).isNotNull();
}
}
This test will now not only wire the stuff it should, but also the beans from the SomeControllerDocumentation.TestConfiguration inner class. This I want to avoid.
You could use profiles: annotate the relevant configuration beans with #Profile("unit-test") and #Profile("integration-test") and inside the tests specify which profile should be active via #ActiveProfiles.
However, it sounds like you could avoid the problem altogether just by reorganising your configuration structure. It's hard to assume anything without seeing any code, though.
Since Spring Boot 1.4, the problem can be avoided by annotation the configuration in the unit tests with #TestConfiguration
I think you talk about #Ignore

Categories

Resources