Giter Site home page Giter Site logo

microcks / microcks-testcontainers-java Goto Github PK

View Code? Open in Web Editor NEW
17.0 4.0 3.0 177 KB

Java lib for Testcontainers that enables embedding Microcks into your JUnit tests with lightweight, throwaway instance thanks to containers.

Home Page: https://microcks.io

License: Apache License 2.0

Java 100.00%
api contract-testing java microcks mocking testcontainers

microcks-testcontainers-java's Introduction

Microcks Testcontainers Java

Java library for Testcontainers that enables embedding Microcks into your JUnit tests with lightweight, throwaway instance thanks to containers

GitHub Workflow Status Version License Project Chat

Build Status

Latest released version is 0.2.7.

Current development version is 0.2.8-SNAPSHOT.

Sonarcloud Quality metrics

Code Smells Reliability Rating Bugs Coverage Technical Debt Security Rating Maintainability Rating

How to use it?

Include it into your project dependencies

If you're using Maven:

<dependency>
  <groupId>io.github.microcks</groupId>
  <artifactId>microcks-testcontainers</artifactId>
  <version>0.2.7</version>
</dependency>

or if you're using Gradle:

dependencies {
    testImplementation 'io.github.microcks:microcks-testcontainers:0.2.7'
}

Startup the container

You just have to specify the container image you'd like to use. This library requires a Microcks uber distribution (with no MongoDB dependency).

Version 0.2.1 and above require version 1.8.1 of Microcks container images.

MicrocksContainer microcks = new MicrocksContainer(
      DockerImageName.parse("quay.io/microcks/microcks-uber:1.8.1"));
microcks.start();

Import content in Microcks

To use Microcks mocks or contract-testing features, you first need to import OpenAPI, Postman Collection, GraphQL or gRPC artifacts. Artifacts can be imported as main/Primary ones or as secondary ones. See Multi-artifacts support for details.

You can do it before starting the container using simple paths:

MicrocksContainer microcks = new MicrocksContainer(DockerImageName.parse("quay.io/microcks/microcks-uber:1.9.0"))
    .withMainArtifacts("apipastries-openapi.yaml")
    .withSecondaryArtifacts("apipastries-postman-collection.json");
microcks.start();

or once the container started using File arguments:

microcks.importAsMainArtifact(new File("target/test-classes/apipastries-openapi.yaml"));
microcks.importAsSecondaryArtifact(new File("target/test-classes/apipastries-postman-collection.json"));

Please refer to our MicrocksContainerTest for comprehensive example on how to use it.

Starting with version 0.2.7 you can also import full repository snapshots at once:

MicrocksContainer microcks = new MicrocksContainer(DockerImageName.parse("quay.io/microcks/microcks-uber:1.9.0"))
      .withSnapshots("microcks-repository.json");
microcks.start();

Using mock endpoints for your dependencies

During your test setup, you'd probably need to retrieve mock endpoints provided by Microcks containers to setup your base API url calls. You can do it like this:

String baseApiUrl = microcks.getRestMockEndpoint("API Pastries", "0.0.1");

The container provides methods for different supported API styles/protocols (Soap, GraphQL, gRPC,...).

The container also provides getHttpEndpoint() for raw access to those API endpoints.

Launching new contract-tests

If you want to ensure that your application under test is conformant to an OpenAPI contract (or other type of contract), you can launch a Microcks contract/conformance test using the local server port you're actually running. This is typically how it could be done for a Spring Boot application:

@LocalServerPort
private Integer port;

@BeforeEach
public void setupPort() {
   // Host port exposition should be done here.
   Testcontainers.exposeHostPorts(port);
}

@Test
public void testOpenAPIContract() throws Exception {
    // Ask for an Open API conformance to be launched.
    TestRequest testRequest = new TestRequest.Builder()
        .serviceId("API Pastries:0.0.1")
        .runnerType(TestRunnerType.OPEN_API_SCHEMA.name())
        .testEndpoint("http://host.testcontainers.internal:" + port)
        .timeout(Duration.ofSeconds(2))
        .build();

    TestResult testResult = microcks.testEndpoint(testRequest);
    assertTrue(testResult.isSuccess());
}

