prevent component scan for unit test - spring

I have a class as following
#ComponentScan(basePackages = { "com.abc.def" })
#Configuration
public class ClassUnderTest(){
#Bean
public void createSomeBean()
{
}
}
I am unit testing this class using
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = ClassUnderTest.class)
public class someUnitTest()
{}
How do I prevent component scan from happening when creating a test context from ClassUnderTest

#ComponentScan(basePackages = "com.abc.def",
excludeFilters = {
#Filter(type = ASSIGNABLE_TYPE,
value = {
ClassUnderTest.class
})
})

Related

How to inject certain properties values during junit to a specific test

My Spring Boot app has some test that are reading their properties from the application.yml that is in the test folder.
cat:
maxAge:30
maxNameSize:10
all is working fine, but I like that in certain tests, other values will be injected:
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {
Cat.class
})
#SpringBootTest
public class CatTest {
#Test
public void testX(){
//inject maxAge=90
// use maxNameSize from the application.yml
....
#Test
public void testZ(){
//inject maxNameSize=5
// use maxAge from the application.yml
....
}
Changing properties on method level is not supported by Spring at this moment.
You can use nested classes to accomplish this.
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {
Cat.class
})
#SpringBootTest
public class CatTest {
#Nested
#SpringBootTest(properties = "cat.maxAge=90")
public class NestedTestX {
#Test
void testX() {
//noop
}
}
#Nested
#SpringBootTest(properties = "cat.maxNameSize=5")
public class NestedTestZ {
#Test
void testZ() {
//noop
}
}
}

ComponentScan.Filter not filtering #Configuration class in spring boot

ComponentScan.Filter not filtering #Configuration class. I'm using spring boot 2.2.12 with spring-context 5.2.12.
SpringBoot class
#EnableMBeanExport
#ComponentScan(basePackages = "com.init”,
excludeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = com.init.server.ServerAConfig.class)})
#SpringBootApplication()
public class MyApplication extends SpringBootServletInitializer {
public static void main(String[] args) throws IOException {
SpringApplication.run(MyApplication.class);
}
}
Under the basepackage com.init, there is a configurtion class ServerAConfig.
#Configuration
#ComponentScan(basePackages = {"com.execute.server”})
public class ServerAConfig {
}
Under package com.execute.server I have class MyServerA.java
My expectation was, MyServerA will not be available in the ApplicationContext
for (String beanName : applicationContext.getBeanDefinitionNames()) {
System.out.println(beanName);
}
but when i run the above print after the boot up it shows MyServerA there in the ApplicationContext. My expectation was MyServerA will not be initialized.
Also tried with different FilterType.
I think you need to get rid of the #SpringBootApplication() annotation, it has a component scan built in that will scan everything in the directory the class is in:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(excludeFilters = { #Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public #interface SpringBootApplication {
// ...
}
So you either add the annotations above that you want to keep and drop the #SpringBootApplication().
Or you can use the scanBasePackages or scanBasePackageClasses of the #SpringBootApplication() annotation and drop your own #Componentscan instead. I think the former method would be better, because this would be tedious.
You could also move the class you want to exclude so it is easier to define what you want to scan without scanning it using the second method.
Use AutoConfigurationExcludeFilter to filter auto configurations classes
#EnableMBeanExport
#ComponentScan(basePackages = "com.init”,
excludeFilters = {
#ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)})
#SpringBootApplication()
public class MyApplication extends SpringBootServletInitializer {
public static void main(String[] args) throws IOException {
SpringApplication.run(MyApplication.class);
}
}

#EnableTransactionManagement breaks my tests

So, I have a Spring Boot app. Recently I added an aspect I would like to apply after #Transaction aspect handler. So on my main app class I added #EnableTransactionManagement with lowering the order for transactions.
#SpringBootApplication
#EntityScan(basePackageClasses = { Application.class, Jsr310JpaConverters.class })
#EnableAutoConfiguration(exclude = RepositoryRestMvcAutoConfiguration.class)
#EnableSpringDataWebSupport
#EnableScheduling
// Need to lower the transaction aspect priority in order to SyncAspect have a higher prio
// The lower the number the higher the priority, defaults to HIGHEST_PRECEDENCE = -2147483648
#EnableTransactionManagement(order = Ordered.HIGHEST_PRECEDENCE + 2)
public class Application extends SpringBootServletInitializer {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
...
The order here does not matter (the test fails even when I just add #EnableTransactionManagement without any additional config) but after adding this annotation one of our tests failed. It is a #WebMVCTest and now it returns 404 for all the requests made in the test. Here is the test class declared.
#RunWith(SpringRunner.class)
#WebMvcTest(controllers = OptionMenuController.class)
#WithMockUser(roles = { "ENTERPRISE_USER" })
#ActiveProfiles(profiles = {"develop"})
public class OptionMenuControllerTest extends AbstractFullFeatureSetControllerTest<OptionMenu> {
...
#Import(DummySecurityConfiguration.class)
public abstract class AbstractFullFeatureSetControllerTest<EntityType extends Identifiable>
extends AbstractControllerTest<EntityType>
...
#TestConfiguration
public class DummySecurityConfiguration {
#Bean(name = "store_based_auth")
public UserDetailsService storeBasedAuthService() {
return Mockito.mock(UserDetailsService.class);
}
#Bean(name = "webui")
public UserDetailsService webUiService() {
return Mockito.mock(UserDetailsService.class);
}
#Bean(name = "integration_api_auth")
public UserDetailsService integrationApiAuthService() {
return Mockito.mock(UserDetailsService.class);
}
#Bean(name = "onboarding_api_auth")
public UserDetailsService onboardingApiAuthService() {
return Mockito.mock(UserDetailsService.class);
}
#Bean
public JwtService jwtService() {
return Mockito.mock(JwtService.class);
}
}
I have no idea why it happens. It looks really strange. But my guess is it has something to do with this #WithMockUser annotation. Maybe when I added #EnableTransactionManagement on the main class it reconfigured something in a wrong way that a dummy user is not found any more that's why I get 404 for all the tests in the test class.
Any ideas or hints?
While debugging I found that the controller method is not ever run.

Spring context doesn't load my class in unittest

I can't get my unit test to load my configuration class.
My test class is annotated:
#RunWith(SpringJUnit4ClassRunner.class)
#ActiveProfiles(profiles = "test")
#ContextConfiguration (classes = ClientConfiguration.class)
public class ClientConfigurationTest { ...
ClientConfiguration.class
#ConditionalOnProperty(value = "dirt.security.oauth2client", matchIfMissing = false)
#Configuration
#PropertySource(value = "classpath:oauth2client-${spring.profiles.active:local}.properties", ignoreResourceNotFound=true)
public class ClientConfiguration {
static {
SecurityContextHolder.setStrategyName(SecurityContextHolder.MODE_INHERITABLETHREADLOCAL);
}
#Bean
#ConfigurationProperties(prefix = "dirt.security.oauth2client.client")
ClientCredentialsResourceDetails clientConfig() {
return new ClientCredentialsResourceDetails() {
#Override
public String toString() {
return ReflectionToStringBuilder.toString(this);
}
};
}
#Bean
#ConfigurationProperties(prefix = "dirt")
protected DirtClientConfig dirtClientConfig() {
return new DirtClientConfig();
}
#Bean
DirtRestTemplate restTemplate() {
DirtRestTemplate dirtRestTemplate =
new DirtRestTemplate(clientConfig(),
new DefaultOAuth2ClientContext(), dirtClientConfig());
dirtRestTemplate.setErrorHandler(new RestTemplateResponseErrorHandler());
return dirtRestTemplate;
}
}
None of the 3 beans get instantiated, and when I call this, it gets a dependecy error on one of the other beans
#Test
public void clientConfig() {
DirtRestTemplate results =
(DirtRestTemplate)context
.getAutowireCapableBeanFactory()
.createBean(DirtRestTemplate.class,
AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE,
true);
assertNotNull(results);
}

test suite inside spring context

Is it possible to run test suite with loaded spring context, something like this
#RunWith(Suite.class)
#SuiteClasses({ Test1.class, Test2.class })
#ContextConfiguration(locations = { "classpath:context.xml" }) <------
public class SuiteTest {
}
The code above obviously wont work, but is there any way to accomplish such behavior?
This is currently how spring context is used in my test suite:
#BeforeClass
public static void setUp() {
final ConfigurableApplicationContext context =
loadContext(new String[] { "context.xml" });
jdbcTemplate = (JdbcTemplate) context.getBean("jdbcTemplate");
// initialization of other beans...
}
I have tried you code, the test suite are running with spring context loaded. Can you explain in more detail what the problem is?
here is the code:
#RunWith(Suite.class)
#SuiteClasses({ Test1.class, Test2.class })
public class SuiteTest {
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:context.xml" })
#Transactional
public class Test1 {}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:context.xml" })
#Transactional
public class Test2 {}
If you want Suite class to have its own application context, try this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:context.xml" })
#Transactional
public class SuiteTest {
#Test public void run() {
JUnitCore.runClasses(Test1.class, Test2.class);
}
}

Resources