Micronaut set up EmbeddedServer for Pact test - java

I have this SpringBoot and Pact test example from Writing Contract Tests with Pact in Spring Boot
#RunWith(SpringRunner.class)
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = "user-service.base-url:http://localhost:8080",
classes = UserServiceClient.class)
public class UserServiceContractTest {
#Rule
public PactProviderRuleMk2 provider = new PactProviderRuleMk2("user-service", null,
8080, this);
#Autowired
private UserServiceClient userServiceClient;
#Pact(consumer = "messaging-app")
public RequestResponsePact pactUserExists(PactDslWithProvider builder) {
return builder.given("User 1 exists")
.uponReceiving("A request to /users/1")
.path("/users/1")
.method("GET")
.willRespondWith()
.status(200)
.body(LambdaDsl.newJsonBody((o) -> o
.stringType("name", “user name for CDC”)
).build())
.toPact();
}
#PactVerification(fragment = "pactUserExists")
#Test
public void userExists() {
final User user = userServiceClient.getUser("1");
assertThat(user.getName()).isEqualTo("user name for CDC");
}
}
In order to generate the PACT file I need to start a mock Provider, which is set up as:
public PactProviderRuleMk2 provider = new PactProviderRuleMk2("user-service", null,
8080, this);
The #SpringBootTest annotation provides a mock web environment running on http://localhost:8080
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE,
properties = "user-service.base-url:http://localhost:8080",
classes = UserServiceClient.class)
Is it possible to do something similar in Micronaut? Can I use an EmbeddedServer running in a specified port such as http://localhost:8080 so my Pact MockProvider can listen to that port?
I would like to specify the port in the test class, not into an application.yml file
Any ideas?

You can use micronaut and pact with junit5, simple example based on hello-world-java:
Add pact dependencies to build.gradle:
// pact
compile 'au.com.dius:pact-jvm-consumer-junit5_2.12:3.6.10'
compile 'au.com.dius:pact-jvm-provider-junit5_2.12:3.6.10'
// client for target example
compile 'io.micronaut:micronaut-http-client'
FooService.java:
import io.micronaut.http.client.RxHttpClient;
import io.micronaut.http.client.annotation.Client;
import javax.inject.Inject;
import javax.inject.Singleton;
import static io.micronaut.http.HttpRequest.GET;
#Singleton
public class FooService {
#Inject
#Client("http://localhost:8080")
private RxHttpClient httpClient;
public String getFoo() {
return httpClient.retrieve(GET("/foo")).blockingFirst();
}
}
FooServiceTest.java:
import au.com.dius.pact.consumer.Pact;
import au.com.dius.pact.consumer.dsl.PactDslWithProvider;
import au.com.dius.pact.consumer.junit5.PactConsumerTestExt;
import au.com.dius.pact.consumer.junit5.PactTestFor;
import au.com.dius.pact.model.RequestResponsePact;
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.api.Test;
import javax.inject.Inject;
import static org.junit.jupiter.api.Assertions.assertEquals;
#MicronautTest
#ExtendWith(PactConsumerTestExt.class)
#PactTestFor(providerName = "foo", hostInterface = "localhost", port = "8080")
public class FooServiceTest {
#Inject
FooService fooService;
#Pact(provider = "foo", consumer = "foo")
public RequestResponsePact pact(PactDslWithProvider builder) {
return builder
.given("test foo")
.uponReceiving("test foo")
.path("/foo")
.method("GET")
.willRespondWith()
.status(200)
.body("{\"foo\":\"bar\"}")
.toPact();
}
#Test
public void testFoo() {
assertEquals(fooService.getFoo(), "{\"foo\":\"bar\"}");
}
}

Related

How to write Junit5 test cases in Springboot for apache camel Producer Template sendBodyAndHeader() to publish a json event to kafka