The TestResult gives you access to all details regarding success of failure on different test cases.

A comprehensive Spring Boot demo application illustrating both usages is available here: spring-boot-order-service.

Using authentication Secrets

It's a common need to authenticate to external systems like Http/Git repositories or external brokers. For that, the MicrocksContainer provides the withSecret() method to register authentication secrets at startup:

microcks.withSecret(new Secret.Builder()
      .name("localstack secret")
      .username(localstack.getAccessKey())
      .password(localstack.getSecretKey())
      .build());
microcks.start();

You may reuse this secret using its name later on during a test like this:

TestRequest testRequest = new TestRequest.Builder()
      .serviceId("Pastry orders API:0.1.0")
      .runnerType(TestRunnerType.ASYNC_API_SCHEMA.name())
      .testEndpoint("sqs://eu-east-1/pastry-orders?overrideUrl=http://localstack:45566")
      .secretName("localstack secret")
      .timeout(5000L)
      .build();

Advanced features with MicrocksContainersEnsemble

The MicrocksContainer referenced above supports essential features of Microcks provided by the main Microcks container. The list of supported features is the following:

  • Mocking of REST APIs using different kinds of artifacts,
  • Contract-testing of REST APIs using OPEN_API_SCHEMA runner/strategy,
  • Mocking and contract-testing of SOAP WebServices,
  • Mocking and contract-testing of GraphQL APIs,
  • Mocking and contract-testing of gRPC APIs.

To support features like Asynchronous API and POSTMAN contract-testing, we introduced MicrocksContainersEnsemble that allows managing additional Microcks services. MicrocksContainersEnsemble allow you to implement Different levels of API contract testing in the Inner Loop with Testcontainers!

A MicrocksContainersEnsemble conforms to Testcontainers lifecycle methods and presents roughly the same interface as a MicrocksContainer. You can create and build an ensemble that way:

MicrocksContainersEnsemble ensemble = new MicrocksContainersEnsemble(IMAGE)
    .withMainArtifacts("apipastries-openapi.yaml")
    .withSecondaryArtifacts("apipastries-postman-collection.json")
    .withAccessToHost(true);
ensemble.start();

A MicrocksContainer is wrapped by an ensemble and is still available to import artifacts and execute test methods. You have to access it using:

MicrocksContainer microcks = ensemble.getMicrocksContainer();
microcks.importAsMainArtifact(...);
microcks.getLogs();

Please refer to our MicrocksContainerTest for comprehensive example on how to use it.

Postman contract-testing

On this ensemble you may want to enable additional features such as Postman contract-testing:

ensemble.withPostman();
ensemble.start();

You can execute a POSTMAN test using an ensemble that way:

TestRequest testRequest = new TestRequest.Builder()
    .serviceId("API Pastries:0.0.1")
    .runnerType(TestRunnerType.POSTMAN.name())
    .testEndpoint("http://good-impl:3003")
    .timeout(2500L)
    .build();

TestResult testResult = ensemble.getMicrocksContainer().testEndpoint(testRequest);

Asynchronous API support

Asynchronous API feature need to be explicitly enabled as well. In the case you want to use it for mocking purposes, you'll have to specify additional connection details to the broker of your choice. See an example below with connection to a Kafka broker:

ensemble.withAsyncFeature()
      .withKafkaConnection(new KafkaConnection("kafka:9092"));
ensemble.start();
Using mock endpoints for your dependencies

Once started, the ensemble.getAsyncMinionContainer() provides methods for retrieving mock endpoint names for the different supported protocols (WebSocket, Kafka, SQS and SNS).

String kafkaTopic = ensemble.getAsyncMinionContainer()
      .getKafkaMockTopic("Pastry orders API", "0.1.0", "SUBSCRIBE pastry/orders");
Launching new contract-tests

Using contract-testing techniques on Asynchronous endpoints may require a different style of interacting with the Microcks container. For example, you may need to:

  1. Start the test making Microcks listen to the target async endpoint,
  2. Activate your System Under Tests so that it produces an event,
  3. Finalize the Microcks tests and actually ensure you received one or many well-formed events.

