End-to-End testing with Wiremock
As a DevOps team you are responsible for all apps and services you create and maintain. This responsibility should also be reflected in your testing strategy. In a (micro)services architecture you will likely have a chain of services before an external resource is called, this chain should be tested. One way of doing this is by implementing End-to-End (E2E) tests.
There are different views and opinions about E2E-testing and what it entails. I think about E2E as a type of testing that should give you enough confidence to merge a change that has an impact on two or more projects (frontend and/or backend) to the master/main branch. I deliberately say two or more, because if a change only has an impact on one project you should use unit tests or integration tests (integration in the way of multiple classes/components, not multiple projects). Another way to define E2E is to only test the UI and no services at all, this is not the type of E2E I am talking about here. My definition of E2E-tests could be described as E2E-integration tests, because you test the UI and all services you own. That way you integrate all projects you are responsible for in one testsuite by testing the flow from the UI starting point to the last service you own.
For me an E2E-test starts in the browser by, for example, filling in a form and clicking on a button. After that all services you, as a team, maintain and manage are actually called. This way you test that the frontend is able to handle the events, performs a service-call and is able to process the response. In the meantime all services are actually called and are also tested to be able to process incoming calls. All services, resources and dependencies you are not responsible for should be mocked, because in E2E-tests you want reliable tests you have all control over. There are several ways to implement this, I will show one way to do so. Maybe you can pick and mix some of this architecture to use at your own project.
At the project I am working on we deploy our apps and services on OpenShift. We have quite a lot of freedom on the platform. Because of that we can create a complete new test environment (an Openshift Project with a unique name) with all apps and services that are applicable for a single E2E-testsuite. This environment is automatically removed after the tests are done to free resources.
All deployed apps and services have configurable URL’s for all external resources and are configured build-time in Jenkins (flexible environment settings). This way an app or service can reference a service with the unique generated Openshift Project route name.
The starting point is a separate E2E-project that holds all Cucumber scripts, Selenium, BrowserStack configuration and Wiremock configuration with all mock responses from external services. You could also use JUnit instead of Cucumber and Selenide instead of Selenium, as we did in our project.
You could integrate this E2E-project code inside the UI app repository or maybe even as a mono-repo, because in my opinion there is a one-to-one relationship between the UI and the E2E-tests. But historically we have a seperate E2E-project repository.
For an E2E-test we start by filling the Wiremock instance with mock responses for all external services that will be called in the test. Wiremock holds all mocks and is highly configurable with (partial) url and query-string matching for different use-cases. Now Selenide opens a browser in BrowserStack and starts our application and a form is filled. When the form is submitted “Service 1” is called, this service uses a database. This database is newly deployed on this environment and filled for E2E-tests. Of course the database is (also in production) owned by the team. If it were not, you should think of a way to mock the database. Next in line is “Service 2”, this service is called by “Service 1”. Where, in production, “Service 2” would call some external services it’s URL’s now point to the Wiremock instance and receives the responses it should. The data drills down to the UI and the E2E-test verifies that the shown information in the browser is as expected.
Now we have hit all the parts we, as a team, are responsible for.
When there is a change in one of the repositories you own and that change is pushed, you can automatically run the E2E-tests. This way you can build the confidence that your change will not break any other app or service. The described architecture is an effective way to test all your responsibilities and be (more) confident to deploy your changes to production.
When working in the frontend it is hard to wrap your head around the actual response that should be created in Wiremock. This could be the response for a 2nd or 3rd service and would, most likely, not look like anything you would expect from the frontend side of view. It can also be hard if a service calls multiple other services for 1 response and you have to create multiple mocks in Wiremock that are compatible with each other.
You should be aware that E2E will not prove for sure whether or not your change is safe. In the testing pyramid E2E-tests are at the top of the pyramid. That means that they are hard, and thus expensive, to maintain and expensive (in time) to run. I would suggest to test at least a happy flow and maybe a couple of other flows. But there is no way you can test all permutations, that is not the goal of an E2E-testsuite. You do not want to test all business logic, there are other tests on other levels for that.
Alternatives or other techniques you could use
I have described one way to implement your E2E-tests. Apart from that, of course, there are other alternatives and techniques to implement tests. Some of those may have other goals on another level. For example it might be cheaper to test more permutations. Some other possibilities are:
- Testcontainers (https://www.testcontainers.org)
- Contract Testing
- Consumer Driven Contract
- Provider Driven Contract