What is Catcher?
It is an end-to-end test tool, specially designed to test systems containing many components. Initially developed as end-to-end microservices test tool it perfectly fits needs of data pipeline testing. Here we describe basic Catcher concepts.
Catcher is a modular system. It has core modules which are shipped with Catcher itself and additional modules which can be installed separately on demand.
Steps
In Catcher terminology module and step is the same.
Every Catcher test can have next types of steps:
- built-in core Catcher steps. They include basic steps like checks, sh, loops, http, wait and others;
- extended Catcher steps. They have more complex implementation and sometimes require dependencies or drivers to be installed in the system. For example: kafka, elastic, docker, mongo, s3 and others.
- your custom steps, written in Python, Java, sh or any other language you prefer.
Simple example of how test can look like:
steps: - http: # load answers via post body post: url: 'http://127.0.0.1:8080/load' body_from_file: "data/answers.json" - elastic: # check in the logs that answers were loaded search: url: 'http://127.0.0.1:9200' index: test query: match: {payload : "answers loaded successfully"}
Variables
Steps with hard-coded values are not that useful. Variables is one of Catcher’s key features. Jinja2 templates are fully supported. Try to use as much variables as you can to avoid code duplication and make things flexible.
Test-local variables
Every step can use existing variables by defining them in the variables
section. They can be either static (as token
) or computed (as user_email
).
variables: user_email: '{{ random("email") }}' token: 'my_secret_token' steps: - http: post: headers: {Content-Type: 'application/json', Authorization: '{{ token }}'} url: 'http://127.0.0.1:8080?user={{ user_email }}' body: {'foo': bar}
Every step can also register it’s output (or part of it) as a new variable:
variables: user_email: '{{ random("email") }}' token: 'my_secret_token' steps: - mongo: # search MongoDb for user request: conf: database: test username: test password: test host: localhost port: 27017 collection: 'users' find_one: {'user_id': '{{ user_email }}'} register: {found_user: '{{ OUTPUT }}'} - check: # check token was saved equals: {the: '{{ found_user["token"] }}', is: "{{ token }}"}
Let’s take closer look at this line: register: {found_user: '{{ OUTPUT }}'}
. Here OUTPUT
system variable stores mongo step’s output. We register it as a new variable found_user
.
OUTPUT is the system variable which is being used to store every step’s output.
System variables
Catcher can also access your system environment variables. F.e. run export MY_ENV_HOST=localhost && catcher my_test.yml
and your environment variable will be picked up. You can disable this behavior by running Catcher with -s flag: catcher -s false my_test.yml
.
steps: - http: # load answers via post body post: url: 'http://{{ MY_ENV_HOST }}/load'
To override variables use -e param while running tests:
catcher -e var=value -e other_var=other_value tests/
Code language: JavaScript (javascript)
Override priority is:
- command line variables override everything below
- newly registered variables override everything below
- test-local variables override everything below
- inventory variables override everything below
- environment variables override nothing
Inventories
As you may notice hard-coding services configuration either in the test or in test’s variables is not that flexible, as they are different for every environment. Catcher uses inventory files for environment-specific configuration. Inventories are stored in the inventory
folder. They also support templates.
You can create inventory file: local.yml
and set variables there:
airflow_web: 'http://127.0.0.1:8080' s3_config: url: http://127.0.0.1:9001 key_id: minio secret_key: minio123
And create develop_aws.yml
with variables:
airflow_web: 'http://my_airflow:8080' s3_config: key_id: my_real_aws_key_id secret_key: my_real_aws_secrey
When you run Catcher you can specify inventory via -i param:
catcher -i inventory/local.yml test
Reports
Sometimes, when you write your test, you need to see what is going on in your system after each step. At the moment Catcher supports only json output. Run it with -p json
option:
catcher -p json my_test.yml
Code language: CSS (css)
And check reports
directory for a json step-by-step report. It will contain all in and out variables for every step.
Installation
Docker
For a quick start you can use Catcher Docker image. It includes catcher-core, all extended modules, drivers and dependencies. It comes ready to use – all you need to do is to mount your local directories with your tests, inventories, etc.
The full command to run it is:
docker run -v $(pwd)/inventory:/opt/catcher/inventory -v $(pwd)/resources:/opt/catcher/resources -v $(pwd)/test:/opt/catcher/tests -v $(pwd)/steps:/opt/catcher/steps -v $(pwd)/reports:/opt/catcher/reports catcher -i inventory/my_inventory.yml tests
Catcher in Docker is usually used in CI while developers prefer Catcher local installation for writing tests.
Local
For a local installation ensure you have Python 3.5+ version. You can use miniconda if you don’t have it.
Run pip install catcher
to install catcher-core. It will install the Catcher and basic steps. If you need any extended steps you need to install them separately. F.e. for kafka and postgres you have to run pip install catcher_modules[kafka,postgres]
Some extended steps also require drivers and system requirements to be installed first. F.e. libclntsh.dylib library for the Oracle database.