Spring boot how to exclude certain class from testing - spring

I have implemented own class for representing of database vector data based on UserType class.
My example:
class MyVectorType implements UserType {
#Override
public int[] sqlTypes() {
return new int[] { Types.ARRAY };
}
};
#Entity
#Table("MY_ENTITY")
public class MyEntity {
private MyVectorType myVectorType;
}
However this class cannot be used in testing with h2 dialect ie. in memory database. There is error: No Dialect mapping for JDBC type: 2003.
Therefore I would like to exclude this entity (inc. repository) from testing but this does not work:
#SpringBootApplication
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
MyEntity.class, MyEntityRepository.class})
})
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}
What is wrong or is there any best practice solving this problem?
EDIT 1: fixed examples - added correct entity and repository
SOLUTION 1:
I think that the only possible solution for this moment is move entity classes (which needs to be excluded) to other package. Then set #EntityScan to scan just non-excluded package. Exclude filters in ComponentScan seems to work only in case of #Component classes, not #Entity. However this is not absolutely best practice to solve this problem.

Just define it as a #MockBean so the real implementation of your repository will be replaced by a functionless mock in your tests:
#MockBean
private MyVectorRepositoryType vectorRepository;

I had a similar problem and I excluded the application configuration in my test configuration, as it seems the test config component scans the application config component scans the classes you want to exclude.

Related

In Spring Boot, can I disable a #RestController (that is #Import'ed) from a 3rd party?

The key is that the #RestController class is from a 3rd party package that I can't touch. I want all the other classes in that package but not a particular #RestController because I want to replace those endpoints with my own.
It also seems this class is #Import'ed by another class that's also in the 3rd party package that I don't control (see Edit3 below)
It's similar to this question/answer, however in that question, OP has access to the #RestController and can append a #ConditionalOnExpression, whereas I cannot.
Is there another way to disable a #RestController in spring?
The specific #RestController class I want to disable is GraphQLController.
Edit1
Here are some ways I tried to exclude it but fails
#ConfigurationPropertiesScan("com.mycomp.package")
#SpringBootApplication(exclude = {graphql.kickstart.spring.webflux.GraphQLController.class})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The above attempt throws a
java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes:
- graphql.kickstart.spring.webflux.GraphQLController
As suggested below, I also tried #ComponentScan.Filter of ASSIGNABLE_TYPE
#ConfigurationPropertiesScan("com.mycomp.package")
#ComponentScan(excludeFilters={
#ComponentScan.Filter(type= FilterType.ASSIGNABLE_TYPE, value=graphql.kickstart.spring.webflux.GraphQLController.class)})
#SpringBootApplication
public class Application {
....
}
No errors but it's still instantiating the bean.
Also tried #ComponentScan.Filter of REGEX
#ConfigurationPropertiesScan("com.mycomp.package")
#ComponentScan(excludeFilters={
#ComponentScan.Filter(type= FilterType.REGEX, pattern = "graphql.kickstart.spring.webflux.GraphQLController")})
#SpringBootApplication
public class Application {
....
}
That didn't seem to work either
Also tried #ComponentScan.Filter of CUSTOM
#ConfigurationPropertiesScan("com.mycomp.package")
#ComponentScan(excludeFilters = #ComponentScan.Filter(type = FilterType.CUSTOM,
classes = GraphQLControllerBeanFilter.class))
#SpringBootApplication
public class Application {
....
}
public class GraphQLControllerBeanFilter implements TypeFilter {
#Override
public boolean match(MetadataReader metadataReader,
MetadataReaderFactory metadataReaderFactory) {
ClassMetadata classMetadata = metadataReader.getClassMetadata();
String fullyQualifiedName = classMetadata.getClassName();
return fullyQualifiedName.equals("graphql.kickstart.spring.webflux.GraphQLController");
}
}
The custom filter doesn't seem to ever see the GraphQLController. In fact, I printed out all the fullyQualifiedName it scans and only the ones in my com.mycomp.package package show. Nothing from 3rd party libraries even though it's loading them.
I'm wondering if the #ComponentScan.Filter ever sees 3rd party packages or if maybe I have some other spring setting overriding it.
I also tried adding #Primary to my Controller
#Primary
#RestController
public class MyGraphQLController extends graphql.kickstart.spring.webflux.GraphQLController
But that results in this error
java.lang.IllegalStateException: Ambiguous mapping. Cannot map 'graphql.kickstart.spring.webflux.GraphQLController'
Edit2
This is the 3rd party library that contains the #RestController I'm trying to exclude
implementation "com.graphql-java-kickstart:graphql-kickstart-spring-boot-starter-webflux:11.0.0"
Edit3
It looks like graphql.kickstart.spring.webflux.boot.GraphQLSpringWebfluxAutoConfiguration is importing the GraphQLController in an #Import annotation.
Is there anyway to disable or replace it in this case?

Not able to prevent couchbase autoconfiguration during tests

