days
-6
-5
hours
0
-4
minutes
-3
-8
seconds
-5
-8
search
Microservices to the rescue

Microservices and test automation

Maxim Chernyak
© Shutterstock / SFIO CRACHO

Can we really bring microservices to QA and test automation? You’d be surprised. In this article, Maxim Chernyak explains how developers can integrate microservices into their automated testing procedures without too much trouble.

Microservices are a poster child for the thousands of organizations who want to build and deploy systems that are scalable, flexible, adaptable, and easy to develop. Fine-grained independent modular processes are key, according to the pundits, making all services easier to deploy and manage.

With all these benefits to boast about, you’d hope that at least some of them would spill over into the QA and testing function. The good news is that you’d be right to be cautiously optimistic – the bright new world of microservices testing is a mix of familiar and not-so-familiar challenges. However, the foundational principles of testing remain unchanged, and the test pyramid is still a broadly good way to visualize the big picture.

microservices

Martin Fowler’s test pyramid – adapted from an idea by Mike Cohn.

In this article, we examine what plot twists microservices inject into a typical testing workflow and offer some suggestions if you’re keen to automate as much testing as possible, which, of course, is a totally sane objective.

SEE ALSO: Testing trends in 2018: Value of automated testing, automation challenges and more

Spotting the difference

Being modular, microservices are independently deployable as small composable components. Because of this, they are also independently testable. However, there are some bear traps for the unwary as testing scales up through small, independent microservices to integration and end-to-end testing across the larger services ecosystem. Further, microservices are often integrated using REST over HTTP (returning JSON or XML documents, for example), which strongly influences our choices as we climb the test pyramid.

Unit testing

Unit tests are typically written at the level of the class or small collection of classes. The goal is to exercise the smallest pieces of software to validate that they behave as expected, before moving on to larger chunks of functionality. Being often numerous and internal to a microservice, unit tests are great candidates for automating and – even in a microservices context – can leverage any one of a number of existing unit-testing frameworks relevant to your language of choice.

It may not be right up to the minute, but you could do worse than check out this extensive Wikipedia-maintained list of unit-testing frameworks. What this page doesn’t show, of course, is how active their respective development teams are, or just how of the moment they are. If you’re coming at this with a blank slate, and this matters to you, some judicious reviews of their GitHub activity will give you a good indication, at least for the open-source frameworks.

SEE ALSO: Testing strategies and challenges – Dos and don’ts

Contract, the Service layer, and test automation

The notion of contract testing reframes some well-established testing principles in a microservices context and maps on to the Service layer of the test pyramid. A contract is simply a set of agreements between a consuming service and a providing service. Best practice is to define a set of well-written versioned contracts for each microservice – these represent a formal description of interaction details between services. Contract testing is, at least partially, a less expensive substitute for brittle, sometimes flaky integration tests.

The aspiration to test microservices independently of each other can give rise to the need to create mocks between consumer and providers. For example, a consumer service can be started up so that it sends some requests to a provider mock, which checks to see if those same requests are allowed as per the contract. Mountebank offers an alternative perspective – it’s an open-source tool providing cross-platform test doubles. It’s a little node.js software appliance and can be told to activate on a specific port in a specific role and provide mock-style responses, playing the part of many different downstream collaborator services.

Automation support is available with Pact, a contract testing tool, which supports a modern evolution of contract testing called consumer-driven contract testing. What this means is that the contract is written as part of the consumer tests. The Pact team claims that “A major advantage of this pattern is that only parts of the communication that are actually used by consumer(s) get tested. This in turn means that any provider behavior not used by current consumers is free to change without breaking tests.” Sounds reasonable!

SEE ALSO: “There’s an upcoming battle between what we see with microservices nowadays and the role of serverless”

Curious as to what Pact test code looks like?

# Describe the interaction
before do
  event_api.upon_receiving('A POST request with an event').
    with(method: :post, path: '/events', headers: {'Content-Type' => 'application/json'}, body: event_json).
    will_respond_with(status: 200, headers: {'Content-Type' => 'application/json'})
end
​
# Trigger the client code to generate the request and receive the response
it 'is successful' do
  expect(subject.save_event(event)).to be_true
end 

A good walk through (from where the above code originates) showing how Pact provides the tools to validate contracts is available here.

It is common to deploy multiple versions of a service API in parallel, with the version number appearing explicitly in endpoint addresses. Service layer testing is often complemented with API tests using Postman, or SoapUI, or a similar tool providing some further automation support.

Other efforts to introduce automation to the consumer-driven contract world include Spring Cloud Contract, which has a goal of enabling consumer-driven contract development of JVM-based applications, shipping with a contract definition language – a DSL – written in Groovy or YAML.

SEE ALSO: Testing Java Microservices: Not a big problem?

Integration testing

At around the 19 minute mark, Sam Newman of Thoughtworks talks about microservices testing in this Youtube video. He draws attention to the fact that with microservices, there are many, many independent services talking to many, many other independent services over the wire. He contends that the combinatorial expansion in the number of potential service-to-service interactions (either directly or indirectly) can spell failure for traditional end-to-end integration testing. The central problem, he claims, is one of too many moving parts:

  • Browsers – can crash for no reason while running tests
  • Timing effects can show a test as failed when it hasn’t
  • Provisioning environments can fail
  • Networks – may not be reliable
  • Deployment issues

The bottom line, he argues, is to exploit consumer-driven contract testing and really limit the volume of full integration tests to the absolute minimum. Maintain a focus on a small number of customer journeys through your system that mimic the key user flows. Some organizations eliminate these kinds of tests altogether.

He urges us to accept the plain truth that – at scale – in terms of load and moving parts, we are not going to catch all problems before our system goes into production. With microservices, there is a lot more potential for emergent behavior – and this emergent behavior will not appear in the test environment.

So, we must be prepared to monitor systems in production and fix issues as they happen. As we move from monolithic systems to finer grained services, we are likely to need to focus more on monitoring and alerting than testing. Why not run those customer journey tests in production? If you’re fascinated by this and other heresies, Sam’s book (2nd edition coming out in 2019) is a great place to start.

SEE ALSO: Monitoring microservices with health checks

Is it for me?

As a concept, the jury is still out on where microservices have a part to play in large scale systems development. Many authorities recommend taking the microservices route only after having established that your needs cannot be met by a (well designed) monolithic application.

Clearly, the microservice architectural pattern is still evolving, so are the patterns for testing in a microservices world. Despite the pace of change, tools for automated testing are reasonably plentiful and on-tap – either press-ganged from other testing domains or developed specifically to address the differences that microservices bring into the testing mix.

If your development organization is dipping its toes in the exhilarating waters of microservices, you could do a lot worse than explore how your testing needs can be met by the body of knowledge and tool support that is emerging across the industry.

 

Author

Maxim Chernyak

Maxim Chernyak is a Head of Test Automation and Performance Testing Lab at A1QA, an expert in test automation methodologies and tools for functional and nonfunctional testing. Accountable for the education and adoption of state-of-the-art quality engineering practices by QA teams