Contract testing for Kafka event based services
Table of Contents
- Contract testing for Kafka event based services
- Table of Contents
- Key takeaway
- Why
- The bigger picture
- Effective communication requirements
- Show me the code
- What's next?
| Disclaimer |
|---|
| Opinionated content follows :-) |
Key takeaway
.. and target of this presentation is try to persuade you that:
Contracts tests are a promising and effective way to reduce end-to-end tests.
Why
.. reduce the number of end-to-end tests?
- Slow
- Brittle
- Expensive to set up and maintain
- We cannot do exhaustive testing
- Slow feedback
- No build time feedback
- Which team owns the end-to-end test?
- writing
- fixing
- maintaining
The bigger picture
Contract tests sit between end-to-end and component tests.
Effective communication requirements
Focusing on ServiceB1 and ServiceA1 services.
- ServiceA1 can de-serialise the event/ bytes, serialised by ServiceB1
- The de-serialised event contains all necessary data for ServiceA1 to perform its business logic (has the proper semantics)
- (1) and (2) though related, are not the same!
- A consumer able to de-serialise, does not mean it can deliver its business purpose
- Avro required fields capture what is required from an event modelling perspective, not the consumer
- One contract
per consumerorper consumer, per consumer use casewould be better?- Different consumers may require different fields
- Different consumers may require different values of the same Enum field
- Doing compatible avro schema changes usually leads to reducing avro required fields
Consumer can read producer data
The avro schemas used by the ServiceA1 Consumer and ServiceB1 Producer are compatible, so that ServiceA1 can de-serialise the bytes, serialised by ServiceB1.
Consumer can deliver business value
The de-serialised event contains all necessary data for ServiceA1 to perform its business logic (has the proper semantics).
This can be achieved in two ways:
Components testsContract tests
Component tests
Currently, we do a lot of them.
Consumer
Pros:
- Exist on the ServiceA1 (consumer) bitbucket repo
- Run as part of the build process
- Fast
- Stable
- Isolated
- Easy to set up
Cons:
- Test mocks the actual producer
- Stall mocks issues!
- ServiceB1 (i.e. actual runtime producer) moves to a new kafka-schemas version
- ServiceB1 service removes an avro optional field that is required from the ServiceA1 (i.e. consumer) perspective
Producer
Pros:
- Exist on the ServiceB1 (producer) bitbucket repo
- Run as part of the build process
- Fast
- Stable
- Isolated
- Easy to set up
Cons:
- How does the producer know that it emits events that are actually what the consumer expects? I does not.
- ServiceA1 requires a particular value of an enum field (e.g.
statusto beFULFILLED), but the producer never generates aFULFILLEDevent?
- ServiceA1 requires a particular value of an enum field (e.g.
Contract tests to the rescue
Consumer
Pros:
- Exist on the ServiceB1 (producer) bitbucket repo
- Run as part of the build process
- Fast
- Stable
- Isolated
- Easy to set up
and an extra one:
- No stall mocks issue!
Producer
Pros:
- Exist on the ServiceB1 (producer) bitbucket repo
- Run as part of the build process
- Fast
- Stable
- Isolated
- Easy to set up
2 extras:
- If producer by mistake stops following the contract producer build fails!
- Meaning, the producer emits events that the consumers actually expect
Triggering the producer
In our use case we have ServiceB1 querying the ServiceB2 DB emitting appropriate data as avro events to the downstream consumers.
How can we trigger the producer to emit the proper event, so that we verify it against its contract?
Generate ServiceB1 Producer input data as part of the contract test
Do we have a guarantee:
- that the input data (
c1ando1) used are actually provided during runtime? - or even that the
DB schemais correct?
No! We have to add an extra contract test!
Show me the code
Open Intellij..
What's next?
- Should the producer have a contract per consumer's use case scenario?
- The consuming service uses the sample event as is for testing these use cases
- Maybe contract tests should focus on verifying just event structure
- Is there any benefit of using avro, regarding schema evolution, if you do contract testing?
- What about non JVM producers/ consumer?
- What about non Java producers?
- UI is a Nodejs REST consumer