For that the MicrocksContainer now provides a testEndpointAsync(TestRequest request) method that actually returns a CompletableFuture. Once invoked, you may trigger your application events and then get() the future result to assert like this:

// Start the test, making Microcks listen the endpoint provided in testRequest
CompletableFuture<TestResult> testResultFuture = ensemble.getMicrocksContainer().testEndpointAsync(testRequest);

// Here below: activate your app to make it produce events on this endpoint.
// myapp.invokeBusinessMethodThatTriggerEvents();
      
// Now retrieve the final test result and assert.
TestResult testResult = testResultFuture.get();
assertTrue(testResult.isSuccess());

microcks-testcontainers-java's People

Contributors

lbroudoux avatar mathieu-amblard avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

microcks-testcontainers-java's Issues

Remove un-necessary http mime dependency

Reason/Context

Http Mime lib is used just to get a constant for application/json content-type. As we try to avoid embedding libraries in TestContainers (to avoid shading 'em), we must get rid of it.

Description

Remove the dependency, replacing by a simple new string.

Implementation ideas

No response

Allow import of remote artifacts

Reason/Context

As of today, we can only load local artifacts coming from the project structure (and loaded as a classpath resource). Because Microcks promotes reusing shared artifacts, we should also be able to load remote HTTP(S) artifacts within our Testcontainers module.

Description

Provide a withMainRemoteArtifacts(String... artifactsUrl) and a withSecondaryRemoteArtifacts(String... artifactsUrl) methods to allow loading remote artifacts just after the container startup.

Implementation ideas

This method may reuse the /api/artifact/download API endpoint of the running Microcks container.

Allow importing local repository snapshots

Reason/Context

As Microcks allows import of standard artifacts, it also allow loading repository snapshots as described in the documentation here: https://microcks.io/documentation/administrating/snapshots/

Snapshots are lightweight structures that can be used to:

  • easily exchange a set of Services & APIs definitions with another instance of Microcks,
  • easily setup a new Microcks instance dedicated to mocking a functional subsystem - optionally with different configured response times for simulating a real behavior,
  • easily backup your instance if you do not bother losing tests runs and analytics data

Description

We want a new withSnapshots(String... snapshotPath) method that allows specifying snapshots to load at container startup.

We also want a specific importSnapshot(File snapshotFile) generic method that can be used after container startup.

Beware that due to their nature, snapshots should loaded first before loading any other artifacts at startup. Artifacts may then override existing parts of services loaded from snapshots. If explicitly imported after the startup, a snapshot may then delete customization done by artifacts.

Implementation ideas

No response

Allows setting fixed ports for Http and gRPC endpoints

Reason/Context

When used in the context of Quarkus Dev Services, it's easier to be able to set fixed ports for mock endpoints. That ways the application.properties configuration properties of REST or gRPC clients can point to locally defined endpoints handled by Microcks.

Description

We should provide setHttpExposedPort() and setGrpcExposedPort() methods. They must be called before the container is started otherwise they'll have no effects.

Implementation ideas

No response

Allow specification of artifacts to load on MicrocksContainer creation

Reason/Context

As of today, loading artifacts into the Microcks container should be done after the container is started-up using 2 methods:

  • importAsMainArtifact(File artifact) or,
  • importAsSecondaryArtifact(File artifact)

However, this way of doing things doesn't really fit with existing modules' logic - all of them have withSomething() fluent API style methods that allow definition of config at creation. Moreover, our approach can be sometimes harder to achieve cause it means you have to retrieve the container once-initialized (depending on test framework this can tricky) to import artifacts.

Description

Ideally, we'd like to be able to have something like:

MicrocksContainer microcks = new MicrocksContainer(IMAGE)
      .withMainArtifacts("apipastries-openapi.yaml")
      .withSecondaryArtifacts("apipastries-postman-collection.json");

Implementation ideas

Thanks to lifecycle methods like protected void containerIsStarted(InspectContainerResponse containerInfo). existing on GenericContainer, it should be possible to delay the loading of artifact just after the container is running and healthy.

Enhance Javadoc and exception handling

Reason/Context

As of today, we just pushed an early prototype.

Description

We need to sanitize some stuffs regarding exception handling, comments, Javadoc and so on.

Implementation ideas

No response

Remove the methods for using fixed ports

Reason/Context

Fixing ports is not recommended by Test containers as we're - by nature - evolving into volatile environments that should be dynamically configurable / re-configurable. All the ports exposed by a testcontainer should be dynamic with the ability to retrieve them using a dedicated method.

Description

The setHttpExposedPort(int httpPort) and setGrpcExposedPort(int grpcPort) methods where introduced for debugging purpose and specific needs of the Quarkus DevServices but are no longer needed as we now know how to use dynamic ports on Quarkus use-cases ;-)

