
Introduction
I’ve been tasked with an interesting one this time. I’ve to create a BFF (Backend For Frontends) API. In this post, I’ll go through the BFF concept, BFF technical stack and also I’ll explain in detail how to create integration tests for that BFF API.
BFF (Backend for Frontends)
In a nutshell, BFF is built to allow client apps to have more friendly, flexible and manageable API to act as a proxy for the backend services.
In a microservices architecture that’s quite important as it is also used as a scoped schema for different devices or platforms. For example, we could create a BFF API for Web, Mobile and Voice because each of these need different data and combinations of queries from the microservices.
For example, an App could need interaction with different type of connectors (database, microservices, REST endpoints, external APIs, and so on)
1 | App |
Technical Stack
A BFF service can be done in hundred different ways, in my case I chose:
NodeJSbabelas ES6 compilerexpressas a webservermorganandwinstonas a logger toolshelmetas a Security middleware for expressgraphqlas SDL (Schema Definition Language)apollo-server-expressas a GraphQL Server middleware for expressnodemonas a dev server reload toolhuskyandlint-stagedas a git hook tooleslintas a linter tooljestas a testing frameworkcirclecias CI tool
Integration Tests
Integration Tests are really important for the QA of an API. Also, when working on microservices architecture, it is is even more necessary. Normally, when developing big applications it turns out we have 50 different microservices connected between them. As a Integration Test developers we have to make sure we only test our scope of the app (which is our current service).
In a nutshell, we have inputs and outputs in our BFF layer as well:
Inputs –> Service –> Outputs
Let’s ask some questions I had when started building the Integration Testing Workflow:
- How can we test our API in a isolated way?
- Using
docker, we can create isolated environment for our API under test and also for the test framework itself.
- Using
- How can we test our API using production-like environment?
- Using
docker, we can create a container with the same configuration as production usingNODE_ENV=production.
- Using
- How can we provide an easy and automated way to run our integration tests?
- Using
docker-compose, we can easily create a definition for our docker containers and run our integration tests quickly.
- Using
- How can we improve the manageability and speed on developing integration tests?
- Using
jestsnapshots to store the expected result (we save some time writing stubs here). So, we choosejestas a testing framework.
- Using
- How can we mock HTTP responses for the external services that our API calls?
- Using
mockttpwe can easily mock external services within our tests.
- Using
- When mocking external services HTTP responses, should we mock changing the behavior on the code or should we intercept HTTP responses from the external APIs?
- Intercept HTTP responses with
mockttp. Then we run the integration tests in a more similar to production environment. That means we test the whole workflow: graphql server, resolvers and data sources.
- Intercept HTTP responses with
- Are we going to use Cucumber and Gherkin?
Gherkinis high level language used on BDD (Behavior Driven Development) helping other stakeholders to define requirements. That is more used for end to end tests than integration tests.
- How many integration tests should we write when testing GraphQL Server?
- I’m still working on the best approach here, but some ideas:
- Create one file for each query defined on Query schema.
- Write tests for queries on all levels of the GraphQL Schema.
- Write tests for error queries which will result on a GraphQL Schema error.
- I’m still working on the best approach here, but some ideas:
- How can we mock the behavior of a Custom Database?
- If you require database queries, use
docker-composeto run your local database withseeds.
- If you require database queries, use
Integration Test Stack
NodeJSdockeras container servicedocker-composeas a admin tool for dockermockttpas HTTP mock server and proxyjestas testing framework (snapshots included)apollo-boostas apollo client
Requirements
- NodeJS version ^8.11.3
dockerdocker-composeschema version 3.6
Architecture
Integration Tests are going to run in docker containers in order to have similar to production environment in which to run tests.
1 | my-bff-service (container) |
Structure
1 | my-bff-service |
Running Tests: Development Mode
On development mode, we will need watch mode for test files and API under test. That means is not ideal to develop tests using the docker workflow because it would be so slow. We are going to use our local development server configured for integration tests + running our tests locally. After developing the tests, then we will test using the containers architecture.
There are two steps to run integration tests in development mode:
- Start BFF on Integration Test mode
1
2cd <root of the project>
npm run dev:integration-tester
Note: Running using dev config is going to restart the server is we do some changes.
- Run Integration Tests locally
1
2
3cd ./test/integration
npm install
npm run test -- --watch
Note: Running jest on watch mode is going to restart the test result each time we save changes.
Running Tests: Production-like Mode
That’s the moment of the truth. We will be running our tests inside of a docker container against our API under test which also is going to be inside of other docker container. Then we have isolated environments.
1 | cd <root of the project> |
OR
1 | cd ./test/integration |
Note: Run commands with sudo because docker-compose requires it.
Docker help
Build and Run docker-compose definition
1
sudo docker-compose up --build
Stop and Remove docker-compose containers, images, networks, …
1
sudo docker-compose down --rmi 'local'
Remove non-used docker images
1
sudo docker image prune -fa
See all docker images
1
sudo docker images -a
TODO
- Use snapstub to automate the creation of stubs for external services mocks
