Good day! I would like to present a new tool for end-to-end testing of microservices - Catcher
Why do you need e2e testing? Martin Fowler recommends avoiding it in favor of simpler tests.
However, the higher the tests are - the less rewritten. Unit tests are almost completely rewritten. Functional tests also have to waste your time in the event of a serious refactoring. End-to-end tests should check business logic, and it changes least of all.
Moreover, even full coverage of all microservices with tests does not guarantee their correct interaction. Developers may incorrectly implement the protocol (errors in the name / data type).
Or implement a new functionality relying on the data schema from the documentation, and on the product environment get a surprise in the form of schema mismatch errors: a mess in the data or someone forgot to update the data schema.
And the tests of each of the involved services will be green.
Really. At my previous place of work, it was decided that it was too long, difficult and expensive to spend time on deploying automatic tests. The system is not large (10-15 microservices with a common hitch). CTO decided that "tests are not important, the main thing is that the system worked." Tested manually on multiple environments.
What it looked like (general process):
And now a little tar in this barrel with chocolate: most of the tests needed to create users, because it is difficult to reuse existing ones.
Firstly, due to the fact that the system is distributed - several services had their own databases, which contained information about users.
Secondly, Kafka was used for permanent data storage. Those. even if the information is deleted / changed in the database, the service will still read it back on restart.
How did the registration of the new test user look (approximately):
Great, user is registered! Now a little more tar: for some tests you need more than 1 test user. And sometimes from the first time tests do not pass.
And how is the verification of the new functionality and confirmation from the business team?
All the same need to be repeated in the next environment.
Do I have to say that after a while you begin to feel like a monkey, which only does what presses the buttons, registering users.
Some other developers (usually the front-end) had problems connecting to the kafka. And with a bug in the terminal with a line of 80+ characters (not everyone knew about tmux).
Pros :
Cons :
If you read up to here nodding your head and saying: “yes, an excellent process, guys know what they are doing,” then you will not be interested further.
Self-made e2e tests are of two types and depend on which of the programmers was more free:
Sounds good. Problems?
Yes, such tests are written on what the person who writes them knows. Usually these are scripting languages ​​like Ruby or Python, which allow you to quickly and simply write this kind of thing. However, sometimes you can stumble upon a bunch of bash scripts, C or something more exotic (I spent a week rewriting the bike on bash scripts to a python, because the scripts were no longer extensible and no one really knew how they work or what they test) .
Sample project here
Pros :
Cons :
Of course, just look in the direction of BDD . There is Cucumber , there is Gauge .
In short, the developer describes the business scenario in a special language, then it implements the script steps in the code. The language is generally human readable and it is assumed that it will be read / written not only by developers, but also by project managers.
The scenarios along with the implementation of the steps are also in a separate project and run by third-party products (Cucumber / Gauge / ...).
The script looks like this:
Customer sign-up ================ * Go to sign up page Customer sign-up ---------------- tags: sign-up, customer * Sign up a new customer with name "John" email "jdoe@test.de" and "password" * Check if the sign up was successful
And implementation:
@Step("Sign up as <customer> with email <test@example.com> and <password>") public void signUp(String customer, String email, String password) { WebDriver webDriver = Driver.webDriver; WebElement form = webDriver.findElement(By.id("new_user")); form.findElement(By.name("user[username]")).sendKeys(customer); form.findElement(By.name("user[email]")).sendKeys(email); form.findElement(By.name("user[password]")).sendKeys(password); form.findElement(By.name("user[password_confirmation]")).sendKeys(password); form.findElement(By.name("commit")).click(); } @Step("Check if the sign up was successful") public void checkSignUpSuccessful() { WebDriver webDriver = Driver.webDriver; WebElement message = webDriver.findElements(By.className("message")); assertThat(message.getText(), is("You have been signed up successfully!")); }
Complete project here
Pros :
Cons :
Of course, to simplify the process.
The developer writes only scripts in json / yaml, and Catcher executes them. The script consists of sequentially executed steps, for example:
steps: - http: post: url: '127.0.0.1/save_data' body: {key: '1', data: 'foo'} - postgres: request: conf: 'dbname=test user=test host=localhost password=test' query: 'select * from test where id=1'
Catcher supports jinja2 patterns, so you can use variables instead of wired values ​​in the example above. Global variables can be stored in inventory files (as in an edible), pull up from the environment and register new ones:
variables: bonus: 5000 initial_value: 1000 steps: - http: post: url: '{{ user_service }}/sign_up' body: {username: 'test_user_{{ RANDOM_INT }}', data: 'stub'} register: {user_id: '{{ OUTPUT.uuid }}' - kafka: consume: server: '{{ kafka }}' topic: '{{ new_users_topic }}' where: equals: {the: '{{ MESSAGE.uuid }}', is: '{{ user_id }}'} register: {balance: '{{ OUTPUT.initial_balance }}'}
Additionally, you can run verification steps:
- check: # check user's initial balance equals: {the: '{{ balance }}', is: '{{ initial_value + bonus }}'}
You can also run some scripts from other scripts, which has a great effect on cleanliness and reuse of the code (including starting only parts of the steps through the tag system, deferred launch, etc. buns).
include: file: register_user.yaml as: sign_up steps: # .... some steps - run: include: sign_up # .... some steps
Inserting and using scripts can solve the problem of waiting for a resource (wait for the service while it starts).
In addition to ready-made built-in steps and an additional repository , it is possible to write your own modules on python (simply by inheriting ExternalStep ) or in any other language:
#!/bin/bash one=$(echo ${1} | jq -r '.add.the') two=$(echo ${1} | jq -r '.add.to') echo $((${one} + ${two}))
and use:
--- variables: one: 1 two: 2 steps: - math: add: {the: '{{ one }}', to: '{{ two }}'} register: {sum: '{{ OUTPUT }}'}
Scripts are placed in the docker file and run through CI.
This image can also be used in Marathon / K8s to test an existing environment. At the moment I am working on a backend (analogue of AnsibleTower) to make the testing process even easier and more convenient.
Pros :
Cons :
When I wrote this tool, I just wanted to reduce the time I usually spend on tests. It so happened that in every new company you have to write (or rewrite) such a system.
However, the tool turned out more flexible than I expected. If someone is interested in an article (or the tool itself), I can tell you how to use Catcher for organizing centralized migrations and updating microservice systems.
As I pointed out in the comments, the topic is not disclosed.
I will try to indicate here the most controversial theses.
tests
directory) and run every time the code changes to CI. And e2e tests are a separate project, they usually run longer, test the interaction of all participating services and do not know anything about the code of your project (black box).Source: https://habr.com/ru/post/435452/
All Articles