I am trying to prevent the application from attempting to connect to the DB while running the Unit tests. Following is what I have done.
#SpringBootApplication(exclude = {
CouchbaseDataAutoConfiguration.class,
CouchbaseAutoConfiguration.class,
})
#ComponentScan(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
ApplicationStartup.class, MessageApplication.class }))
public class MessageApplicationTests {
public static void main(String[] args) {
SpringApplication.run(MessageApplicationTests.class, args);
}
}
#ActiveProfiles("test")
#SpringBootTest(classes = MessageApplicationTests.class)
class TestClass {
#Autowired
Serviceclass serviceclass;
#Test
void testMethod() {
}
}
Apart from this, I have added the following in application-test.yml
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration
- org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration
- org.springframework.cloud.aws.autoconfigure.messaging.MessagingAutoConfiguration
Both are not helping.
Can someone help me understand what is wrong here?
Also exclude your Config class (the one that extends AbstractCouchbaseConfig)
But if you have any references to repositories such as via Autowire or as args to #Service constructors, the application will fail to start. The exclude of auto configuration classes did not seem to matter when I tried it.
#ComponentScan(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
ApplicationStartup.class, MessageApplication.class, Config.class}))
Probably not related to your issue, but I found that with multiple #SpringBootApplication classes (you have MessageApplication and MessageApplicationTests, right?), that Spring goes through the auto-config classes for both of them, not just the one in
SpringApplication.run(MessageApplicationTests.class, args) ) So one would need #SpringBootApplication excludes on both classes to completely exclude them (although I found that excluding didn't change anything).
The spring-data-couchbase project tests provide a mock couchbase server (src/test/resources/integration.properties -> mocked) or can use a standalone couchbase server (unmanaged). That might be useful for your testing.
The above answer posted by Michael Reiche is correct. Adding few more points to address the concern raised by him.
We need to exclude the configuration class for Couchbase. But the autowired repository beans would create a problem then.
To resolve it we can mock the repository beans so that it doesn't try to create actual repository beans and load them to the context.
Not including the autoconfiguration classes in the exclusion list did matter for me, as it would try to configure the Couchbase since the dependency is there in the classpath
#SpringBootApplication(exclude = {
CouchbaseDataAutoConfiguration.class, CouchbaseAutoConfiguration.class,
CouchbaseRepositoriesAutoConfiguration.class, CouchbaseReactiveDataAutoConfiguration.class,
CouchbaseReactiveHealthContributorAutoConfiguration.class
})
#ComponentScan(excludeFilters = #ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = {
ApplicationStartup.class, MessageApplication.class , CouchBaseConfiguration.class }))
public class MessageApplicationTests {
#MockBean
Repositoryclass repoBean;

How to write the unit test case for the Classes which are annotated with #Configuration in Spring Boot application

I have a Configuration Class, which creates the bean for RedissonClient and also Creates the CacheManager. How to create the Unit Test case for this Configuration classes.
Can we write unit test case for #Configuration Class?
If we can, How we need to develop.
I prefer to write the test case in Spock Framework, with Groovy. If not, using Junit or Mockito Framework. How to write the unit test case for the Classes which are annotated with #Configuration in Spring Boot application
#Configuration
public class CacheConfiguration {
private static final String CONFIG= "Configuration";
#Value("${redis.server.url}")
private String redisUrl;
#Value("${redis.server.password}")
private String password;
#Bean
public RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress(redisUrl).setPassword(password);
RedissonClient client = Redisson.create(config);
return client;
}
#Bean
public CacheManager redissonCacheManager(RedissonClient redissonClient) {
Map<String, CacheConfig> config = new HashMap<String, CacheConfig>();
config.put(CONFIG, new CacheConfig(24*60*1000, 12*60*1000));
return new RedissonSpringCacheManager(redissonClient, config);
}
}
I think you should realize that classes annotated with #Configuration are not really java classes, or at least you should not treat them like this. I know it sound controversial, I'll explain...
So historically spring used XML configurations to declare beans.
In spring 2.5 I guess, they've introduced an annotation based method where you put annotations #Component/#Service on classes, put #Autowired wherever you want spring to inject dependencies, then spring starts, scans the classpath, detects the beans and starts the application context with these beans.
Then Spring 3.0 has introduced a Java Configuration way of doing spring related configurations:#Configuration / #Bean in a special class.
So you should view these configuration classes as a "substitution" to the methods I've described before
Now let me ask, do you think you should test and XML bean configuration on its own? Probably not...
Do you think you should test that class has annotation #Component on it and all the necessary dependencies are autowired (with reflection or whatever)? Probably not.
So why you want to test the Java Config classes?
Here is another argument that you might find interesting
I've said that these classes are solely for spring to resolve the beans and it internally runs them. But spring doesn't just "run" them - it creates run-time wrapper for them to overcome some technicalities. Here is an example of one such thing:
Lest assume we have three beans: A,B,C such as B and C depend on A.
class A {}
class B {
private A a;
public B(A a) {this.a = a;}
}
class C {
private A a;
public C(A a) {this.a = a;}
}
All beans are expected to be singletons so we define the configuration like this:
#Configuration
public class MyConfig {
#Bean
public A a() { return new A(); }
#Bean
public B b() { return new B(a()); }
public C c() {return new C(a()); }
}
Note that we call a() in definitions of B and C
Now, let me ask you a question: If its a "regular" java code how two different invocations of method a() (in B's and C's constructor) respectively are supposed to return the same instance of A?
So spring indeed uses a lot of sophisticated code and this is what actually runs in runtime, not your class as is, but the transformed version of it and you never know what are those transformations exactly since its and internal spring thing. But is so what is the point of testing it as it is?
I believe there are more arguments like this, but the point is clear - don't test the Configuration classes on their own.
Instead you can use an integration test that will run the spring container and load all the classes required in the configuration. However, in this case you'll probably want to mock some classes (by using #MockBean for example) so it won't be a 100% accurate test.
In terms of code coverage - IMO you should exclude these classes from coverage altogether
After some research and found that, we can run the embedded redis server and we can check whether we able to connect to redis server by spin up the application. I don't if this is correct or not. But by doing so, it really takes time to complete it, took around 20 seconds. Used following dependency to test this // https://mvnrepository.com/artifact/it.ozimov/embedded-redis
testCompile group: 'it.ozimov', name: 'embedded-redis', version: '0.7.2'
#SpringBootTest(classes = [TestApp])
class CacheConfigurationSpec extends Specification {
#Shared
RedisServer redisServer;
def setupSpec() {
redisServer = RedisServer.builder()
.port(6379)
.setting("bind 127.0.0.1")
.setting("maxmemory 128M")
.build()
redisServer.start()
}
def cleanupSpec() {
if (redisServer != null) {
redisServer.stop()
}
}
#Autowired
private RedissonClient redissonClient;
def "load all contexts"() {
}
}
#SpringBootApplication
class TestApp {
static void main(String[] args){
SpringApplication.run(TestApp.class, args)
}
}

