Tavern

repository·master·Indexed 22 days ago

https://github.com/taverntesting/tavern

A pytest plugin, command-line tool, and Python library for automated API testing using a concise YAML-based syntax. Tavern supports RESTful APIs, MQTT, and gRPC services, and allows for the creation of custom backend plugins. It features support for GraphQL operations, OpenAPI specification-based test generation, and variable extraction using JMESPath for complex multi-stage server workflows.

Tokens
33.7K
Snippets
111
Records
131
Agent score
75%

What's inside tavern

  1. Use Pytest marks to filter or skip tests

    master

    Since version 0.11.0, you can use marks in your Tavern YAML files to interact with Pytest. Marks allow you to:

    • Filter tests via CLI: Select specific tests to run using py.test -m "<mark_name>" or exclude them using py.test -m "not <mark_name>".
    • Skip tests: Use the skip mark to always skip a test.
    • Mark expected failures: Use xfail to mark a test that is expected to fail (e.g., a known bug or a temporary issue).

    Marks are applied to the entire test, not to individual stages (except for the skip keyword, which can be applied to stages).

    test_name: Get server info from slow endpoint
    marks:
      - slow
    
    stages:
      - name: Get info
        request:
          url: "{host}/get-info-slow"
          method: GET
  2. Understand Strict Key Checking in Tavern

    master

    'Strict' key checking determines whether extra keys in a response should be ignored or cause an error.

    • Enabled (strict: on): All keys in dictionaries at all levels must match the expected response exactly. Extra keys will cause a failure.
    • Disabled (strict: off): Extra keys in the response are ignored as long as the keys specified in your test are present.

    Default Behavior:

    • JSON body: Enabled by default.
    • Headers: Disabled by default.
    • Redirect query parameters: Disabled by default.

    List Behavior: When strict is off, extra items in lists are ignored. However, items must still appear in the specified order. To match list items in any order, use the list_any_order setting within the json key of a request.

    # Example of strict vs non-strict behavior
    # If response is: { "first": 1, "second": { "nested": 2, "extra": 3 } }
    
    # This FAILS if strict is ON because of 'extra'
    response:
      json:
        first: 1
        second:
          nested: 2
    
    # This PASSES if strict is OFF
    response:
      json:
        first: 1
        second:
          nested: 2
  3. Prevent string formatting using the !raw tag

    master

    Because curly braces {} are automatically interpreted for string formatting in Tavern, sending literal braces in a string can cause errors or unexpected behavior. To send a string containing literal braces without triggering the formatter, use the !raw tag. This tag effectively escapes the braces (e.g., converting { to {{) so they are treated as plain text.

    request:
      json:
        # Sent as {"raw_braces": "{not_escaped}"}
        raw_braces: !raw "{not_escaped}"
  4. Extend objects using YAML merge keys

    master

    You can use YAML anchors to partially reuse a request and then override or extend specific fields using the merge key (<<: *anchor_name). This is useful when a request is mostly identical to another but requires a different URL, method, or expected response.

    ---
    # Define a base request
    - &base_request
      url: http://api.example.com/user
      method: GET
      headers:
        Authorization: "Bearer {token}"
    
    # Reuse and extend the base request
    - name: Get User Profile
      request:
        <<: *base_request
        url: http://api.example.com/profile  # Overwrites the URL
      response:
        status_code: 200
  5. How Tavern plugins work

    master

    Tavern uses a plugin system to define how requests are made and how responses are verified. This allows you to override default behaviors (like using requests for HTTP) to test against local servers (e.g., using tavern-flask for Flask or tavern-fastapi for FastAPI) or use different protocols entirely.

    Core components of a plugin include:

    • Entry Points: Registered via setuptools to tell Tavern which module or class handles specific protocols (e.g., tavern_http or tavern_mqtt).
    • Session: A class managing the lifecycle of a connection (e.g., a requests.Session or an MQTT client).
    • Request: A class that encapsulates the logic for executing a specific request type.
    • Response Verifier: A class that inherits from tavern.response.base.BaseResponse to validate the results of a request.
  6. Reuse stages across tests using stage IDs

    master

    You can define reusable stages in external configuration files and reference them in your tests. This is ideal for common setup steps like authentication.

    1. Define the stage: In a configuration file, define a stage with a unique id.
    2. Reference the stage: In your test file, add a stage with type: ref and the corresponding id.

    When a stage is defined in a config file, it can include its own variables and use save to store data (like tokens) for subsequent stages in the test.

    # auth_stage.yaml
    ---
    name: Authentication stage
    variables:
      user:
        user: test-user
        pass: correct-password
    
    stages:
      - id: login_get_token
        name: Login and acquire token
        request:
          url: "{service:s}/login"
          json:
            user: "{user.user:s}"
            password: "{user.pass:s}"
          method: POST
        response:
          status_code: 200
          save:
            json:
              test_login_token: token
    
    # test_file.tavern.yaml
    ---
    test_name: Test authenticated /hello
    includes:
      - !include auth_stage.yaml
    
    stages:
      - type: ref
        id: login_get_token
      - name: Authenticated /hello
        request:
          url: "{service:s}/hello/Jim"
          method: GET
          headers:
            Authorization: "Bearer {test_login_token}"
        response:
          status_code: 200
  7. Load protobuf definitions in Tavern

    master

    Tavern supports three methods for loading protobuf definitions:

    1. Precompiled Python Modules: If you have existing Python gRPC stubs, point to the module path using grpc.proto.module. This is the most reliable method.
    2. Proto Files via Folder: Point to a directory containing .proto files using grpc.proto.source. Tavern will attempt to compile them using the protoc binary (or the binary defined in the PROTOC environment variable). Compiled files are stored in a proto folder relative to the Tavern YAML file. Warning: This requires protobuf>=5,<6 and a compatible protoc version.
    3. Server Reflection: If the server supports gRPC reflection, you can enable it in the grpc block. This is the least reliable method as it depends on server configuration.
    # Method 1: Using a Python module
    grpc:
      proto:
        module: server/helloworld_pb2_grpc
    
    # Method 2: Using a folder of .proto files
    grpc:
      proto:
        source: path/to/protos
    
    # Method 3: Using Server Reflection
    grpc:
      attempt_reflection: true
  8. Reuse requests and YAML fragments using Anchors

    master

    To avoid duplicating repetitive steps like authentication or common requests, you can use YAML anchors.

    In standard YAML, anchors (&name) are only valid within a single document (separated by ---). However, Tavern overrides this default behavior to allow anchors to be preserved and reused across different documents within the same file. This allows you to define a login stage once and reference it in multiple test documents.

    ---
    test_name: Test One
    stages:
      - &login_anchor
        name: Login
        request:
          url: http://api.example.com/login
          method: POST
        response:
          status_code: 200
    
    ---
    test_name: Test Two
    stages:
      - *login_anchor  # Reuses the entire login stage
      - name: Next Step
        request:
          url: http://api.example.com/data
          method: GET
  9. How to test gRPC services with Tavern

    master

    Tavern supports testing gRPC services using three distinct approaches for handling protobuf definitions. You can choose the method that best fits your workflow:

    1. Pre-compiled: Use a protobuf that has been compiled ahead of time. Tavern imports the generated Python module directly.
    2. Runtime compilation: Provide the .proto source file. Tavern compiles it at test time using the source key within a proto block.
    3. Server reflection: No local .proto files are required. Tavern discovers the service schema by querying the server's reflection API.
    # Example conceptual structure for the three approaches
    
    # 1. Pre-compiled (importing a module)
    # (Requires the generated module to be in the python path)
    
    # 2. Runtime compilation
    proto:
      source: path/to/helloworld_v2_compiled.proto
    
    # 3. Server reflection
    proto:
      reflection: true
  10. Create a custom backend plugin for Tavern

    master

    Tavern allows you to extend its functionality by implementing custom backends. A custom backend enables you to define new types of request and response handling logic that can be used within Tavern test stages.

    In a Tavern test stage, you can specify a custom request type (e.g., touch_file) and a custom response type (e.g., file_exists). The backend plugin maps these names to specific Python logic that executes the action and verifies the outcome.

    stages:
      - name: My custom stage
        custom_request_name:
          param: value
        custom_response_name:
          param: value
  11. Use extension functions for response validation

    master

    You can perform complex validations on responses using the $ext key. This allows you to call external Python functions (like tavern.helpers:validate_jwt) and pass them arguments via extra_kwargs. This is useful for verifying things like JWT signatures or complex data structures that standard JSON matching cannot handle.

        response:
          status_code: 200
          json:
            $ext:
              function: tavern.helpers:validate_jwt
              extra_kwargs:
                jwt_key: "token"
                key: CGQgaG7GYvTcpaQZqosLy4
                options:
                  verify_signature: true
                  verify_aud: false
  12. Use Tinctures to wrap stages or tests with Python logic

    master

    Tinctures allow you to run Python functions at specific points during a test or a stage. They are similar to external functions but can wrap the execution of a stage using a yield statement.

    • Test-level Tinctures: Run for every stage in the test.
    • Stage-level Tinctures: Run only for that specific stage.

    If a tincture uses yield, the code before yield runs before the stage, and the code after yield runs after. If you capture the result of the yield (e.g., (expected, response) = yield), you can introspect the response and compare it against the expected values.

    Refer to functions using the format path.to.package:function and pass arguments via extra_kwargs or extra_args in the YAML.

    --- 
    test_name: Test tincture
    
    tinctures:
      - function: package.helpers:time_request
    
    stages:
      - name: Make a request
        tinctures:
          - function: package.helpers:print_response
            extra_kwargs:
              extra_print: "blooble"
        request:
          url: "{host}/echo"
          method: POST
          json:
            value: "one"
    
      - name: Make another request
        request:
          url: "{host}/echo"
          method: POST
          json:
            value: "two"