Does Spring Cloud Contract support serial dependencies between microservices? - spring-cloud-contract

Let's say that we have 3 Microservices A, B and C. We want to test a functionality of microservice A, which depends on microservice B. However, the response of microservice B also depends on the microservice C. Does Spring Cloud Contract support such cases? Because by reading the documentation I get that when a contract is created for the interaction between A-B (and inserted in B), the automatically generated test in B will fail since it waits a response from C in order to provide an answer. And even if we have another contract for the interaction B-C, this means that we have to modify the auto-generated test in B, so that the test is successful. Is there a way when creating a contract for A-B to specify that this also depends on the interaction B-C?

I think you are reading more into the documentation than is actually there. The contract is not asserted when you test the consumer, it just generates stub responses that match the contract. So, in your scenario, to test service A you only need stubs from service B. You don't really even need the contracts, and you don't need anything from service C. I wonder if you actually tried it and something didn't work?

Related

Is there a software engineering concept/pattern for "service injection"?

I'm implementing a service that needs to call another service that calculates a result in a way I cannot know.
Let's say I have the following scenario:
I have some place in my code, calling a HTTP request to a defined endpoint another service returning a defined result. Now, I mustn't dictate how the result will be calculated, however I can define the result output data type I'm expecting. I want to put emphasis on this, since otherwise I would just implement the calculation logic in my service.
I would then describe it to the user:
You need to provide an HTTP service, with this exact endpoint, receiving these exact parameters, delivering this exact result type, but how you calculate the result is your job. I just need the URL of your service.
Afterwards the user of my service would configure the URL to their HTTP service into my service, so that I can make a HTTP request to {url}/defined-endpoint.
I couldn't think of another name but "service-injection" to describe this concept, since it has a resemblance to dependency injection, just that in code you don't provide an object instance, but a service that is called via http.
My question is: Is there a pattern for this concept or an alternative that more elegantly solves the general problem of outsourcing a calculation to another service?
You are defining a contract of how the interface between your service and the other service. This means that as long as the contract is respected by both parties the integration and communication will succeed. Not sure if "service-injection" is a good terminology for this. You are not injecting something in your own service, you are simply delegating the calculation to another one, but you don't inject the logic of the service into your own. And that is good because then you have a good separation of concerns and loose coupling. As long as the contract is respected, both services can be changed in whatever way it is needed and the integration would still hold.
Is there a pattern for this concept or an alternative that more elegantly solves the general problem of outsourcing a calculation to another service?
This is just how things work in a micro-services ecosystem. You have multiple services exposing APIs that communicate with each other to provide as a whole a higher-order functionality.

