Elasticsearch Spring boot integration test - java

I am looking for the way to add embedded elasticsearch to my spring boot integration test.
I looked at elastic search integration test but it does not work together with spring boot as both should uses different test runner.
I have a class test as below unfortunately it does not work with error:
java.lang.IllegalStateException: No context information for thread:
Thread[id=1, name=main, state=RUNNABLE, group=main]. Is this thread
running under a class
com.carrotsearch.randomizedtesting.RandomizedRunner runner context?
Add #RunWith(class
com.carrotsearch.randomizedtesting.RandomizedRunner.class) to your
test class. Make sure your code accesses random contexts within
#BeforeClass and #AfterClass boundary (for example, static test class
initializers are not permitted to access random contexts).
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = App.class)
#WebAppConfiguration
#IntegrationTest("server.port:0")
public class TestExample extends ElasticsearchIntegrationTest {
TestRestTemplate testRestTemplate = new TestRestTemplate();
#Value("${local.server.port}")
int port;
#Test
public void testOne(){
ResponseEntity<String> results = testRestTemplate.getForEntity(String.format("http://localhost:%d/client/1", port), String.class);
System.out.print(results);
}
}
Does anybody has some ideas how to make them run or what is alternatives ??

You can actually do what you need without any additional elasticsearch testing dependencies. The idea is basically to create an embedded node and then use the NodeClient to communicate with it.
For that, I created my own EmbeddedElasticsearchServer class which looks (more or less) like this:
public class EmbeddedElasticsearchServer implements InitializingBean {
public EmbeddedElasticsearchServer() {
ImmutableSettings.Builder elasticsearchSettings = ImmutableSettings.settingsBuilder()
.put("http.enabled", "false")
.put("path.data", "target/elasticsearch-data");
node = nodeBuilder()
.local(true)
.settings(elasticsearchSettings.build())
.node();
client = node.client();
}
#Override
public void afterPropertiesSet() throws Exception {
// Initialization stuff:
// - create required indices
// - define mappings
// - populate with test data
}
public Client getClient() {
return client;
}
}
Then, in spring configuration (let's call it integration-test-context.xml) I did this:
<bean id="embeddedElasticsearchServer"
class="com.example.EmbeddedElasticsearchServer" />
<bean id="elasticsearchClient"
class="org.elasticsearch.client.node.NodeClient"
factory-bean="embeddedElasticsearchServer"
factory-method="getClient" />
Then you can just autowire the client in your test like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/integration-test-context.xml")
public abstract class AbstractElasticsearchIntegrationTest {
#Autowired
private Client elasticsearchClient;
// Your rests go here...
}

Related

#Autowired field null when executing production code via integration tests

Bit of a weird one that I've been scratching my head over for the past few days. I have a JPA repository that is field injected into a service class. Is works perfectly when running the server and sending a request via a client but when the code is executed via integration tests the field injected class (CustomerRepository ) is always null.
I've tried various advice via the internet but I've not found a similar scenario to mine, any help would be much appreciated
Service class
#GRpcService
public class CustomerService extends CustomerServiceGrpc.CustomerServiceImplBase {
#Autowired
private CustomerRepository repository;
#Override
public void createCustomer(CreateCustomerRequest request, StreamObserver<CreateCustomerResponse> responseObserver) {
final CustomerDao convertedDao = ProtoToDaoConverter.convertCustomerRequestProtoToCustomerDao(request);
repository.save(convertedDao);
responseObserver.onNext(CreateCustomerResponse.newBuilder().setSuccess(true).build());
responseObserver.onCompleted();
}
}
Integration test
#ExtendWith(SpringExtension.class)
#SpringBootTest
public class CustomerServiceIT {
#Rule
private final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
#Test
public void something() throws IOException {
String serverName = InProcessServerBuilder.generateName();
// Create a server, add service, start, and register for automatic graceful shutdown.
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(new CustomerService()).build().start());
customerServiceGrpc.CustomerServiceBlockingStub blockingStub = CustomerServiceGrpc.newBlockingStub(
// Create a client channel and register for automatic graceful shutdown.
grpcCleanup.register(InProcessChannelBuilder.forName(serverName).directExecutor().build()));
final CreateCustomerRequest request = CreateCustomerRequest.newBuilder().setFirstName("Simon").setSecondName("Brown").setRole("Product Developer").build();
final CreateCustomerResponse response = blockingStub.createCustomer(request);
}
}
In test you invoke new CustomerService(). You create an object by itself, not via spring. I guess you should create a field in test class
#Autowired private final CustomerService customerService
and pass it in
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(customerService).build().start());
You can use Mockito or any other tests framework to mock your class dependencies (your service and your JPA Repository).
You can use these features :
#InjectMocks - Instantiates testing object instance and tries to inject fields annotated with #Mock or #Spy into private fields of testing object
#Mock - Creates mock instance of the field it annotates
In your test class your have to use #Mock to inject "a fake respository" :
#ExtendWith(SpringExtension.class)
#SpringBootTest
public class CustomerServiceTest {
#InjectMocks
private CustomerService testingObject;
#Mock
private CustomerRepository customRepository;
#Rule
private final GrpcCleanupRule grpcCleanup = new GrpcCleanupRule();
#BeforeMethod
public void initMocks(){
MockitoAnnotations.initMocks(this);
}
#Test
public void something() throws IOException {
// inside the testing method you have to define what you mocked object should return.
// it means mocking the CustomRepository methods
CustomerDao customer = new CustomerDao(); // fill it with data
Mockito.when(customRepository.save()).thenReturn(customer);
// Create a server, add service, start, and register for automatic graceful shutdown.
grpcCleanup.register(InProcessServerBuilder
.forName(serverName).directExecutor().addService(new CustomerService()).build().start());
customerServiceGrpc.CustomerServiceBlockingStub blockingStub = CustomerServiceGrpc.newBlockingStub(
// Create a client channel and register for automatic graceful shutdown.
grpcCleanup.register(InProcessChannelBuilder.forName(serverName).directExecutor().build()));
final CreateCustomerRequest request = CreateCustomerRequest.newBuilder().setFirstName("Simon").setSecondName("Brown").setRole("Product Developer").build();
final CreateCustomerResponse response = blockingStub.createCustomer(request);
}
}
As your repository and it's methods are mock you customerService should be populated with fake data for test purpose.
Note : : It nice to have a naming convention for classes and especially tests classes. A common use is to always give the a suffix of xxTest as i did in the answer
It's hard to answer without being able to read stacktraces. I would start investigating this problem by looking at the spring context configuration classes. Maybe some of them are missing at the runtime of your integration test.
If spring #Configuration or other #Component classes exist in your application, it might help to load them explicitly with your tests, along with your unexpectedly null-value CustomerRepository:
#ExtendWith(SpringExtension.class)
#SpringBootTest(classes = {AppConfig.class, CustomerRepository.class })
public class CustomerServiceIT {
This might not solve it, but maybe reveals some error messages that help you to investigate the problem.

spring-boot - running junit test with two servers

I created with java spring-boot a server (“node”). There are a few instance of nodes, as the difference is by a configuration file for every node. For example:
node1.properties:
application.name=FullNode
receiving.server.addresses=tcp://localhost:8001
propagation.server.addresses=tcp://localhost:8002
recovery.server.address=http://localhost:8060
....
node 2.properties:
application.name=FullNode
receiving.server.addresses=tcp://localhost:6001
propagation.server.addresses=tcp://localhost:6002
recovery.server.address=http://localhost:8050
...
To test the process of sending data to the server, I wrote a JUnit test for the TransactionController.
TransactionController:
#RestController
#RequestMapping("/transaction")
public class TransactionController {
#Autowired
private TransactionService transactionService;
...
#RequestMapping(method = PUT)
public ResponseEntity<Response> addTransaction(#Valid #RequestBody
AddTransactionRequest addTransactionRequest) {
return transactionService.addNewTransaction(addTransactionRequest);
}
...
}
Test:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = AppConfig.class)
#SpringBootTest
public class DBTests {
#Autowired
private TransactionController transactionController;
#Test
public void addTransaction() {
transactionController.addTransaction(transactionRequest);
}
}
The problem is, that every node also sends his transactions to the other nodes. But How I could test it with JUnit? I could not just create in the test two instance of a TransactionController, because TransactionController is a spring bean singleton, and the only way to run a node, is with his configuration file.
How could I do it?
I would look at the Mockito framework. Mocking out the TransactionService. Instead of firing up 2 servers which lends itself to be more of an integration test instead of a unit test.

