Contract testing for Kafka event based services

Table of Contents

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?

The bigger picture

Contract tests sit between end-to-end and component tests.

Effective communication requirements

Focusing on ServiceB1 and ServiceA1 services.

  1. ServiceA1 can de-serialise the event/ bytes, serialised by ServiceB1
  2. The de-serialised event contains all necessary data for ServiceA1 to perform its business logic (has the proper semantics)

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 tests
  • Contract 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. status to be FULFILLED), but the producer never generates a FULFILLED event?

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 (c1 and o1) used are actually provided during runtime?
  • or even that the DB schema is 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