Yandex Taxi Testsuite

repository·develop·Indexed 41 days ago

https://github.com/yandex/yandex-taxi-testsuite

A Python-based, microservice-oriented testing framework built on pytest. It facilitates black-box testing of microservices by managing service environments, mocking dependencies, and providing direct database access for precondition setup and result verification. Includes plugins for ClickHouse, Kafka, MongoDB, MySQL, and PostgreSQL.

Tokens
20.6K
Snippets
68
Records
116
Agent score
51%

What's inside Yandex Taxi Testsuite

  1. Explore the Chat Backend Example

    develop

    The Chat Backend example demonstrates how to test a service where message storage is delegated to an external service. The example is composed of three main parts:

    1. Chat backend service: The core service implementation (server.py).
    2. Conftest: The test configuration and fixtures (tests/conftest.py).
    3. Test: The actual test cases for the service (tests/test_service.py).
  2. Pretty-print service logs with the Service Logs Formatter plugin

    develop

    The testsuite.plugins.servicelogs plugin provides functionality to pretty-print service logs (including colorization and simplification).

    Key behaviors:

    • On Failure: It reads logfiles only when a testcase fails, ensuring no extra overhead during successful runs.
    • Live Logs: If pytest is started with the -s option, logs are printed via a special thread to support live viewing.
  3. What is Testpoint and how does it work

    develop

    Testpoint is a mechanism that allows a service under test to call code within a testcase. It is implemented as a mockserver handler bound to the mockserver/testpoint path.

    Common use cases include:

    • Synchronizing states between the testcase and the service.
    • Reproducing race conditions.
    • Passing data bidirectionally between the service and the testcase.
    • Error injection.

    To use Testpoint, the service must have built-in support. The service communicates with the testcase via a simple HTTP POST interface at the /testpoint endpoint using JSON payloads.

    ## HTTP Interface Example
    
    POST /testpoint HTTP/1.0
    Host: mockerver
    Content-Type: application/json
    
    {"name": "foo", "data": {"foo": "bar"}}
  4. How static file lookup works in Testsuite

    develop

    Testsuite provides mechanisms to locate static files associated with a test case. When requesting a file, the system searches through a hierarchy of directories relative to the current test file. If a file is not found in any of these locations, a FileNotFoundError is raised.

    If your test is located in tests/test_foo.py, the lookup order for a file named filename.txt is:

    1. Per case: tests/static/test_foo/test_case_name/filename.txt
    2. Per file: tests/static/test_foo/filename.txt
    3. Per package default/: tests/static/default/filename.txt
    4. Static directory: tests/static/filename.txt
    5. Initial data dirs: Paths returned by initial_data_path()
  5. Perform inexact pattern matching with testsuite.matching

    develop

    The testsuite.matching module provides utility objects for inexact pattern matching. Instead of comparing objects against exact values, you can use special matching objects in assertions. This is particularly useful when testing API responses where certain fields (like IDs or timestamps) are dynamic and cannot be predicted exactly.

    from testsuite import matching
    
    def test_order_create(...):
        response = await client.post('/order/create')
        assert response.status_code == 200
    
        # Use matching objects for dynamic fields
        assert response.json() == {
            'order_id': matching.uuid_string,
            ...
        }
  6. Handle timezones with mocked_time

    develop

    The mocked_time system operates with the following timezone rules:

    • Storage: All mocked time is stored as offset-naive datetime objects in the UTC timezone.
    • Setting Time: mocked_time.set(...) treats offset-naive datetimes as UTC. If you provide an offset-aware datetime, it is automatically converted to an offset-naive UTC datetime.
    • Retrieving Time: mocked_time.now() returns an offset-naive datetime by default. To get an offset-aware datetime, pass the tz argument to mocked_time.now(tz=...).
  7. Understand the Chat Application example structure

    develop

    The examples demonstrate a simple chat application architecture to show how testsuite can be used to test microservices and their interactions with storage layers.

    Architecture:

    1. Frontend: chat-backend/chat.html (interacts with the backend).
    2. Backend: chat-backend (a Python HTTP service).
    3. Storage: The backend communicates with one of several implemented storage services:
      • chat-storage-mongo (MongoDB)
      • chat-storage-mysql (MySQL)
      • chat-storage-postgres (PostgreSQL)

    Directory Pattern: Each example follows this structure:

    • chat-xxx/server.py: The microservice implementation.
    • chat-xxx/tests: The testsuite tests for that specific component.
  8. How OpenTracing prevents background task interference

    develop

    In a testsuite environment, services often start once for the entire session. If a test starts a background task that makes HTTP calls after the test finishes, those calls might hit the mockserver during a different test, causing that second test to fail because the request is unmocked.

    To solve this, enable OpenTracing-like behavior in pytest.ini using mockserver-tracing-enabled = true.

    When enabled:

    1. The mockserver uses the mockserver-trace-id-header to identify which test initiated a request.
    2. If an unhandled request arrives with a trace-id belonging to a completed test, the mockserver treats it as a 'no-fail' request (returns 500) so the current running test is not interrupted.
    3. If the trace-id belongs to the current test, it fails as usual.
  9. How database management works in Testsuite

    develop

    Testsuite uses database plugins that can manage the lifecycle of database processes (starting and stopping them). By default, Testsuite ensures a clean testing environment by clearing the database and applying data fixtures before every test run. This guarantees the database is always in a known state.

    If you do not want Testsuite to manage the lifecycle, you can configure it to connect to an existing database instance instead.

  10. How Yandex Taxi Testsuite works

    develop

    Testsuite is a microservice-oriented testing framework based on pytest. It is designed for black-box testing of microservices (written in C++ or Python) via HTTP calls, while allowing direct database access for setup and assertions.

    Core Lifecycle:

    1. Environment Setup: Testsuite starts required databases (e.g., PostgreSQL, MongoDB, Redis).
    2. Data Seeding: Before each test, the framework fills the databases with necessary test data.
    3. Mocking: Testsuite starts a mockserver to mimic other microservices in the ecosystem.
    4. Service Execution: The service under test is started in a separate process.
    5. Test Execution: Tests perform HTTP requests to the service and verify outcomes.

    Verification Capabilities:

    • HTTP Assertions: Verify that requests were processed correctly.
    • Database Assertions: Check the service's database directly to verify state changes.
    • External Call Verification: Check if the service made calls to external services, including the order of calls and the data payload.
    • Testpoint Mechanism: Check the internal state of the service via data sent back to the test.