They can be removed.

Implementation ideas

Remove both methods for fixing Http and Grpc ports.

Provide a fallback method for running a test when you don't have access to MicrocksContainer instance

Reason/Context

As working on the integration of Microcks as a DevService for Quarkus, it appears that it's difficult to have access to the MicrocksContainer devservice instance. This instance was initialized during the augmentation phase and - for the moment - I don't find any way of injecting it into the CDI context for later consumption by the unit tests.

As a consequence, the unit cannot invoke microcksContainer.testEndpoint(testRequest) because of the null instance.

Description

As the Quarkus unit test as access to the URL where the container is running that way:

@ConfigProperty(name= "quarkus.microcks.default")
String microcksContainerUrl;

we could propose a fallback where a static method of MicrocksContainer is used with an extra argument that is actually this microcksContainerUrl which is the Http endpoint.

Implementation ideas

No response

Question: POSTMAN test runner support with test containers

Hi! I am not sure if this is a bug, an unimplemented feature, or an error on my side, so I'm opening as a question for now.

I'm following along and making a sample application modeled after the api-lifecycle/shift-left-demo/spring-boot-order-service project to do contract tests against a sample API that I made for learning. But the behavior Im' seeing is present even in the spring-boot-order-service when I attempt to run using the POSTMAN runner.

Specifically, if you go into api-lifecycle/shift-left-demo/spring-boot-order-service/src/test/java/org/acme/order/api/OrderControllerContractTests.java and change this:

   @Test
   public void testOpenAPIContract() throws Exception {
      // Ask for an Open API conformance to be launched.
      TestRequest testRequest = new TestRequest.Builder()
            .serviceId("Order Service API:0.1.0")
            .runnerType(TestRunnerType.OPEN_API_SCHEMA.name())
            .testEndpoint("http://host.testcontainers.internal:" + port + "/api")
            .build();

To

   @Test
   public void testOpenAPIContract() throws Exception {
      // Ask for an Open API conformance to be launched.
      TestRequest testRequest = new TestRequest.Builder()
            .serviceId("Order Service API:0.1.0")
            .runnerType(TestRunnerType.POSTMAN.name()) // <-- CHANGED TO POSTMAN HERE
            .testEndpoint("http://host.testcontainers.internal:" + port + "/api")
            .build();

      TestResult testResult = microcksContainer.testEndpoint(testRequest);

The tests now fail with this error:

2023-10-17T15:01:20.678-05:00  INFO 18525 --- [           main] i.g.m.testcontainers.MicrocksContainer   : Caught a ConditionTimeoutException for test on http://host.testcontainers.internal:54320/api

And examining the docker container logs for the microcks-uber container that was used shows this log:

19:10:49.574 ERROR 1 --- [    task-3] i.g.m.u.p.PostmanTestStepsRunner         : IOException while executing request

org.apache.hc.client5.http.HttpHostConnectException: Connect to http://localhost:3000 [localhost/127.0.0.1] failed: Connection refused
	at java.base/sun.nio.ch.Net.pollConnect(Native Method)
	at java.base/sun.nio.ch.Net.pollConnectNow(Net.java:672)
	at java.base/sun.nio.ch.NioSocketImpl.timedFinishConnect(NioSocketImpl.java:547)
	at java.base/sun.nio.ch.NioSocketImpl.connect(NioSocketImpl.java:602)
	at java.base/java.net.SocksSocketImpl.connect(SocksSocketImpl.java:327)
	at java.base/java.net.Socket.connect(Socket.java:633)
	at org.apache.hc.client5.http.socket.PlainConnectionSocketFactory.lambda$connectSocket$0(PlainConnectionSocketFactory.java:85)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:569)
	at org.apache.hc.client5.http.socket.PlainConnectionSocketFactory.connectSocket(PlainConnectionSocketFactory.java:84)
	at org.apache.hc.client5.http.socket.ConnectionSocketFactory.connectSocket(ConnectionSocketFactory.java:113)
	at org.apache.hc.client5.http.impl.io.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:181)
	at org.apache.hc.client5.http.impl.io.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:447)
	at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.connectEndpoint(InternalExecRuntime.java:162)
	at org.apache.hc.client5.http.impl.classic.InternalExecRuntime.connectEndpoint(InternalExecRuntime.java:172)
	at org.apache.hc.client5.http.impl.classic.ConnectExec.execute(ConnectExec.java:142)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.ProtocolExec.execute(ProtocolExec.java:192)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.HttpRequestRetryExec.execute(HttpRequestRetryExec.java:96)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.ContentCompressionExec.execute(ContentCompressionExec.java:152)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.RedirectExec.execute(RedirectExec.java:115)
	at org.apache.hc.client5.http.impl.classic.ExecChainElement.execute(ExecChainElement.java:51)
	at org.apache.hc.client5.http.impl.classic.InternalHttpClient.doExecute(InternalHttpClient.java:170)
	at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:106)
	at org.apache.hc.client5.http.impl.classic.CloseableHttpClient.execute(CloseableHttpClient.java:55)
	at org.springframework.http.client.HttpComponentsClientHttpRequest.executeInternal(HttpComponentsClientHttpRequest.java:93)
	at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48)
	at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66)
	at io.github.microcks.util.postman.PostmanTestStepsRunner.runTest(PostmanTestStepsRunner.java:192)
	at io.github.microcks.util.postman.PostmanTestStepsRunner.runTest(PostmanTestStepsRunner.java:58)
	at io.github.microcks.service.TestRunnerService.launchTestsInternal(TestRunnerService.java:199)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:568)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196)
	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
	at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:756)
	at org.springframework.aop.interceptor.AsyncExecutionInterceptor.lambda$invoke$0(AsyncExecutionInterceptor.java:115)
	at org.springframework.util.concurrent.FutureUtils.lambda$toSupplier$0(FutureUtils.java:74)
	at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java:1768)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
	at java.base/java.lang.Thread.run(Thread.java:833)