Using #PostConstruct in a test class causes it to be called more than once

I am writing integration tests to test my endpoints and need to setup a User in the database right after construct so the Spring Security Test annotation #WithUserDetails has a user to collect from the database.
My class setup is like this:
#RunWith(value = SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#WithUserDetails(value = "email#address.com")
public abstract class IntegrationTests {
#Autowired
private MockMvc mockMvc;
#Autowired
private Service aService;
#PostConstruct
private void postConstruct() throws UserCreationException {
// Setup and save user data to the db using autowired service "aService"
RestAssuredMockMvc.mockMvc(mockMvc);
}
#Test
public void testA() {
// Some test
}
#Test
public void testB() {
// Some test
}
#Test
public void testC() {
// Some test
}
}
However the #PostConstruct method is called for every annotated #Test, even though we are not instantiating the main class again.
Because we use Spring Security Test (#WithUserDetails) we need the user persisted to the database BEFORE we can use the JUnit annotation #Before. We cannot use #BeforeClass either because we rely on the #Autowired service: aService.
A solution I found would be to use a variable to determine if we have already setup the data (see below) but this feels dirty and that there would be a better way.
#PostConstruct
private void postConstruct() throws UserCreationException {
if (!setupData) {
// Setup and save user data to the db using autowired service "aService"
RestAssuredMockMvc.mockMvc(mockMvc);
setupData = true;
}
}
TLDR : Keep your way for the moment. If later the boolean flag is repeated in multiple test classes create your own TestExecutionListener.
In JUnit, the test class constructor is invoked at each test method executed.
So it makes sense that #PostConstruct be invoked for each test method.
According to JUnit and Spring functioning, your workaround is not bad. Specifically because you do it in the base test class.
As less dirty way, you could annotate your test class with #TestExecutionListeners and provide a custom TestExecutionListener but it seem overkill here as you use it once.
In a context where you don't have/want the base class and you want to add your boolean flag in multiple classes, using a custom TestExecutionListener can make sense.
Here is an example.
Custom TestExecutionListener :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.web.client.TestRestTemplate;
import org.springframework.test.context.TestContext;
import org.springframework.test.context.support.AbstractTestExecutionListener;
public class MyMockUserTestExecutionListener extends AbstractTestExecutionListener{
#Override
public void beforeTestClass(TestContext testContext) throws Exception {
MyService myService = testContext.getApplicationContext().getBean(MyService.class);
// ... do my init
}
}
Test class updated :
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#AutoConfigureMockMvc
#WithUserDetails(value = "email#address.com")
#TestExecutionListeners(mergeMode = MergeMode.MERGE_WITH_DEFAULTS,
value=MyMockUserTestExecutionListener.class)
public abstract class IntegrationTests {
...
}
Note that MergeMode.MERGE_WITH_DEFAULTS matters if you want to merge TestExecutionListeners coming from the Spring Boot test class with TestExecutionListeners defined in the #TestExecutionListeners of the current class.
The default value is MergeMode.REPLACE_DEFAULTS.