Need a help to write junit5 test case in springboot for a post api where it uses apache camel producer template to send message to kafka. Please find the controller class details for which junit test cases are required. Note-I don't have any service/repository layer for this.This is standalone controller which is responsible to publish message to kafka by using camel producer template.Thanks is advance.
Controller Class-->
`
`import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.rms.inventory.savr.audit.model.AuditInfo;
import java.util.Map;
import javax.annotation.PostConstruct;
import org.apache.camel.ProducerTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class MockKafkaProducerController {
#Autowired ProducerTemplate producerTemplate;
#Value("${audit.inbound.endpoint.kafka.uri:audit-json-topic}")
private String auditTopicKafka;
#Value("${camel.component.kafka.consumer.supply}")
private String supplyLineUpdateKafkaTopic;
#Value("${camel.component.kafka.consumer.demand}")
private String demandLineUpdateKafkaTopic;
#Value("${camel.component.kafka.consumer.supply-bucket}")
private String supplybucketTopicKafka;
#Value("${camel.component.kafka.consumer.demand-bucket}")
private String demandbucketTopicKafka;
#Value("${camel.component.kafka.consumer.availability}")
private String availabilityTopicKafka;
private Map<String, String> dataTypeTopicMap;
#PostConstruct
void init() {
dataTypeTopicMap =
Map.of(
"SUPPLY_LINE",
supplyLineUpdateKafkaTopic,
"SUPPLY_BUCKET",
supplybucketTopicKafka,
"DEMAND_LINE",
demandLineUpdateKafkaTopic,
"DEMAND_BUCKET",
demandbucketTopicKafka,
"AVAILABILITY",
availabilityTopicKafka);
}
#PostMapping("/api/mock/producer")
public ResponseEntity<Boolean> saveAuditInfo(#RequestBody AuditInfo auditInfo)
public ResponseEntity<Boolean> saveAuditInfo(
#RequestBody AuditInfo auditInfo, #RequestHeader("AUDIT_TYPE") String auditType)
throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.findAndRegisterModules();
if (auditType == null || auditType.isEmpty()) {
auditType = "SUPPLY_LINE";
}
String topicName = dataTypeTopicMap.get(auditType);
// producerTemplate.
producerTemplate.sendBodyAndHeader(
auditTopicKafka, objectMapper.writeValueAsString(auditInfo), "messageFormat", "CANONICAL");
topicName, objectMapper.writeValueAsString(auditInfo), "messageFormat", "CANONICAL");
return ResponseEntity.ok(Boolean.TRUE);
}
#PostMapping("/api/inventory/audit/mock/producer")
public ResponseEntity<Boolean> publishInventoryAudit(#RequestBody String auditInfo)
public ResponseEntity<Boolean> publishInventoryAudit(
#RequestBody String auditInfo, #RequestHeader("AUDIT_TYPE") String auditType)
throws JsonProcessingException {
producerTemplate.sendBody(auditTopicKafka, auditInfo);
if (auditType == null || auditType.isEmpty()) {
auditType = "SUPPLY_LINE";
}
String topicName = dataTypeTopicMap.get(auditType);
producerTemplate.sendBody(topicName, auditInfo);
return ResponseEntity.ok(Boolean.TRUE);
}
}`
`
I tried to mock producer template but not able to fix.

Unable to mock AWSSecretsManager in Spring boot using Mockito

I am new to Java and I have created sample class for which I need to write unit test case
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
import com.amazonaws.services.secretsmanager.model.InvalidParameterException;
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
import org.springframework.stereotype.Component;
#Component
public class MySecretService {
private AWSSecretsManager client;
public MySecretService() {
this.client = AWSSecretsManagerClientBuilder.standard().withRegion("US-west-2").build();
}
public String GetMyKey(String secretId) {
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest();
getSecretValueRequest.setSecretId(secretId);
try {
GetSecretValueResult getSecretValueResult = client.getSecretValue(getSecretValueRequest);
return getSecretValueResult.getSecretString();
} catch (ResourceNotFoundException resourceNotFoundException) {
throw resourceNotFoundException;
} catch (InvalidParameterException invalidParameterException) {
throw invalidParameterException;
}
catch (Exception ex)
{
throw ex;
}
}
}
I tried to write unit test case as below
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
import com.amazonaws.services.secretsmanager.AWSSecretsManagerClientBuilder;
import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
import com.amazonaws.services.secretsmanager.model.ResourceNotFoundException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
class MySecretServiceTest {
#Mock
private AWSSecretsManager client;
private MySecretService mySecretService;
#BeforeEach
void setUp() {
this.mySecretService = new MySecretService();
}
#Test
void getMyKey() {
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest();
getSecretValueRequest.setSecretId("test");
GetSecretValueResult getSecretValueResult = new GetSecretValueResult();
getSecretValueResult.setSecretString("xyz");
when(this.client.getSecretValue(getSecretValueRequest))
.thenReturn( getSecretValueResult);
assertEquals("xyz", mySecretService.GetMyKey("test"));
}
}
However my test is getting failed where it says AWSSecretsManager is null with below error.
java.lang.NullPointerException
at com.example.demo.MySecretServiceTest.getMyKey(MySecretServiceTest.java:40)
I tried different solution but won't help me to execute the test cases successfully.
I think that way how did you structure your code, you make it really hard to inject AWSSecretsManager client. This is because it is not exposed as a dependency in the MySecretService service.
What I suggest doing is the following:
Create a configuration class and put an instance for AWSSecretsManager in the IoC container:
#Configuration
public class AWSConfig {
#Bean
public AWSSecretsManager awsSecretsManager() {
return AWSSecretsManagerClientBuilder.standard()
.withRegion("us-east-1")
.build();
}
}
Inject the bean of AWSSecretsManager inside your service:
#Service
public class MySecretService {
private final AWSSecretsManager awsSecretsManager;
public SecretsService(AWSSecretsManager awsSecretsManager) {
this.awsSecretsManager = awsSecretsManager;
}
public String getMyKey(String secretId) {
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest().withSecretId(secretId);
try {
GetSecretValueResult getSecretValueResult = awsSecretsManager.getSecretValue(getSecretValueRequest);
return getSecretValueResult.getSecretString();
} catch (ResourceNotFoundException | InvalidParameterException resourceNotFoundException) {
throw resourceNotFoundException;
}
}
}
Now, in your test I see that you are mixing JUnit 4 and Junit 5 dependencies, which is not a good idea. The latest Spring Boot starter for testing comes with JUnit 5, I suggest sticking to it. Knowing this, we can do the following:
import com.amazonaws.services.secretsmanager.AWSSecretsManager;
import com.amazonaws.services.secretsmanager.model.GetSecretValueRequest;
import com.amazonaws.services.secretsmanager.model.GetSecretValueResult;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;
#ExtendWith(MockitoExtension.class)
class MySecretServiceTest {
#Mock
private AWSSecretsManager client;
#InjectMocks
private SecretsService mySecretService;
#Test
void getMyKey() {
GetSecretValueRequest getSecretValueRequest = new GetSecretValueRequest();
getSecretValueRequest.setSecretId("test");
GetSecretValueResult getSecretValueResult = new GetSecretValueResult();
getSecretValueResult.setSecretString("xyz");
when(client.getSecretValue(getSecretValueRequest))
.thenReturn(getSecretValueResult);
assertEquals("xyz", mySecretService.GetMyKey("test"));
}
}

How to inject mongoclient to my POST service

I have a very simple Quarkus application which accepts input and insert it into MongoDB using MongoClient.
Controller:
#ApplicationScoped
#Path("/endpoint")
public class A {
#Inject
B service;
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public Document add(List<? extends Document> list) {
return service.add(list);
}
}
Service Class:
#ApplicationScoped
public class B {
#Inject
MongoClient mongoClient;
private MongoCollection<Document> getCollection() {
return mongoClient.getDatabase(DBname).getCollection(coll);
}
public Document add(List<? extends Document> list) {
Document response = new Document();
getCollection().deleteMany(new BasicDBObject());
getCollection().insertMany(list);
response.append("count", list.size());
return response;
}
}
As you see that my service removes existing data and inserts the new data. For JUnit testing, I am trying to set up embedded MongoDB and want my service call to use the embedded mongo. But no success.
My JUnit class
I tried out many approaches discussed on the internet to set up the embedded mongo but none worked for me.
I want to invoke my POST service but actual mongodb must not get connected. My JUnit class is as below:
#QuarkusTest
public class test {
List<Document> request = new ArrayList<Document>();
Document doc = new Document();
doc.append("Id", "007")
.append("name", "Nitin");
request.add(doc);
given()
.body(request)
.header("Content-Type", MediaType.APPLICATION_JSON)
.when()
.post("/endpoint")
.then()
.statusCode(200);
}
You need to use a different connection-string for your test than for your regular (production) run.
Quakus can use profiles to do this, the %test profile is automatically selected when running #QuarkusTest tests.
So you can add in your application.properties something like this :
quarkus.mongodb.connection-string=mongodb://host:port
%test.quarkus.mongodb.connection-string=mongodb://localhost:27017
Here mongodb://host:port will be use on the normal run of your application and mongodb://localhost:27017 will be used from inside your test.
Then you can use flapdoodle or Testcontainers to launch a MongoDB database on localhost during your test.
More information on configuration profiles: https://quarkus.io/guides/config#configuration-profiles
More information on how to start an external service from a Quarkus test: https://quarkus.io/guides/getting-started-testing#quarkus-test-resource
Have u tried flapdoodle:
package com.example.mongo;
import com.mongodb.BasicDBObject;
import com.mongodb.MongoClient;
import com.mongodb.client.MongoCollection;
import com.mongodb.client.MongoDatabase;
import de.flapdoodle.embed.mongo.MongodExecutable;
import de.flapdoodle.embed.mongo.MongodProcess;
import de.flapdoodle.embed.mongo.MongodStarter;
import de.flapdoodle.embed.mongo.config.IMongodConfig;
import de.flapdoodle.embed.mongo.config.MongodConfigBuilder;
import de.flapdoodle.embed.mongo.config.Net;
import de.flapdoodle.embed.mongo.distribution.Version;
import de.flapdoodle.embed.process.runtime.Network;
import java.util.Date;
import org.junit.After;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
public class EmbeddedMongoTest
{
private static final String DATABASE_NAME = "embedded";
private MongodExecutable mongodExe;
private MongodProcess mongod;
private MongoClient mongo;
#Before
public void beforeEach() throws Exception {
MongodStarter starter = MongodStarter.getDefaultInstance();
String bindIp = "localhost";
int port = 12345;
IMongodConfig mongodConfig = new MongodConfigBuilder()
.version(Version.Main.PRODUCTION)
.net(new Net(bindIp, port, Network.localhostIsIPv6()))
.build();
this.mongodExe = starter.prepare(mongodConfig);
this.mongod = mongodExe.start();
this.mongo = new MongoClient(bindIp, port);
}
#After
public void afterEach() throws Exception {
if (this.mongod != null) {
this.mongod.stop();
this.mongodExe.stop();
}
}
#Test
public void shouldCreateNewObjectInEmbeddedMongoDb() {
// given
MongoDatabase db = mongo.getDatabase(DATABASE_NAME);
db.createCollection("testCollection");
MongoCollection<BasicDBObject> col = db.getCollection("testCollection", BasicDBObject.class);
// when
col.insertOne(new BasicDBObject("testDoc", new Date()));
// then
assertEquals(1L, col.countDocuments());
}
}
Reference : Embedded MongoDB when running integration tests
Thanks everyone for suggestions. I declared test collections in application.properties file. %test profile automatically get activated when we run junits, so automatically my services picked up the test collections. I deleted the test collections after my junit test cases got completed.

SpringBootTest with RANDOM_PORT in Spring Boot 2.1.1 is using 8080 every time

I have a test class that is running in Spring Boot 2.1.1 and Java 11 and no matter what I do, it runs on port 8080:
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestPropertySource;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
#RunWith( SpringJUnit4ClassRunner.class )
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = TestClass.Config.class
)
#ContextConfiguration(
classes = TestClass.Config.class
)
#TestPropertySource( properties = "server.port=0" )
public class TestClass
{
#LocalServerPort
private String port;
#Test
public void testPort() throws Exception
{
mockMvc
.perform(
MockMvcRequestBuilders.get( "/" )
)
.andExpect( MockMvcResultMatchers.status().isOk() );
}
#Configuration
#RestController
public static class Config
{
#Bean
ServletWebServerFactory servletWebServerFactory()
{
return new TomcatServletWebServerFactory();
}
#GetMapping( "/" )
public String test(HttpServletRequest request, HttpServletResponse response)
{
//This still shows no random port
System.out.println( request.getLocalPort() );
return "ok";
}
}
}
Even when I try this:
#Bean
ServletWebServerFactory servletWebServerFactory()
{
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.setPort( SocketUtils.findAvailableTcpPort() );
return factory;
}
which does result in the field port as having a random port number, MockMvc still uses the default port for my controller.
How can I get it to use a random port?
Try setting the port to 0 , it worked for me.
#Bean
ServletWebServerFactory servletWebServerFactory() {
return new TomcatServletWebServerFactory(0);
}
Running the test twice produces 2 different random ports.
63957
64043
Try this. You are free to use TestRestTemplate instead of RestAssured and MockBean as required
import static org.hamcrest.Matchers.equalTo;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.web.server.LocalServerPort;
import io.restassured.RestAssured;
#SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class BookIT_SpringBootTest_WithWebServerAndRestAssured {
#LocalServerPort
private int port;
#Test
public void myTest() {
RestAssured
.given()
.baseUri("http://localhost/api")
.port(port)
.queryParam("id", "1")
.when()
.get("/book/get-book-id")
.then()
.statusCode(200);
}
}

