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:

  1. command line variables override everything below
  2. newly registered variables override everything below
  3. test-local variables override everything below
  4. inventory variables override everything below
  5. 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.ymlCode 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.

By Val

Leave a Reply