Discussion: Should CDC based producer tests mock business logic or integrate with business logic [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
My question addresses a conceptual issue with consumer driven contracts (CDCs), not with Spring-Cloud-Contract in particular (though tagged otherwise).
A CDC usually guarantees a particular behavior of a service (producer/supplier) to its client (consumer). Many architectures (e.g. Hexagonal/Clean/Onion Architecture) have an adapter/layer to provide the technical interface(s) of the service (REST/SOAP/Messaging/...). They separate the adapter from the business logic. When tests are written based on a CDC it is possible to just test the adapter (classical unit test) and mock away the business logic.
However, the contract formally does not only specify a particular mapping of the result (from the business logic) to the data transfer object as returned by the adapter. Moreover it contains semantics wrt. to the request/response behavior: If the consumer sends a specific request with meaningful data, the producer should respond with a particular correct answer (with respect to the request).
My point for discussion/question is: Should the test on the producer side then be an integration test (integrating business logic) to check if the business logic will return the correct answer? In this case the next layer (behind business logic) might be mocked away as long as the business logic is an integral part of the test case.
In Spring CDCs (but I think in other CDC implementations as well) it is possible to distinguish between consumer and producer side and the generated test cases. For example it would be possible to reduce the producer part to check for formal correctness of the response (e.g., if a particular data item is contained and validates against a pattern) while it specifies a concrete response for the consumer part at the same time. This might lead to the (mis-) understanding on the consumer side (and backed by the consumer tests) that the concrete specified data would be returned by the producer if the consumer had sent the specified request from the CDC.
What do users of CDCs think? How do you specify behavior and to what extent do you test it on the producer side?
We describe it in the documentation - https://docs.spring.io/spring-cloud-contract/docs/2.2.5.RELEASE/reference/htmlsingle/#getting-started-introducing-spring-cloud-contract-purposes . Let me write it here for your convenience
Spring Cloud Contract’s purpose is NOT to start writing business
features in the contracts. Assume that we have a business use case of
fraud check. If a user can be a fraud for 100 different reasons, we
would assume that you would create two contracts, one for the positive
case and one for the negative case. Contract tests are used to test
contracts between applications and not to simulate full behavior.
If you check Pact's documentation (https://docs.pact.io/faq#do-i-still-need-end-to-end-tests) you'll find
Contract tests replace a certain class of system integration test (the
ones you do to make sure that you're using the API correctly and that
the API responds the way you expect). They don't replace the tests
that ensure that the core business logic of your services is working.
In case of Spring Cloud Contract - if you have a controller that delegates work to a service, then you should mock out the service and test only whether your controller can take a request and send back a proper response

Using pact for a service with a sequence of requests and responses

Here is my use case:
My Service is: ServiceA.
It depends on the following services: ServicesB and ServiceC.
ServiceA sends a POST request to ServiceB with some authentication details (username+password) and ServiceB replies back with a json document which has a sessionId.
Request:
POST /authenticate
{
"username": "_at_api",
"password": "xxx"
}
Response:
{
"sessionId": "axy235da7ad5a24abeb3e7fbb85d0ef45f"
}
The above sessionId is used for all api calls from ServiceA to ServiceC.
ServiceA requests to serviceC to start a job using a POST request and serviceC returns with a job id(alphanumeric).
Request:
POST /jobs/local/start
Header: Authentication: axy235da7ad5a24abeb3e7fbb85d0ef45f
{
...
}
Response:
{
"status": "RUNNING",
"jobId": "a209016e3fdf4425ea6e5846b8a46564abzt"
}
ServiceA keeps polling serviceC for the completion of the job using the jobId returned above:
Request:
GET /jobs/status/a209016e3fdf4425ea6e5846b8a46564abzt
Header: Authentication: axy235da7ad5a24abeb3e7fbb85d0ef45f
Response:
{
"status": "RUNNING"
}
The polling continues until the status is returned as COMPLETED or FAILED.
Response:
{
"status": "COMPLETED"
}
How can I use Pact to test serviceA?
My plan is to use only unit tests and contract tests to achieve code coverage of more than 90%. Is it a good idea, or do I need to have a separate tests using virtual servers? My understanding is that Pact is a superset of virtual server (example: mountebank) and everything which a virtual server can do, Pact can do. And so I do not need a separate Component testing. Also, it looks like Contract testing completely replaces end-to-end testing, so I do not need end-to-end testing as well. Is this right?
Also, it looks like Contract testing completely replaces end-to-end testing, so I do not need end-to-end testing as well. Is this right?
No. Contract testing is not functional testing (see this excellent article, with the same title)
What is contract testing?
Contract testing is about testing whether two components are able to communicate.
Consider a contract between a house and a postal worker: The postal worker needs to know that they can approach the house and deliver post (and, that sometimes they may be unable to do this - perhaps the mailbox is full).
From the postal worker's perspective, the contract looks like this:
Find postbox (with a success and fail case)
Deliver post to postbox (with a success and fail case)
Note that the postal worker doesn't know anything about the implementation of the postbox. Perhaps there are multiple reasons that delivering to the postbox might fail - maybe the door is jammed, maybe the box is full, maybe
the post is too big to fit in it.
In this hypothetical case, our postal worker doesn't do anything different in those cases - they just fail to deliver. So, from the perspective of the contract, the reason for the failure is irrelevant. The contract - that the worker can try to deliver post, and that they can be successful or unsuccessful - can be tested without enumerating all the possible reasons for failure.
See the article linked above for a more detailed example, but to quote from the end of it:
Contracts should be about catching:
bugs in the consumer
misunderstanding from the consumer about end-points or payload
breaking changes by the provider on end-points or payload
A really nice feature of Pact is that you can test multiple contracts against only the bits of communication that they rely on.
Note that the consumer contract tests only describe communication that the consumer needs to make or understand. A contract is not (necessarily) a full API description.
Ok, but why can't I use contract tests for end to end tests?
It's possible to use a tool like Pact to replace your end-to-end tests. However, although contract testing has a lot of similarities with the features you'd need for end-to-end testing, contract testing (and Pact in particular) isn't designed for end-to-end testing.
If you're doing end-to-end testing by extending an existing consumer's tests (say, adding all the possible reasons to failure to the postworker's tests), then it's no longer clear what the contract means. The contract now describes how the communication works along with the behaviour.
This will cause problems when you start adding more consumers (say, a parcel courier) - do you duplicate all of the failure cases in all the consumers, or do you just keep them in the original consumer tests? If you duplicate the tests, then you have a lot of things to change if you change the behaviour of the provider - and your tests will be brittle. If you don't duplicate the tests, then your end-to-end tests are stuck in one consumer - with all the problems of losing them if you decommission that consumer.
With pure contract tests, you (ideally) don't have to change anything if you're adding more possible reasons for failure that the consumers already understand.
There are many other reasons that you'll have headaches if you try this (your tests start relying heavily on exact data, and the meaning of failed verifications and can-i-deploy hooks would change if your tests are end-to-end tests), but the key takeaway is that Pact is not designed as a replacement for end-to-end testing. You can use it that way, but it's not advisable and is likely to lead to painful maintenance.
How can I use Pact to test serviceA?
You describe each request separately, using Pact provider state as the prerequisite for each request.
Additionally, you may find the question on PACT - Using provider state helpful.
Although this comes late, for everyone who stumbles across this:
The pact spec (v3.0) only supports a single request per interaction (but multiple interactions per consumer-provider-pair). On the provider side, every interaction results in an individual test. So while having a sequence or requests running on the provider side is not conceptually not a good idea, it also doesn't work technically.
On the consumer side though, you have an alternative - which I would encourage not to misuse for end-to-end tests. In my case, I had a class which implemented a template/strategy pattern which involved multiple requests and didn't allow injecting intermediate states on the consumer side. It required all requests to have valid responses.
In that case, pact-jvm-consumer-junit (=JUnit4) allows to specify multiple PactVerification annotations on a single test method (Kotlin in my case):
#Test
#PactVerifications(
value = [
PactVerification("Service B", fragment = "authenticateOk"),
PactVerification("Service C", fragment = "jobStartOk")
PactVerification("Service C", fragment = "jobStatusOk")
]
)
fun `test successful job execution`() {
// add your test here.
}
On the provider side, all fragments above are executed as individual tests, thus they need a proper state as specified in the answer above.
I got this approach running in JUnit4. Haven't found a quick way to mimic it JUnit5 though.

Who is the consumer in a POST scenario?

I have read that on Pact, the consumer is the one initiating the request. I have a service (let's call it A) that is used to draw pictures that will then be submitted (via POST) to a service (let's call it B) that will process those inputs and generate albums.
My question is: If the service B is the expert on knowing which kind of inputs and in which format should receive in order to create albums, how come is service A the consumer and therefore the one that will be writing the contract? Shouldn't it be the service B that specifies what kind of data should be receiving?
From [1]:
A component that initiates a HTTP request to another component (the service Provider). Note that this does not depend on the way the data flows - whether it is a GET or a PUT / POST / PATCH, the Consumer is the initiator of the HTTP request.
But I think your question is really asking about consumer driven contract testing [2]. You're conflating who is building the service with expertise / understanding of the requirements for its existence. The Consumer knows what it needs from the service, and the Provider knows how to implement it.
Consumer driven contracts reverses the typical API design process of creating an API first and then consumers coming along to use it. First, the consumer specifies what it requires, publishes the expectations as a contract and the Provider then implements it.
Pact generally assumes this mode, but it's not strictly necessary (i.e. the Provider API can exist in advance).
[1] https://docs.pact.io/documentation/how_does_pact_work.html
[2] https://martinfowler.com/articles/consumerDrivenContracts.html

pact HTTP message consumer

I have a relationship between two components/microservices where component A sends events by HTTP to component B. In a traditional pact HTTP consumer/provider pattern, A is the consumer of B since A is sending the request and B is responding. However, in this case, B is the real consumer of the events that A provides.
Is there a way of implementing the consumer/provider tests so that the consumer test can be written on the receiving side (B) rather than on the sending side?
I have seen that message pacts have been introduced which sounds like it could be used in this scenario but I haven't seen any easy to understand examples of how this is implemented and if it can be used in conjunction with HTTP like in my scenario.
I'm using pact-jvm-junit.
You've outlined to two senses in which the term consumer/provider can be used - the HTTP consumer/provider pair and the consumer/provider of the data itself.
Pact only uses the HTTP consumer/provider sense of the words, because you can't really set up the mock in reverse. You can still use Pact exactly the same way you would normally though - in fact, the first project that used Pact was one where the data flowed from the javascript client to the backend server.
Most HTTP consumer/provider pairs have bi-directional data flow anyway. It's a rare app that is read only. Rather than thinking of it as "how do I, as the consumer of the information, wish to receive the data", think of it as saying "how do I, as the sender of this data, wish to transfer it to the recipient?".

Resources