Spring Boot Test with Cucumber and Mockito - spring-boot

I recently started to use Cucumber to implement a set of behavior driven tests for a Java project based on Spring Boot. I would like to call REST endpoints of this application using something like REST Assured or a custom REST client and for external systems and database I would like to setup some mock with Mockito, as I have already done with unit tests.
But I didn't find a complete working solution that I can apply to use Mockito beans in my Cucumber steps, for example to simulate a possible response from database queries.
I found a lot of posts of people over the years that had similar problems with different versions of Cucumber/Junit/Spring but I don't understand if it exists a right way to make these tools working together because I didn't a single complete example related to these tools together. Can anyone share experiences (versions/examples) in real world projects using Spring Boot Test, Cucumber and Mockito?

I finally found the solution. You can start the Cucumber test suite with a class like this
import static io.cucumber.junit.platform.engine.Constants.PLUGIN_PROPERTY_NAME;
import static io.cucumber.junit.platform.engine.Constants.GLUE_PROPERTY_NAME;
import org.junit.platform.suite.api.ConfigurationParameter;
import org.junit.platform.suite.api.IncludeEngines;
import org.junit.platform.suite.api.SelectClasspathResource;
import org.junit.platform.suite.api.Suite;
#Suite
#IncludeEngines("cucumber")
#SelectClasspathResource("bdd")
#ConfigurationParameter(key = PLUGIN_PROPERTY_NAME,
value = "pretty")
#ConfigurationParameter(key = PLUGIN_PROPERTY_NAME,
value = "usage")
#ConfigurationParameter(key = PLUGIN_PROPERTY_NAME,
value = "html:target/cucumber-reports")
#ConfigurationParameter(key = GLUE_PROPERTY_NAME,
value = "myproject.cucumber.glue")
public class RunCucumberTest {
}
In the glue folder you can create a Spring Boot Test that will create the Spring Context and create the bridge between Spring Boot and Cucumber worlds thanks to CucumberContextConfiguration
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.TestPropertySource;
import io.cucumber.spring.CucumberContextConfiguration;
import myproject.repository.MyEntityRepository;
#CucumberContextConfiguration
#SpringBootTest
#ActiveProfiles("test")
public class SpringBootTestStarter {
#MockBean
private MyEntityRepository myEntityRepository;
}
Here you can use Mockito MockBean annotation and in the steps related to Cucumber scenarios you can now use this bean as a mock

Related

Spring Starter Test - JUnit SpringBoot

Hello im new in testing and I first time trying to test a JpaRepository which interact with postgres in reality but in test it interacte with H2 Db i ve installed H2 dependencie and the software aswell but i still have a problem when using the repository on my test btw the dependencie is mention in scope test just to clarify more, any whenever i run this test below i got this error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field initService in com.project.socializer.SocializerApplication required a bean of type 'com.project.socializer.runner.InitService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.project.socializer.runner.InitService' in your configuration.
`***************************
APPLICATION FAILED TO START
***************************
Description:
Field initService in com.project.socializer.SocializerApplication required a bean of type 'com.project.socializer.runner.InitService' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.project.socializer.runner.InitService' in your configuration.`
here is my code :
package com.project.socializer.user.repository;
import com.project.socializer.SocializerApplication;
import com.project.socializer.user.entity.Roles;
import com.project.socializer.user.entity.UserEntity;
import org.junit.jupiter.api.*;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.*;
#DataJpaTest
class UserRepositoryTest {
#Autowired
UserRepository userRepository;
#BeforeEach
void setUp() {
userRepository.save(new UserEntity("Jhon","Doe","jhon.doe#gmail.com","test1234","1999-12-19",new Roles("USER")));
}
#AfterEach
void afterEach() {
userRepository.deleteAll();
}
#Test
void findByEmail() {
Optional<UserEntity> userTest = userRepository.findByEmail("jhon.doe#gmail.com");
assertTrue(userTest.isPresent(),"User Exist");
}
}
i tried many thing but sems nothing to work i dont know whats the problem and i hope for a fix from you.
I just had to set this up myself and I can relate to the frustration.
If you are using an in-memory database to run an integration test you have to remember that there is a lot more going on behind the scenes to load your updated PersistenceUnit (where your database details are defined) than meets the eye. What that error message is telling you is that somewhere in the background, in order for one of your test dependencies to be initialized, it has a transitive dependency on this InitService class. So it is starting up the Spring container, discovers it needs one of these InitService instances, and then finds that it doesn't exist (since we never initialized it). To correct this, we'll need to load your original production SpringBoot configuration, with all of its Beans and settings. (Incidentally, this is extremely expensive processing-wise which is why a lot of developers recommend you just mock your DAO classes rather than hit a real database for the tests).
Now assuming you still want to test your DAO layer without mocking it (I know I did), to initialize a test run using your production starting point, you'll want to use the #SpringBootTest annotation within your test class. What this does is tell your project that you want to initialize using your #SpringBootApplication configuration along with your test.
This can be confirmed as expected usage per this line in the #DataJPATest Javadoc:
If you are looking to load your full application configuration, but
use an embedded database, you should consider #SpringBootTest combined
with #AutoConfigureTestDatabase rather than this annotation.
Don't quote me on this, but I believe that if you use a copy of applications.properties within your src/test/resources folder to define your test PersistentUnit, you do not need to use this #AutoConfigureTestDatabase annotation. I didn't require it but then again, I stuck to the JPA interfaces rather than implement the Spring Data so your mileage may vary.
As a final mention, since this is going to reload your entire application in order to run just one test, processing can bubble out of control quickly. As such, I would recommend using profiles (#Profile and #ActiveProfiles) and configuration classes (#Configuration and #TestConfiguration) to partition your #Bean definitions so that you can better control what gets loaded and when. Partitioning your configurations also has the added benefit of letting you call different component and entity scan commands depending on the profile, which can help better organize your project.
Hope that helps!

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;

SpringBoot #IntegrationTest annotation for testing

I am very new to SpringBoot. I need to understand how to write an integration test using SpringBoot. I have seen some examples on the Internet which use the #IntegrationTest annotation whereas some other examples which use the #SpringBootTest annotation.
I am just wondering what is the difference between the two?
Which is the best way of writing an integration test in Spring boot?
The IntegrationTest was deprecated sine spring boot 1.4, so the suggestion is using SpringBootTest after 1.4
Deprecated as of 1.4 in favor of org.springframework.boot.test.context.SpringBootTest with webEnvironment=RANDOM_PORT or webEnvironment=DEFINED_PORT.
Basic template for writing the integration tests in springboot.
the sql group is additional annotation. When ever you want to execute the particular sql queries before and after method run so u can use #SqlGroup annotation.
#RunWith(SpringJUnit4ClassRunner.class)
#SpringBootTest(classes = MainApplication.class,
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#SqlGroup({
#Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD,
scripts = "classpath:beforeTestRun.sql"),
#Sql(executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD,
scripts = "classpath:afterTestRun.sql")})
public class CustomerControllerIntTest {}

