spring integration test fail to load context "Another resource already exists with name dataSource" - maven

I am using test annotation introduced in spring-boot 1.4.3 for my integration tests
#RunWith(SpringRunner.class)
#SpringBootTest
public class MyServiceIT { }
According to documentation, test context is cached and reused to speed up integration tests. This behavior is what I want since it takes significant amount of time to initialize application context. My failsafe plugin is configured with
<forkCount>1</forkCount>
<reuseForks>true</reuseForks>
to allow integration tests to run in the same process to take advantage of application context caching.
Recently, I wrote a integration test used #MockBean annotation to mock behavior for some beans.
#RunWith(SpringRunner.class)
#SpringBootTest
public class AnotherServiceIT {
#MockBean
SomeService service1
}
While the test runs fine on it's own, when running through maven verify, multiple integration tests fails with the error message
javax.naming.NamingException: Another resource already exists with
name dataSource - pick a different name
If I skip this particular test with JUnit #Ignore annotation, everything goes back to normal.
This behavior seems to indicate that using #MockBean changes the caching behavior, and each test attempts to create its own datasource. I should also mention that I am using an AtomikosDataSourceBean created through XADataSourceAutoConfiguration.
How can I overcome this issue so my integration test can still use cached context and use #MockBean at the same time?

Hmm, does SomeService relate to your Datasource in any way?
Because your context is cached and #MockBean does the following:
used to add mocks to a Spring ApplicationContext ... Any existing single bean of the same type defined in the context will be replaced by the mock,
and
If there is more than one bean of the requested type, qualifier metadata must be specified at field level:
#RunWith(SpringRunner.class)
public class ExampleTests {
#MockBean
#Qualifier("example")
private ExampleService service;
Edit:
So if your SomeService is an implementation of a DataSource try adding a Qualifier. If SomeService has a DataSource in it, and you need to access some methods in it, you could try to use #Mock and specify the any objects that need to be returned either through their own mock or autowire.
#Mock
SomeService someService;
#Mock
SomeDependency mockDependency;
#Autowired
OtherDependency realDependency;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
doReturn(mockDependency).when(someService).getSomeDependency();
doReturn(realDependency).when(someService).getOtherDependency();
}

Related

Unit testing Spring Boot application Service layer