How to not connect to service when testing with Spring?

I have an application built with JHipster which contains several tests.
I created a simple configuration class that instantiate a bean connected to an external service as such :
#Configuration
public class KurentoConfiguration {
#Bean(name = "kurentoClient")
public KurentoClient getKurentoClient(#Autowired ApplicationProperties applicationProperties) {
return KurentoClient.create(applicationProperties.getKurento().getWsUrl());
}
}
But as you would guess, this code crash during testing because the external service is not up but this code is still run during application context loading.
So I need to create a "stateless" version of this bean to be used during testing.
Here is a simple example of a test that fail because of my configuration :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Face2FaceApp.class)
public class LogsResourceIntTest {
private MockMvc restLogsMockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
LogsResource logsResource = new LogsResource();
this.restLogsMockMvc = MockMvcBuilders
.standaloneSetup(logsResource)
.build();
}
#Test
public void getAllLogs()throws Exception {
restLogsMockMvc.perform(get("/management/logs"))
.andExpect(status().isOk())
.andExpect(content().contentType(MediaType.APPLICATION_JSON_UTF8_VALUE));
}
}
What is the solution to make this bean not highly dependent of an external service during unit testing ?
You can use the MockBean annotation in your test to replace your existing bean :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Face2FaceApp.class)
public class LogsResourceIntTest {
#MockBean
private KurentoClient kurentoClient;
private MockMvc restLogsMockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
LogsResource logsResource = new LogsResource();
this.restLogsMockMvc = MockMvcBuilders
.standaloneSetup(logsResource)
.build();
given(kurentoClient.someCall()).willReturn("mock");
}
....
}
Here is the Spring Boot documentation :
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-testing.html#boot-features-testing-spring-boot-applications-mocking-beans
Thanks to the help of everyone here, I managed to solve this problem :
I created an interface of KurentoClient and implemented a proxy that call KurentoClient methods
My "normal" #Bean kurentoClient returns the implemented proxy
I writed a #TestConfiguration (UnitTestConfiguration) and added a #Bean with the same signature as the one crated above but this one returns mockito's mock(KurentoClient.class)
I created a class TestBase that every test class extends and which
contains
#RunWith(SpringRunner.class)
#SpringBootTest(classes = MyApp.class)
#ContextConfiguration(classes = UnitTestConfiguration.class)
public class TestBase {
}

