Microservice Tech Stack at Rungway - Part 3
By: Claudio Consolmagno
Tags: Selenium, Testing, Groovy, Spock Framework, Microservices

This is part 3, the final part of a blog post series related to the development of microservices at Rungway. In the first part we wrote an overview of the different technology choices in our microservice infrastructure. In the second post we wrote about choices in creating new services vs changing existing ones. In this third post we’ll be writing about our approach to testing our microservices.

Testing Approach

Following on from the previous section, testing is of extreme importance to us. Here at Rungway we take pride in our backend testing approach: for each REST endpoint in a microservice we implement these 5 types of testing at increasing levels of abstraction:

Unit Testing Integration Testing API Integration Testing End-to-end Integration Testing Pre-Production Manual Testing

Unit Testing (1) is the simplest type of testing where you cover a single function or small piece of functionality. No surprises here to any good developer. The interesting thing that we do here at Rungway is that, as part of the same microservice project we also do a type of Integration Testing (2). So we have Unit Testing under project/src/test and Integration Testing under project/src/integrationTest . In our integration tests we cover input-output of the REST endpoints themselves by fully bringing up the service and connecting it up with running docker instances of whatever integration that service needs, say MongoDB or RabbitMq. This is done with the help of the excellent Test Containers library.

Having the Integration Testing this way is much more powerful than using mock implementations as you run your tests against the same version of the integration you have in production and avoid issues related to mock implementations. You also catch this way certain bugs that could only happen on service start-up or at runtime (e.g. random Spring beans taking over the show…). The downside is that running individual integration tests can take a lot longer to run as compared to unit tests, so maybe 30 seconds as opposed to 3. But note that the primary delay is in the service and docker start-up, so once they do for the first integration test, the rest runs fairly quickly (provided you don’t dirty up Spring Context).

For API Integration Tests (3) we setup a separate project that is run periodically against our test environment. This level of integration testing lets us cover many test cases that would be impossible within the service itself as well as testing that the service is properly configured and running. We have separate test environment which we can test and assert many different scenarios and flows, for example login flow, new user (onboarding) flow, notifications flow, etc. To completely verify these flows work correctly and test their edge cases, several endpoints need to be called in different services and our api integration tests can do just that.

For Unit, Integration and API Integration testing our stack uses Groovy+Spock primarily. As already mentioned, we make use of TestContainers as part of Integration testing (but not API Integration Tests as it is unnecessary).

End-to-End Testing (4) is a separate layer where functionality is tested from the product’s front-ends (web, mobile apps) directly in another testing environment. This is done using Protractor (with Selenium) for Web and for Mobile we use WebDriver.io which drives the tests using Appium. Both of these are integrated into our CI pipeline.

The final layer of testing is the traditional Pre-Production Manual Testing (5), where QA would perform a battery of tests and regression tests against the product before giving a sign-off for release. This is usually done on a targeted basis so focus is given to whatever feature has been created or changed during the sprint and regression.

Photo by Markus Spiske on Unsplash

Testing Using Groovy and Spock

At Rungway we love Groovy+Spock! As mentioned in part 1, Groovy is a fairly lax language which we aren’t confident in writing business logic with, but are totally fine with as a testing language. It’s great to have a dynamically typed language for testing as it keeps tests concise and focused. Groovy also hides getters/setters and provides other features that make your testing code look very minimal. For example, Groovy ignores method and field visibility so you can access it directly and use it or test it as you like! In a perfect world you wouldn’t need to do this but the world isn’t perfect and not all code is perfect so it’s good to have that flexibility.

Spock is a testing framework written in Groovy. It provides a long list of features that help you in making testing easy and nearly trivial. A 1 liner of Spock code (e.g. 1 * subscriber.receive("message") >> "ok" ) can easily translate to over 10 lines when written in other frameworks. And that’s not even the best Spock feature! That would be “where blocks”. Seriously, look up Spock and try it out, Spock has nearly everything you need to fully test your code. The only other features you may need are: Test Containers (already mentioned above), MockMvc (to call your tested endpoint in your integration test) and Wiremock (to stub external service calls in integration tests as well).

Testing Using Protractor and WebDriver.io

We won’t go into this here as our very next post will be on this topic. Stay tuned!

Lint and Static Code Analysis

Having a consistent code style helps in the maintenance and longevity of the code. When you work with many different services (and developers) you don’t want to have one style in one project and a different style in another. That however is easily fixed these days with linters. I won’t go into details here as it’s a fairly straight forward topic but these are the ones we use: Java (checkstyle), Kotlin (detekt), Groovy (codenarc). All our projects are forced to use the same configuration files in those tools. Similarly we use SpotBugs for static code analysis on Java and Kotlin. It is so easy to integrate these tools as part of CI that it wouldn’t make sense to not use them.

Closing Remarks

In the last blog posts we provided a high-level overview of technologies that we use when building microservices at Rungway. We haven’t touched much yet on front-end technologies and it would be interesting in following posts to drill down into certain technologies and choices we’ve made along the way so expect more content coming out from us in the near future!