I'm a bit confused about the test capabilities and utilities provided by Spring Boot.
I'm using spring-boot-starter-test in my project and I'd like to unit test my services without the database connection
At the moment I'm using #WebMvcTest for contoller test suites and #SpringBootTest for all the other test classes.
But I read somewhere that #SpringBootTest is meant to be used only in integration tests...
Reading documentation I didn't understood what's the suggested approach for services. Should I only test them in integration with repos?
UPDATE
That's an excerpt of a test class for one of my services:
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
#SpringBootTest
internal class SignupServiceTest(
#Autowired val signupService: SignupService
) {
#MockkBean
lateinit var userRepository: UserRepository
#Test
fun `should return exception if username already used`() {
every { userRepository.findByUsername("registered-user") } returns fakeUser(username = "registered-user")
assertThatThrownBy {
signupService.createNewAccount(fakeSignupForm(username = "registered-user"))
}.isExactlyInstanceOf(UsernameNotAvailableException::class.java)
}
// ... other tests
}
Using #SpringBootTest for unit tests is a bit of a overkill. Because this would boot up the whole application context.
To test individual (service) classes I would go with #RunWith(MockitoJUnitRunner.class) and instead of #Autowired and #MockBean use #Mock and #InjectMocks(If you use constructor injection, you wouldn't have to use this. which would be the better option)
You could still use #Autowired with #ContextConfiguration and load specific classes(if there are not too many transitive dependencies)
If you do not want to use mocks, then you can use embedded databases and use #DataMongoTest or #DataJpaTest and use Springboot testing capabilities.
Keep it simple....
You should favor implementing unit tests to test your business logic (tests running without Spring, plain JUnit tests) over integration tests (tests starting a Spring container, #SpringBootTest) as they are more lightweight and give you feedback a lot faster.
Quote from Spring Boot doc
One of the major advantages of dependency injection is that it should make your code easier to unit test. You can instantiate objects by using the new operator without even involving Spring. You can also use mock objects instead of real dependencies.
Often, you need to move beyond unit testing and start integration testing (with a Spring ApplicationContext). It is useful to be able to perform integration testing without requiring deployment of your application or needing to connect to other infrastructure.
try to mock the calls to database using mockito, or use h2 database for the tests
I was previously using #SpringBootTest as well then realized it was also trying to connect to my database, which should not be needed since it's a simple service test and all dependencies are mocked. After a bit of research I found this solution works quite well. Also note now my spring properties were not being injected so I used #Spy to create a pojo property object with some test values.
#ExtendWith(MockitoExtension.class)
public class MyServiceTest {
/**
* This is the bean under test as it is not a mock
*/
#InjectMocks
private MyService myService;
#Mock
private OtherService otherService;
#Spy
private MyServiceProperties properties = mockProperties();
#BeforeEach
public void configureMocks() {
given(this.otherService.getData(any())).willReturn(mockDataResponse());
}
#Test
void testMyServiceReport_Success() {
myService.runReport();
//assert response as needed
verify(otherService, times(1)).getData(any());
}
Also here is some help with the imports which can sometimes be confusing:
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.mockito.BDDMockito.any;
import static org.mockito.BDDMockito.given;
import static org.mockito.BDDMockito.times;
import static org.mockito.BDDMockito.verify;

Integration testing with spring declarative caching

I'm trying to write integration tests for a Spring Boot 2 Application.
One test should test updating a value via REST.
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureTestEntityManager
#Transactional
public class TenantEndpointIT {
#Autowired
private TestRestTemplate template;
#Autowired
private TestEntityManager entityManager;
#Test
public void nok_updateValueForbidden() {
}
}
Now, I thought the cleanest way was to create the value with the TestEntityManager in the #Before method, then test the REST endpoint in the actual test.
But the service called by the REST Endpoint is annotated with Spring Caching annotations. So the test fails if I do that. I could use the service directly or make a second REST call. That creates problems with other tests using the same Value, because even if the DB is rolled-back, the cache seems to contain the value. (Now I'm using #DirtiesContext).
My question is, how do you correctly integration test services with #Cachable?
Is there a way to get the Cache and explicitly put/remove?
I tried autowiring a CacheManager, but it won't find one and fails.
If you add #AutoConfigureCache on your test, it will override whatever cache strategies you've defined in your app by a CacheManager that noops. That's pretty useful if you want to make sure that cache doesn't interfere with your tests.

Spring Annotation #WebMvcTest does not work in an app that has Jpa repositories

I have a Spring App that uses JPA repositories (CrudRepository interfaces). When I try to test my controller using the new Spring test syntax #WebMvcTest(MyController.class), it fails coz it tries to instantiate one of my service class that uses JPA Repository, does anyone has any clues on how to fix that? The app works when I run it.
Here is the error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.myapp.service.UserServiceImpl required a bean of type 'com.myapp.repository.UserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.myapp.repository.UserRepository' in your configuration.
According to the doc
Using this annotation will disable full auto-configuration and instead apply only configuration relevant to MVC tests (i.e. #Controller, #ControllerAdvice, #JsonComponent Filter, WebMvcConfigurer and HandlerMethodArgumentResolver beans but not #Component, #Service or #Repository beans).
This annotion only apply on the Spring MVC components.
If you are looking to load your full application configuration and use MockMVC, you should consider #SpringBootTest combined with #AutoConfigureMockMvc rather than this annotation.
I was able to unit test a Rest Controller by implementing junit 5 and using #SpringJUnitConfig along with #WebMvcTest. I am using Spring Boot 2.4.5 and this is my example:
#SpringJUnitConfig
#WebMvcTest(controllers = OrderController.class)
class OrderControllerTest {
#Autowired
private MockMvc mockMvc;
// This is a Mock bean of a Spring Feign client that calls an external Rest Api
#MockBean
private LoginServiceClient loginServiceClient;
// This is a Mock for a class which has several Spring Jpa repositories classes as dependencies
#MockBean
private OrderService orderService;
#DisplayName("should create an order")
#Test
void createOrder() throws Exception {
OrderEntity createdOrder = new OrderEntity("123")
when(orderService.createOrder(any(Order.class))).thenReturn(createdOrder);
mockMvc.perform(post("/api/v1/orders").contentType(MediaType.APPLICATION_JSON).content("{orderId:123}"))
.andExpect(status().isCreated())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8))TODO: here it will go the correlationId
.andExpect(jsonPath("$.orderId").value("123"));
}
}
Please only use #SpringBootTest when you are implementing integration tests.
I faced this same problem. Using #SpringBootTest and #AutoConfigureMockMvc worked perfectly for me.

How to ensure load time weaving takes place for Eclipselink when using SpringBootTest with other tests running beforethe Spring one

I'm using Spring Rest Docs to generate documentation for my REST services. This involves running unit(strictly integration) tests that run against a live Spring Boot Container that is kicked off by the test. The test class looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MySpringConfiguration.class)
#WebAppConfiguration
public class ApiDocumentation {
private MockMvc mockMvc;
#Rule
public final JUnitRestDocumentation restDocumentation = new JUnitRestDocumentation("target/generated-snippets");
#Autowired
private WebApplicationContext context;
#Autowired
private ObjectMapper objectMapper;
#Before
public void setUp() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(this.context)
.apply(documentationConfiguration(this.restDocumentation))
.build();
}
#Test
public void testSomething() throws Exception {
}
}
The application uses JPA with EclipseLink for the EntityManager implementation.
When I run the test standalone in my IDE or as the only test present when I run a Maven build using the maven-surefire-plugin everything works fine.
However it's not the only test I want to run in the suite. As soon as I run other tests in the suite I come across the issue mentioned here, namely
"Spring's agent does not initialize the persistence context until the application accesses the Spring context. If the application has already triggered the loading of the persistent class before accessing the Spring context, weaving will not occur."
and get errors like this:
Exception Description: The method [_persistence_set_someField_vh] or [_persistence_get_someField_vh] is not defined in the object [mypackage.MyEntity].
So what do people normally do to get around this ? Run SpringBootTest classes in a different module to unit tests that access entities ?
As far as I concerned problem caused by dynamic weaving, if you make it static it should work proper. Possibly it could help you
Another solution could be to disable dynamic weaving in that particular test using eclipselink.weaving JPA property.
See this question and its answers: #SpringBootTest interferes with EclipseLink dynamic weaving

Spring tries to autowire Mockito mocks

I have a spring managed application in which I like for my service layer to be mocked. So I created a Spring Application Java Config and returned a mock of the actual service.
For e.g,
#Bean
#Profile("resource")
public MyService mockService() {
return mock(MyService.class)
}
And then the MyService goes as
class MyService {
#Autowired
private MyDao dao;
}
When Spring creates bean of name "mockService", it also tries to Autowire MyDao on the mock ? This in my opinion defeats the purpose of mocking. Is this the expected behaviour, whats the workaround ?
So bottomline, its best practice to code to interfaces rather than concrete classes particularly if you are wanting to write focused tests on specific layers.
Well, I suppose you're trying to execute an integration test, but using some mocks, doing something like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {Application.class, YourMockConfigurationClass.class})
#WebIntegrationTest
public class MyIntegrationTest {
...
}
In this case, you can create a mock of your service, and tell spring that this service instance has priority:
#Bean
#Primary
public MyService mockService() {
return mock(MyService.class)
}
Doing this, whenever Spring has to inject an unqualified MyService instance, it always chooses your mock. But it doesn't prevent Spring to create the original service.
So inside Spring context there will be two instances of MyService, the mock instance and the original implementation. But using #Primary you're telling Spring that your mock has priority.
On the other hand, in order to prevent Spring to load any mock in your environments (dev, test, etc...), you should annotate your mock configuration class either with a testing #Profile or a custom annotation (like #MockBean) and then configure your component scan strategy to not load this class (#ComponentScan(excludeFilters=#Filter(MockBean.class)))

Resources