Testing Spring boot REST resource issues

There is a microservice made with Spring Boot. It consist of Jetty, Jersey, Jackson and Liquibase. One of tasks of this service is to receive some data via REST and return response. This operation goes through next units:
MyResource, #Component REST with JAX-RS annotations, that receives data and ask #Autowired MyService for response.
MyService, #Component service with #Autowired MyResource to ask it for response.
MyResource, simple #JpaRepository interface
This application works fine, but now I need to add some tests for every module. Usually I use Mockito to test units, so I test my service with mocked MyRepository (Mockito #Mock annotation + when() method). I want to test MyResource.java the same way.
I tried to use TestRestTemplate way to test with spring-boot-starter-test and my test class looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = Application.class)
#WebIntegrationTest(randomPort = true)
public class MyResourceTest {
#Value("${local.server.port}")
private int port;
private String getBaseUrl() {
return "http://localhost:" + port;
}
#Test
public void test() {
final TestRestTemplate restTemplate = new TestRestTemplate();
assertEquals(restTemplate.postForEntity(getBaseUrl() + "/test", null, ResponseObject.class).getBody(), new ResponseObject());
}
}
And there are two problems. First - when my test is running, they run up whole spring application, so my liquibase scripts is trying to find database and this is a very long-time process. Second - I can't replace MyService class with Mockito proxy.
I tried to find some manuals about best practices in testing spring boot REST applications and I found MockMvc-based way, but it looks like don't run up server to run test. Can you please share your way to test REST resource in spring boot?
MockMvc is the prefered solution for your problem.
It runs the spring boot application but 'mocks' the request,so it does not really run over http but behaves as such.
You can get all beans of your application injected into your test class using #Autowired, so you can mock spring beans there.
I run all my tests over 6 classes of configuration/support.
AbstractTest to configure core of tests
#ActiveProfiles(resolver = TestActiveProfilesResolver.class)
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#IntegrationTest
#SpringApplicationConfiguration(classes = Application.class)
public abstract class AbstractTest {
...
}
AbstractRepositoryTest to test my repositories over jdbc+jdbi
#Transactional
public abstract class AbstractRepositoryTest<R> extends AbstractTest implements Repositories {
#Inject
private ObjectMapper mapper;
#Inject
protected Repositories repositories;
private final Class<R> repositoryType;
...
}
AbstractServiceTest to test my services, this class contains the core of my service's test
public abstract class AbstractServiceTest {
...
}
AbstractIntegrationTest contains the core of my integration tests, utils methods to test controller's, etc.
public abstract class AbstractIntegrationTest extends AbstractTest {
...
}
Application provides to AbstractTest a context to run tests, this classe is same class that I use to run my spring boot application
#SpringBootApplication
public class Application extends SpringBootServletInitializer {
public static void main(final String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
return application.sources(Application.class);
}
...
}
And finnaly the TestActiveProfilesResolver, that provide the profile to match application-test.properties, It's necessary because exist the open issue on JIRA, here
public class TestActiveProfilesResolver implements ActiveProfilesResolver {
#Override
public String[] resolve(final Class<?> testClass) {
final String activeProfile = System.getProperty("spring.profiles.active");
return new String[] {activeProfile == null ? "test" : activeProfile};
}
}
Sometimes my tests extend AbstractRepositoryTest, AbstractIntegrationTests or AbstractServiceTest, it depends on what I want.
This configuration solved all my problems to test services, controllers etc.

Categories