Exclude elasticsearchTemplate from Spring-Boot Test - spring-boot

I have an application that use Elasticsearch and I'd like to disable this integration when I'm testing some controllers. How can I disable elasticsearchTemplate on Spring-Boot test?
Application.class:
#SpringBootApplication
#EnableElasticsearchRepositories(basePackages = "com.closeupinternational.comclosure.elasticsearch")
public class Application {
...
Repository.class:
#Repository
public interface PipelineRepository extends ElasticsearchRepository<Pipeline, String> {
...
Test Controller.class:
#ExtendWith(SpringExtension.class)
#EnableAutoConfiguration(exclude = {ElasticsearchDataAutoConfiguration.class,
ElasticsearchRepositoriesAutoConfiguration.class})
#WebMvcTest(ProductionCycleExecutionController.class)
#Slf4j
public class ProductionCycleExecutionControllerTest {
#Autowired
private MockMvc mvc;
#MockBean
private ProductionCycleExecutionService prodCycleExecService;
...
I'm not using inside ProductionCycleExecutionService and I don't wanna try to test elasticsearch repository PipelineRepository at this moment.
Error:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'pipelineRepository' defined in
com.closeupinternational.comclosure.elasticsearch.PipelineRepository defined in
#EnableElasticsearchRepositories declared on Application: Cannot resolve reference to bean
'elasticsearchTemplate' while setting bean property 'elasticsearchOperations'; nested exception is org.springframework.beans.factory

Just remove #ExtendWith(SpringExtension.class) and #EnableAutoConfiguration(exclude = {ElasticsearchDataAutoConfiguration.class, ElasticsearchRepositoriesAutoConfiguration.class})
These annotations aim to bootstrap the whole Spring context and configure it
#WebMvcTest should be enough in your case as it bootstraps web-related context only
Upd
If you have any dependencies in your ProductionCycleExecutionController (like elasticsearchTemplate you mentioned) then mock them like this if you don't need to define their behavior, as follows:
#MockBeans(value = {#MockBean(YourBean1.class), #MockBean(YourBean2.class), #MockBean(YourBean3.class)})
If you do need to define mocking behavior then mock as property in class:
#MockBean
private YourBean yourBean;

Related

#webMvcTest is not excluding and loading beans marked as #Repository

I've a #RestController which has only one dependency in field #Autowire
that dependency is #component, that component Class definition has some autowired fields which are #service and those services have some #repositories.
In this whole flow I've kafka, Quartz, Cassandra and DB2
So when I was creating a Unit test case for my controller, I dont want to setup whole application. so I decided to use #webMvcTest and used #MockBean on my only one dependency of controller class.
But my Test is throwing and exception because its trying to create a Dao bean, which is marked as #repository.
#ActiveProfiles("test")
#WebMvcTest(controllers = MyControllerTest .class)
class MyControllerTest {
#MockBean
MyControllerDependency dependency;
#Autowired
MockMvc mockMvc;
#Test
void test_something() throws Exception {
assert(true);
}
}
Here is oversimplified version of code
#Component
class MyControllerDependency {
#AutoiWired
MyCustomService service;
}
#Service
class MyCustomService{
#Autowired
MyCustomDao dao;
}
#Repository
class MyCustomDao{
#Autowired
private JdbcTemplate template;
}
I'm getting following exception in test.
Exception
***************************
APPLICATION FAILED TO START
***************************
Description:
Field template in com.....MyCustomDao` required a bean of type 'org.springframework.jdbc.core.JdbcTemplate' that could not be found.
Question is, When I'm using #WebMvcTest slice and already mocking the only required dependency MyControllerDependency then why spring test context is trying to load MyCustomDao which is annotated as #Repository.
I can do integration testing with SpringbootTest & AutoconfigureMockMVC, but for writing Junit test just for controller, I need to use WebMvcTest slice. which is creating a problem.
I ran into a similar problem where I want to test only my controller using #WebMvcTest, but the spring context was trying to create nondependent spring beans and was failing as below.
Failed to load ApplicationContext
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'TestController' defined in file ...
Solution: load only the controller your testing for an example like #ContextConfiguration(classes = DemoController.class).
Also, find below a complete sample
#WebMvcTest
#ContextConfiguration(classes = DemoController.class)
public class DemoControllerTest {
#Autowired
MockMvc mockMvc;
#MockBean
DemoService demoService;
#Test
public void testGetAllProductCodes_withOutData() throws Exception {
when(productCodeService.getAllProductCodes()).thenReturn(new ArrayList<ProductCodes>());
mockMvc.perform(MockMvcRequestBuilders.get("/services/productCodes")).andExpect(MockMvcResultMatchers.status().isNoContent());
}
}
}
Do you have any #ComponentScan("...") annotation active on your #SpringBootApplication?
As described in Spring Boot Reference Documentation:
Another source of confusion is classpath scanning. Assume that, while you structured your code in a sensible way, you need to scan an additional package. Your application may resemble the following code:
#SpringBootApplication
#ComponentScan({ "com.example.app", "com.example.another" })
public class MyApplication {
// ...
}
Doing so effectively overrides the default component scan directive with the side effect of scanning those two packages regardless of the slice that you chose. For instance, a #DataJpaTest seems to suddenly scan components and user configurations of your application. Again, moving the custom directive to a separate class is a good way to fix this issue.
One solution is to create a seperate #Configuration that is annotated with #ComponentScan. When creating a #WebMvcTest the configuration (and its component scan is ignored).
#Configuration
#ComponentScan("com.example.another")
public class DbConfig {
}
This usually happens when you have explicit #ComponentScan annotation on the spring boot main application class.
The #ComponentScan annotation suppresses the default component scanning mechanism that happens with #Webmvctest where it scans up the package hierarchy and apply excludeFilters to find only controller and its related classes.
When you mock your bean using #MockBean annotation you should define what that mocked bean should do when you call it's method, you usually do this using when in Mockito. In your case it can be done this way:
#ActiveProfiles("test")
#WebMvcTest
class MyControllerTest {
#MockBean
MyControllerDependency dependency;
#Autowired
MockMvc mockMvc;
#Test
void test_something() throws Exception {
when(dependency.sample()).thenReturn("Hello, Mock");
mockMvc.perform(get("/api/test/restpoint")
.accept(MediaType.APPLICATION_JSON))
.andDo(print())
.andExpect(status().isOk());
}
}
At this line:
when(dependency.sample()).thenReturn("Hello, Mock");
Instead of dependency.sample() you should put whatever method of MyControllerDependency class your controller call when you send a GET request to /api/test/restpoint path and with thenReturn("Hello, Mock") you define what is the mocked output of that method (when it gets called by your controller in your Unit test).

NUnit 5 Spring MVC test NoSuchBeanDefinitionException for Autowired dependency in submodule

I have a project with two submodules; one is the data access layer the other is the API service.
The data access module uses JOOQ and an autowired DSLContext in a service class. Also, I'm using JUnit 5, and Spring Boot 2.2.4.
The QueryService class in the data access module has a member like #Autowired private DSLContext dsl;
The test class is set up like this:
#SpringBootTest
public class MyServiceTests {
#Autowired
QueryService service;
#Autowired
private DSLContext dsl;
#Test
public void TestDoSomething() throws Exception {
service.selectBusinessEntityRelatedByBusinessEntity("C00001234", mockAuth);
}
}
The tests in this module run correctly. Configuration is read from the application.yaml, and autowire injects either real services or a mock into both my QueryService and the local dsl.
The API service is a different story. If I use the #SpringBootTest annotation with no MVC I can successfully get the tests to inject a local DSLContext with configuration from the application.yaml. Test set up similar to this:
#SpringBootTest
public class CustomersControllerTests {
#Autowired
private Gson gson;
#Autowired
DSLContext dsl;
#Test
public void addCustomerTest() {
}
What I need though is to use #WebMvcTest so that MockMvc is initialized but switching to #WebMvcTest causes injection to fail in the service class implemented in the data access module. The injection fails to find the DSLContext bean within the query service class. I set up the test like this:
#WebMvcTest
public class CustomersControllerTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private Gson gson;
private static final String testSub = "329e6764-3809-4e47-ac48-a52881045787";
#Test
public void addCustomerTest() {
var newCustomer = new Customer().firstName("John").lastName("Doe");
mockMvc.perform(post("/customers").content(gson.toJson(newCustomer)).contentType(MediaType.APPLICATION_JSON)
.with(jwt().jwt(jwt -> jwt.claim("sub", testSub)))).andExpect(status().isNotImplemented());
}
This is the actual error:
2020-02-25 18:14:33.655 WARN 10776 --- [ main] o.s.w.c.s.GenericWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customersController': Unsatisfied dependency expressed through field '_customersService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'customersService': Unsatisfied dependency expressed through field '_queryService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'queryService': Unsatisfied dependency expressed through field '_dsl'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.jooq.DSLContext' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
So, I know the test application configuration is correct because it works when not using MVC annotation. Also, I can create a DSLContext in the API project tests and I can actually run the API service outside the test.
So, why cant the DSLContext be found when using the MVC test setup?
This might be because #WebMvcTest fully disables Spring Boot's Autoconfiguration and only scans in #Controllers and a few other select classes, that you need for your ..well...MVC tests..
The Spring documentation recommends doing this in your case:
If you are looking to load your full application configuration and use MockMVC, you should consider #SpringBootTest combined with #AutoConfigureMockMvc rather than this annotation.

How to have bean of repository interface of spring data jpa during run of test cases

We have written below 2 classes
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestConfig.class})
public class AbcFilterTest {
#Autowired
private AbcUtils abcUtils;
#Autowired
private AbcRepository abcRepository;
#Test
public void testFilter() {
MockHttpServletRequest httpServletRequest = new MockHttpServletRequest();
MockHttpServletResponse httpServletResponse = new MockHttpServletResponse();
MockFilterChain mockChain = new MockFilterChain();
}
}
#Configuration
#ComponentScan(basePackageClasses = {AbcUtils.class, AbcRepository.class,})
public class TestConfig {
}
For running a test class, i need instance of both AbcUtilsclass and AbcRepository interface(extending CurdRepository) which are autowired in the class i am testing.1st one has #Component while 2nd has #Repositry on top of it.As you can see,in my test class I have autowired both util and repository class with component scan as above code.In real time spring framework creates implementaion class for the repository interface but On running test case,I am getting below error
Caused by: org.springframework.beans.BeanInstantiationException:
Failed to instantiate [com.a.b.c.persistence.AbcRepository]: Specified
class is an interface.
Kindly suggest how to make this test class work. Please note that we are not using Spring Boot and Mockito.We are doing integration testing.
We are using Spring rest and Spring Data JPA with hibernate.
Scanning AbcUtils.class and AbcRepository.class is not enough if you want to write something in the database. You need to scan the configuration that contains your DataSource definition and related config. Since the tests will be instantiated like a Spring bean too, the test class need to implement the ApplicationContextAware interface:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/config.xml"})
public class TestClass implements ApplicationContextAware{
#Autowired
Repository repository;
//....
}

Spring Boot Controller Test: mocking service requiring downstream objects causing ApplicationContext to not load

I am attempting to run a controller-level Spring Boot unit test with a Mock for my service-layer dependency. However, this Mock is requiring a downstream repository dependency which uses an EntityManager object, which is causing my test to fail on loading the ApplicationContext.
My test does not involve the repository dependency or EntityManager, it is using the Mocked service object to return a canned response. Why is Spring complaining about the repo/EntityManager if I only want to mock the service-layer object?
Controller unit test code:
#RunWith(SpringRunner.class)
#WebMvcTest
#AutoConfigureWebClient
public class MobileWearControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
UserDeviceService userDeviceService;
//.....
}
UserDeviceService code:
#Service
public class UserDeviceService {
private UserDeviceRepository userDeviceRepository;
public UserDeviceService(UserDeviceRepository userDeviceRepository) {
this.userDeviceRepository = userDeviceRepository;
}
//....
}
UserDeviceRepository code:
#Repository
public class UserDeviceRepositoryImpl implements UserDeviceRepositoryCustom {
#PersistenceContext
private EntityManager em;
//....
}
Expecting the test to run.
Actual result is getting the following stack trace:
java.lang.IllegalStateException: Failed to load ApplicationContext
...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userDeviceRepositoryImpl': Injection of persistence dependencies failed; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManagerFactory' available
...
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'javax.persistence.EntityManagerFactory' available
...
My issue was the annotations I was using for my test.
Using #AutoConfigureWebClient tries to standup the entire Spring Context; since I am unit testing my controller I want to test only the web layer and mock the downstream dependencies (ie UserDeviceService). So, I should be using #SpringBootTest and #AutoConfigureMockMvc instead, which will set up my Spring context only for the controller layer.
Using this approach I'm able to get UserDeviceService to mock successfully and thus allowing my test to compile and run:
#RunWith(SpringRunner.class)
#SpringBootTest
#AutoConfigureMockMvc
public class MobileWearControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
UserDeviceService userDeviceService;
//...
}
First, you need to specify what controllers you are going to test
#WebMvcTest(YourController.class)
Additionally, With JUnit5, you don't need to configure any extensions, as #WebMvcTest contains #ExtendWith(SpringExtension.class). You are apparently on JUnit4, but this shouldn't do any harm.
Check for example https://spring.io/guides/gs/testing-web/

Spring Data - MongoDB - JUnit test

I would have a question concerning Spring Data - MongoDB and JUnit test.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = { UserRepository.class, User.class })
public class MyJUnitTest {
The UserRepository looks like this:
#Repository
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(final String username);
}
I get the following Exception:
Failed to instantiate [... .repository.UserRepository]: Specified class is an interface
My question now would be, how to do it, that UserRepository is instantiate although there is no implementation class because Spring Data does the implementation by its own? If I do not mark USerRepository with #Repository than Spring does not create a bean object
[EDIT]
I have tried the example of the link you posted and it works fine if I run the application over the main- method.
Then I tried to implement a test class but in this case I get the same exception:
Error creating bean with name 'hello.test.TestClass': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private hello.CustomerRepository hello.test.TestClass.repository; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [hello.CustomerRepository] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
My test class looks like this in src/test/java/hello/test (hello.test is the package):
#ComponentScan("hello")
#EnableMongoRepositories(basePackages = "hello")
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = { CustomerRepository.class, Customer.class })
public class TestClass {
#Autowired
private CustomerRepository repository;
#Test
public void testMethod() {
System.out.println("repositoryd: " + repository);
}
}
and my CustomerRepository looks like this (with #Configuration annotation):
#Configuration
public interface CustomerRepository extends MongoRepository<Customer, String> {
public Customer findByFirstName(String firstName);
public List<Customer> findByLastName(String lastName);
}
Actually I don't know which annotations I need in order to get the test running - Maybe you would have another suggestion in order that I can solve this issue.
For Spring Boot 1.5.8.RELEASE
You can use #SpringBootTest to bootstrap all you spring configurations.
Your test will look like
#RunWith(SpringRunner.class)
#SpringBootTest
public class SomeRepositoryTests {
#Autowired
private SomeRepository someRepository;
#Test
public void someTest() {
someRepository.someMethod(...);
// assertions
}
}
Of course you want to use embedded mongodb for test so add
for Maven
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<scope>test</scope>
</dependency>
for Gradle
testCompile('de.flapdoodle.embed:de.flapdoodle.embed.mongo')
Your repo CustomerRepository doesn't require #Configuration or #Repository annotations. Spring will do it for you as you extends base Repository classes.
To setup Mongo Repositories you need to extend ApplicationContext with the following annotations.
#Configuration
#EnableAutoConfiguration // create MongoTemplate and MongoOperations
#EnableMongoRepositories(basePackages = "hello.test") // Create your repos
public class ApplicationConfig {
}
You also would like to use in-memory database for you unit/integration tests so them won't alter productions database.
To enable it just add the following dependency:
<dependency>
<groupId>de.flapdoodle.embed</groupId>
<artifactId>de.flapdoodle.embed.mongo</artifactId>
<version>1.50.2</version>
<scope>runtime</scope>
</dependency>
Finally, configure you test class with the ApplicationContext
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = ApplicationConfig.class)
public class MyJUnitTest {
// Test cases ...
}
Within #SpringApplicationConfiguration you need to point to a configuration class. Neither UserRepository nor User probably are one I assume.
To get started with both Spring and Spring Data MongoDB fundamentals, be sure to check out this getting started guide.
If using junit 5 and you want to start up spring in the most lightweight way possible you will need the following annotations on your IntegrationTest class
#DataMongoTest
#ExtendWith(SpringExtension.class)
#ContextConfiguration(classes = {MyMongoService.class})
#EnableMongoRepositories(basePackageClasses = MyRepository.class)
public class MyMongoIntegrationTest {
#Autowired
private MongoTemplate mongoTemplate;
#Autowired
private MyRepository myRepository;
#Autowired
private MyMongoService myMongoService; <-- Assuming this injects MyRepository
}

Resources