Start elasticsearch within gradle build for integration tests

Is there a way to start elasticsearch within a gradle build before running integration tests and afterwards stop elasticsearch?
My approach so far is the following, but this blocks the further execution of the gradle build.
task runES(type: JavaExec) {
main = 'org.elasticsearch.bootstrap.Elasticsearch'
classpath = sourceSets.main.runtimeClasspath
systemProperties = ["es.path.home":"$buildDir/elastichome",
"es.path.data":"$buildDir/elastichome/data"]
}
For my purpose i have decided to start elasticsearch within my integration test in java code.
I've tried out ElasticsearchIntegrationTest but that didn't worked with spring, because it didn't harmony with SpringJUnit4ClassRunner.
I've found it easier to start elasticsearch in the before method:
My test class testing some 'dummy' productive code (indexing a document):
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.ImmutableSettings.Builder;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
import org.elasticsearch.indices.IndexAlreadyExistsException;
import org.elasticsearch.node.Node;
import org.elasticsearch.node.NodeBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class MyIntegrationTest {
private Node node;
private Client client;
#Before
public void before() {
createElasticsearchClient();
createIndex();
}
#After
public void after() {
this.client.close();
this.node.close();
}
#Test
public void testSomething() throws Exception {
// do something with elasticsearch
final String json = "{\"mytype\":\"bla\"}";
final String type = "mytype";
final String id = index(json, type);
assertThat(id, notNullValue());
}
/**
* some productive code
*/
private String index(final String json, final String type) {
// create Client
final Settings settings = ImmutableSettings.settingsBuilder().put("cluster.name", "mycluster").build();
final TransportClient tc = new TransportClient(settings).addTransportAddress(new InetSocketTransportAddress(
"localhost", 9300));
// index a document
final IndexResponse response = tc.prepareIndex("myindex", type).setSource(json).execute().actionGet();
return response.getId();
}
private void createElasticsearchClient() {
final NodeBuilder nodeBuilder = NodeBuilder.nodeBuilder();
final Builder settingsBuilder = nodeBuilder.settings();
settingsBuilder.put("network.publish_host", "localhost");
settingsBuilder.put("network.bind_host", "localhost");
final Settings settings = settingsBuilder.build();
this.node = nodeBuilder.clusterName("mycluster").local(false).data(true).settings(settings).node();
this.client = this.node.client();
}
private void createIndex() {
try {
this.client.admin().indices().prepareCreate("myindex").execute().actionGet();
} catch (final IndexAlreadyExistsException e) {
// index already exists => we ignore this exception
}
}
}
It is also very important to use elasticsearch version 1.3.3 or higher. See Issue 5401.

Categories