My guess here is there is no postman executor runtime available when the test container is spinning up the microcks uber jar. Enabling postman was a separate step in my microcks installation, even as a docker extension, so my suspicion is postman is not enabled in the test container so the runner is failing. But that's just an educated guess.

Should this be possible? Is there something else I need to do?

Document how to use Microcks Testcontainers

Describe the bug

README has no information on how to use Microcks Testcontainers. We should at least provide following hints:

  • How to include it into your Java project (Maven + Gradle)
  • How to start a `MicrocksContainer``
  • How to use the Java API in mocking use-case
  • How to use the Java API in contract-testing use-case

Attach any resources that can help us understand the issue.

No response

Allow importing remote artifacts after container startup

Reason/Context

Thanks to #42 we now allow importing remote artifacts but only during container startups. For certain use cases (like using the library from microcks-quarkus, me must allow to import remote artifacts after the container has been started up.

Description

Create new API methods like downloadAsMainRemoteArtifact() or downloadAsSecondaryRemoteArtifact() to allow this.

Implementation ideas

No response

Feedback on importArtifact

Reason/Context

This is for the simplicity of the module, help maintenance and build on top of existing features.

Description

Looking at the code I found the importArtifact method and I was wondering if the docker image already provides the microcks cli and if would be much better to rely on withCopyFileToContainer from Testcontainers than implementing the http upload.

Didn't realize before but I think the same can be applied to testEndpoint, right?

Implementation ideas

No implementation is needed. Test describing the scenario and docs would be helpful. If that make sense then the importArtifact could be removed.

Correctly substitute image name when using Microcks native image

Reason/Context

When building a MicrocksContainersEnsemble including the async features (withAsyncFeatures()), the async minion container image is automatically deduced from the main container image name. As of today, the microcks-uber part of image is replace by microcks-uber-async-minion, leaving the tag unchanged.

This way of doing things is broker when you use the -native suffix tagged image as there is no microcks-uber-async-minion:nightly-native, for example. Just a microcks-uber-async-minion:nightly.

Description

While we don't have a -native image for async minion, we should also take care of removing the specific -native suffix from image tag.

Implementation ideas

No response

Retrofit to Java 8

Reason/Context

As of today, this module is compiled using Java 11 compatibility level. However, Java 8 is still strong and TestContainers official modules are required to be compatible with the Java 8 bytecode.

Description

We have to remove Java 11-specific code features (typically the new HttpClient) and port them to existing ones in Java 8. We also have to change the Maven compilation options for this module.

Implementation ideas

No response

Make TestRequest.Builder more fluent

Reason/Context

As of today, we only have basic types as signature within the TestRequest.Builder utility:

  • timeout is a long,
  • runnerType is a string

Those are not very readable and we have to describe things like this:

TestRequest otherTestRequestDTO = new TestRequest.Builder()
            .serviceId("Pastry orders API:0.1.0")
            .runnerType(TestRunnerType.ASYNC_API_SCHEMA.name())
            .testEndpoint("kafka://kafka:19092/pastry-orders")
            .timeout(3000L)
            .build();

Description

It would be nice to be able to support explicit types like Duration and enumeration like TestRunnerType.
That way, we will be able to write things like:

TestRequest otherTestRequestDTO = new TestRequest.Builder()
            .serviceId("Pastry orders API:0.1.0")
            .runnerType(TestRunnerType.ASYNC_API_SCHEMA)
            .testEndpoint("kafka://kafka:19092/pastry-orders")
            .timeout(Duration.ofSeconds(3))
            .build();

Implementation ideas

No response

Provide a fallback method for importing artifacts when you don't have access to MicrocksContainer instance

Reason/Context

As working on the integration of Microcks as a DevService for Quarkus, it appears that it's challenging to have access to the MicrocksContainer devservice instance. This instance was initialized during the augmentation phase and - for the moment - I don't find any way of injecting it into the CDI context for later consumption by the hot replacer service.

Description

As the Quarkus components have access to the URL where the container is running that way:

@ConfigProperty(name= "quarkus.microcks.default")
String microcksContainerUrl;

we could propose a fallback where a static method of MicrocksContainer is used with an extra argument that is actually this microcksContainerUrl which is the Http endpoint.

Implementation ideas

No response

Allow adding container dependencies for Async minion container from ensemblse

Reason/Context

When using the Async mocking feature, the Async minion container must connect to an external broker. If this broker is within another Testcontainer, it may not be started and ready when Async minion tries connecting. Most of the time, this is not a problem as the scheduling will try to reconnect a few seconds later.

However, in some situations - like Kafka, for example - we may face DNS resolution/caching strategies that prevent resolving the host if not present at first resolution.

Description

We need a way to configure dependencies on startup. This is possible on GenericContainer instances but not directly on a MicrocksContainersEnsemble as it just implements Startable.

It is still possible to do this in a second time by using:

ensemble.getAsyncMinionContainer().dependsOn(kafkaContainer);

but it may not be easy to use if you're using the fluent .with().with().with() API style ๐Ÿ˜‰

Implementation ideas

We need a withAsyncDependsOn() method at the ensemble level to do the trick.

Ease Developer eXperience ๐Ÿ‘ฉโ€๐Ÿ’ป

Reason/Context

We should make usage of the API the easiest possible

Description

Implement a bunch of changes:

  • Downgrade to Java 11 instead of Java 17 to avoid people being not able to use it cause of old JDK ;-)
  • Shade the domain model lib into our own package to avoid pulling too many dependencies
  • Add an extra constructor for the container with just a String
  • Split the method to get mock endpoints into many ones (one per protocol)
  • Complete unit test and README

Implementation ideas

No response

Allow AsyncAPI mocking and contract-testing support

Reason/Context

As of today (version 0.1.4 of this library) we just support essential features of Microcks provided by the main Microcks container. The list of supported features is the following:

  • Mocking of REST APIs using different kinds of artifacts,
  • Contract-testing of REST APIs using OPEN_API_SCHEMA runner/strategy,
  • Mocking and contract-testing of SOAP WebServices,
  • Mocking and contract-testing of GraphQL APIs,
  • Mocking and contract-testing of gRPC APIs.

Asynchronous API mocking and ASYNC_API_SCHEMA contract-testing strategy are not supported at the moment as it would require an additional microcks-async-minion container as well as a Kafka broker to ensure communication with the main container.

Description

We'd like to extend the notion of MicrocksContainersEnsemblewe described in #24 to also allow the usage of Microcks asynchronous features.

Implementation ideas

Ideally, we should be able to manage 2 different modes:

  • the one we're providing a Kafka broker because the user doesn't care (typically, we'll use a RedPanda testcontainer here)
  • the one we're reusing a Kafka broker that the user has initialized. In that case, we may want/need to reuse or share the same docker network and also have loose-coupling just reusing a broker bootstrap URL to connect everything

Make tests more robust switching from Redpanda to vanilla Kafka

Reason/Context

We observe some inconsistencies while using Redpanda broker as the 1st poll loop on consumer is sometime too long/longer than provided timeout. This makes the Microcks Async minion tests fail as they do not find any messages to consume.

Description

Replace the RedpandaContainer by KafkaContainer implementation in unit tests.

Implementation ideas

No response

Allow Postman contract-testing support

Reason/Context

As of today (version 0.1.4 of this library) we just support essential features of Microcks provided by the main Microcks container. The list of supported features is the following:

  • Mocking of REST APIs using different kinds of artifacts,
  • Contract-testing of REST APIs using OPEN_API_SCHEMA runner/strategy,
  • Mocking and contract-testing of SOAP WebServices,
  • Mocking and contract-testing of GraphQL APIs,
  • Mocking and contract-testing of gRPC APIs.

POSTMAN contract-testing strategy is not supported at the moment as it requires an additional microcks-postman-runner container that must be linked to the main container.

This feature is required by some users (see #21) and would allow to implement Different levels of API contract testing in the Inner Loop with Testcontainers!

Description

We'd like to implement some kind of wrapper that allows starting/stopping the 2 containers together: the main MicrocksContainer but also the Postman runner container (a GenericContainer should be enough for that as we don't have to provide specific configuration methods).

Ideally, this should be transparent from the user point of view: just start/configure/stop an ensemble of Microcks related containers in a single operation (no manual configuration of each and every containers).

Implementation ideas

Different options to consider:

  • Use Docker-compose based test containers (by embedding a docker-compose file in our lib?)
  • Use LinkableContainer and reuse the same shared network
  • Create a simple Java wrapper but it must respond to Tetscontainers lifecycle events

Some additional requirements:

  • Managed containers should be configurable to access the host network
  • MicrocksContainer operations should remain accessible
  • Images names and versions must be configurable to a certain degree (notion of platform version to introduce here?)

Add Secret creation/import on Microcks container

Reason/Context

Now that we can access to brokers (see #27), we need to provide a way to provide authentication mechanisms. This is already handled in Microcks via the concept of Secrets.

Description

We must provide something like withSecret(secret) method on MicrocksContainer so that the secret will be created on container startup.

Implementation ideas

No response

Contract Tests - JsonMappingException when adding operations headers

Describe the bug

First of all, thank you for your great work about Microcks !

I am playing around with it to replace PACT testing that does not suit our needs.
I am trying to execute contract tests as defined in this example : https://github.com/microcks/microcks-testcontainers-java#launching-new-contract-tests and some of the operations required technical headers.

Therefore I have setup my test like this :

    @Test
    void validate_open_api_contract() throws Exception {
        Header header1= new Header();
        header1.setName("my-required-header-1");
        header1.setValues("value-1");
        Header header2 = new Header();
        header2 .setName("my-required-header-2");
        header2 .setValues("value-2");
        TestRequest testRequest = new TestRequest.Builder()
                .serviceId("some-api:1.0")
                .runnerType(TestRunnerType.OPEN_API_SCHEMA.name())
                .testEndpoint("http://host.testcontainers.internal:" + port + "/path")
                .filteredOperations(List.of("GET /resources}"))
                .operationsHeaders(Map.of("GET /resources", List.of(header1, header2)))
                .build();
        TestResult testResult = microcks.testEndpoint(testRequest);
        ObjectMapper mapper = new ObjectMapper().setSerializationInclusion(JsonInclude.Include.NON_NULL);
        System.out.println(mapper.writerWithDefaultPrettyPrinter().writeValueAsString(testResult));
        assertThat(testResult.isSuccess()).isTrue();
    }

When I execute, I got a org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonMappingException due to headers.

I am using io.github.microcks:microcks-testcontainers version 0.1.3.

Expected behavior

I expect the deserialization of the response to a TestResult works.

Actual behavior

When Microcks tries to deserialize the response to a TestResult, I got the following exception :

org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonMappingException: Can not deserialize instance of java.lang.String out of START_ARRAY token
 at [Source: {"id":"651ea7baa485fb277f71cddb","version":0,"testNumber":1,"testDate":1696507834409,"testedEndpoint":"http://host.testcontainers.internal:33907/path","serviceId":"651ea7b9a485fb277f71cdd2","timeout":10000,"elapsedTime":0,"success":false,"inProgress":true,"runnerType":"OPEN_API_SCHEMA","operationsHeaders":{"GET /resources}":[{"name":"my-required-header-1","values":["value-1"]},{"name":"my-required-header-2","values":["value-2"]}]},"testCaseResults":[{"success":false,"elapsedTime":-1,"operationName":"GET /resources}","testStepResults":[]}]}; line: 1, column: 380] (through reference chain: io.github.microcks.testcontainers.model.TestResult["operationsHeaders"]->io.github.microcks.testcontainers.model.OperationsHeaders["GET /resources}"]->java.util.HashSet[0]->io.github.microcks.testcontainers.model.Header["values"])

	at org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonMappingException.from(JsonMappingException.java:270)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.DeserializationContext.reportMappingException(DeserializationContext.java:1234)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.DeserializationContext.handleUnexpectedToken(DeserializationContext.java:1122)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.StringDeserializer._deserializeFromArray(StringDeserializer.java:91)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:41)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.StringDeserializer.deserialize(StringDeserializer.java:11)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:287)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:259)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:26)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.MapDeserializer._readAndBindStringKeyMap(MapDeserializer.java:517)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:362)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.std.MapDeserializer.deserialize(MapDeserializer.java:27)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:504)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.impl.MethodProperty.deserializeAndSet(MethodProperty.java:104)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.BeanDeserializer.vanillaDeserialize(BeanDeserializer.java:276)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:140)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:3798)
	at org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:2842)
	at io.github.microcks.testcontainers.MicrocksContainer.testEndpoint(MicrocksContainer.java:203)
	at io.github.microcks.testcontainers.MicrocksContainer.testEndpoint(MicrocksContainer.java:165)

This is because, the mapper tries to deserialize the array ["value-2"] to the field values : https://github.com/microcks/microcks-testcontainers-java/blob/main/src/main/java/io/github/microcks/testcontainers/model/Header.java#L42 which is a String.

As a quick workaround, I have created a custom deserializer but I though that Jackson was able to handle it by default...

package io.github.microcks.testcontainers.model;

import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonParser;
import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonProcessingException;
import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonToken;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.DeserializationContext;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.JsonDeserializer;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.annotation.JsonDeserialize;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class Header {

    private String name;
    private String values;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getValues() {
        return values;
    }

    @JsonDeserialize(using = ArrayToStringDeserializer.class)
    public void setValues(String values) {
        this.values = values;
    }
}

class ArrayToStringDeserializer extends JsonDeserializer<String> {

    @Override
    public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
        if (jsonParser.currentToken() == JsonToken.START_ARRAY) {
            List<String> values = new ArrayList<>();
            jsonParser.nextToken();
            while (jsonParser.hasCurrentToken() && jsonParser.currentToken() != JsonToken.END_ARRAY) {
                values.add(jsonParser.getValueAsString());
                jsonParser.nextToken();
            }
            return String.join(",", values);
        }
        return null;
    }
}

How to Reproduce?

No response

Microcks version or git rev

No response

Install method (docker-compose, helm chart, operator, docker-desktop extension,...)

No response

Additional information

No response

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.