how to conditionally not create beans in spring boot?

In my application, I have a component that reads data from other system when the application is started.
However, during testing, I don't want this component to be created
#Component
#Slf4j
public class DeviceStatisticsSyncHandler {
#EventListener
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
}
I can use condition to solve this, but other code readers need to understand, so I don't think this is a very good method:
#EventListener(condition = "#deviceStatisticsSyncHandler .isServiceEnabled()")
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
public boolean isServiceEnabled() {
return !serviceEnabled;
}
#Value("${test.mode:false}")
public boolean serviceEnabled;
My application doesn't use Profiles, is there any other method to solve this problem.
Spring Boot version:2.1.3
One possible option is not to load the DeviceStaticsticsSyncHandler at all if you're in a test mode.
The "test.mode" is not a good name here, because the production code contains something tightly bound to the tests.
How about the following approach:
#Component
#ConditionalOnProperty(name ="device.stats.handler.enabled", havingValue = "true", matchIfMissing=true)
public class DeviceStatisticsSyncHandler {
// do whatever you need here, but there is no need for "test.mode" enabled related code here
}
Now in Tests you can define a test property "device.stats.handler.enabled=false" on the test itself or even place that definition in src/test/reources/application.properties so it will be false for all tests in the module.
An obvious advantage is that this definition is pretty much self explanatory and can be easy understood by other project maintainers.
for me, it's not the case of the condition rather environment-related. I will solve this problem using spring profile.
Step 1: Create an Interface first
public interface DeviceStatisticsSyncHandler {
public void handle(ApplicationReadyEvent event);
}
Step 2: Create an Implementation for production
#Component
#Profile("!test")
public class DeviceStatisticsSyncHandlerImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
syncDeviceStatisticsDataSync();
}
}
step 3: create an implementation of test
#Component
#Profile("test")
public class DeviceStatisticsSyncHandlerTestImpl implements DeviceStatisticsSyncHandler {
#EventListener
#Override
public void handle(ApplicationReadyEvent event) {
//do Nothing
}
}
final step
All you need to do is set/toggle the property
-Dspring.profiles.active=test
or
-Dspring.profiles.active=prod
I found a way to achieve this without any further external configuration required.
The idea is to create a general configuration that applies to all integration tests and use #MockBean there to replace the real bean. So one should create a class like this under the test classpath (i.e. that is not scanned during normal application launch):
#Configuration
public class IntegrationTestConfiguration
{
#MockBean
public DeviceStatisticsSyncHandler deviceStatisticsSyncHandler;
}
I was actually surprised that #MockBean can be used here, but the Javadoc explicitly points that out: Can be used as a class level annotation or on fields in either #Configuration classes, or test classes that are #RunWith the SpringRunner..

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.

Resources