Spring Boot MongoRepository unit testing using pre-installed MongoDB

I have a regular Spring Boot application (1.3.2) with MongoDB using MongoRepository.
I would like to write an integration test for one of my endpoints that gets the data from the MongoDB. As far as I see from the Spring Boot 1.3 Release Notes Spring has auto-configuration for Embedded MongoDB (de.flapdoodle.embed.mongo). However, I cannot figure out from Spring and flapdoodle documentation on how to write an integration test that would use already installed version of MongoDB on my filesystem.
So far my integration test looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(Application.class) // my application class
#WebAppConfiguration
public class IntegrationTest {
#Autowired
private MyRepository myRepository;
#Before
public void setup() {
myRepository.save(new MyEntity());
}
#Test
public void test() {
// here I will fire requests against the endpoint
}
}
I have added two dependencies with test scope: spring-boot-starter-test and de.flapdoodle.embed:de.flapdoodle.embed.mongo. So when I run the test, I can see that flapdoodle attempts to download version of MongoDB, but fails since I am behind the proxy. But I do not want to download any versions, I want it to use my locally installed MongoDB. Is it possible to do this?
If you want to use your locally installed MongoDB (not recommended, since then the tests depend on a particular DB that can get into a dirty state), then you shouldn't be pulling in the embedded MongoDB.
I believe this config will do what you're asking for, though (seems to work in my Spring Boot 1.3.5 test):
import java.net.UnknownHostException;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import com.mongodb.MongoClient;
#EnableAutoConfiguration(exclude = MongoAutoConfiguration.class)
#Configuration
public class TestConfig
{
#Primary
#Bean
MongoClient mongoClient()
{
try
{
return new MongoClient("localhost", 27017);
}
catch (UnknownHostException e)
{
throw new RuntimeException(e);
}
}
}
However, I suspect you would be better off properly configuring the proxy and using the embedded mongoDB in your tests. See this answer for hints on how to do that.

#Provider AND Registration in Application(ResourceConfig) requirement for custom MessageBodyWriter/Reader?

Is there a requirement that a custom MessageBodyWriter and MessageBodyReader must not only be annotated by #Provider annotation AND also be included in the Application configuration through Application or ResourceConfig. If yes, why?
My train of thought was that, if classes are annotated with #Provider then the JAX-RS runtime by default loads such classes as it's runtime component. Declaring the same class inside the Application makes is a redundant exercise. It appears my train of thought is wrong, but I am looking for some kind of explanation on how and why this has been designed this way(ie both annotation and Application configuration).
I can understand that some form of Application configuration would be required on the Jersey Client side, but am not very confident about that either.
For instance, the JavaMarshaller class below has been annotated with #Provider
import javax.ws.rs.ext.MessageBodyReader;
import javax.ws.rs.ext.MessageBodyWriter;
import javax.ws.rs.Consumes;
import javax.ws.rs.Produces;
import javax.ws.rs.ext.Provider;
#Provider
#Produces("application/example-java")
#Consumes("application/example-java")
public class JavaMarshaller implements MessageBodyReader, MessageBodyWriter {
.......
Now in the Application class is it required to do as below?
#ApplicationPath("services")
public class ShoppingApplication extends Application {
private Set<Object> singletons = new HashSet<Object>();
private Set<Class<?>> classes = new HashSet<Class<?>>();
public ShoppingApplication() {
classes.add(JavaMarshaller.class);
}
.........
Typically, the registration of classes dynamically is a feature of application servers. Since your'e deploying to tomcat, Jersey will likely expect that you're listing your providers and resources in your Application class.
If you were deploying to a full app server, like WildFly or GlassFish, you wouldn't need to do this.

Resources