Functional tests in Cyan



Hello!



My name is Timofey, I am a Python developer in the Cyan Platform team. Our team is developing tools for product developers. These are libraries: HTTP client, web server, database access libraries, and monitoring tools for microservices and the site as a whole, and integration with CI / CD, and much more.



, — .



...





, , . , , , , , .



, - -. . , - . , , .



Code-coverage



. diff-coverage: pull-request , . — 80%, , pull-request diff-coverage . .



, , , . , , - : diff, :





API-, : dev, beta prod, .



-



coverage — -. ?



-, :



. , , , .



. 2 3 — . , , . , - “” .



- . -, . : — !





, . .



API . API Application Programming Interface, , HTTP API, RabbitMQ / Kafka .



, , HTTP Mock Server, , .





:



  • , API, RabbitMQ, .
  • , .
  • , black box, , .
  • , -.


, Python, C#, frontend NodeJS . Python pytest. Python- -, C#- API-. pytest , .



-?



! , :



  • , -. .
  • . - , — .
  • , -, CI-pipeline .
  • , , , , , .


, .



, API , — -.





, . , app.toml. , :



[[dependency]]
type = "postgres"
alias = "users"

[[dependency]]
type = "rabbitmq"


command line :



cian-functional-test-utils deps up


, docker-compose.yml docker-compose up -d. “” docker-compose, Elasticsearch, . , alias .



conftest.py :



@pytest.fixture(scope='session', autouse=True)
async def start(runner, pg):
    #           ,
    #          health-check.
    await runner.start_background_python_web()
    #       HTTP API,   RabbitMQ , 
    await runner.start_background_python_command('save-users-consumer')

@pytest.fixture(scope='session')
async def pg(postgres_service):
    db = await postgres_service.create_database_by_alias('users')
    #  `pathlib.Path`  .
    await db.execute_scripts(Path('database_schemas') / 'postgres.sql')
    return db


! :



async def test_v1_get_user(http, pg):  #   pg  conftest.py
    # arrange
    await pg.execute('INSERT INTO users (id, name) VALUES (1, "Bart")')
    # act
    response = await http.request('GET', '/v1/get-user/', params={'id': 1})
    # assert
    assert response.status == 200
    assert response.data == {'id': 1, 'name': 'Bart'}


PostgreSQL MsSQL, Cassandra, Redis, Elasticsearch.



HTTP API , :



async def test_save_users_consumer(pg, queue_service):
    # arrange
    #      RabbitMQ ,
    #  ,    .
    await queue_service.wait_consumer(queue='save-users')
    # act
    await queue_service.publish(
        exchange='users',
        routing_key='user.created',
        payload={'id':1, 'name': 'Bart'},
    )
    await asyncio.sleep(0.5)  #  ,    
    # assert
    row = await pg.fetchrow('SELECT name FROM users WHERE id = 1')
    assert row['name'] == 'Bart'


RabbitMQ ( ), . ? RabbitMQ .



, , , .



, . . - API . “” : , HTTP-, Management API. , API, . , .



HTTP-. .



HTTP



HTTP- mountebank. ( ) HTTP. , , :



@pytest.fixture(scope='session')
async def users_mock(http_mock_service):
    #     ,    ,
    #    URL    .
    return await http_mock_service.make_microservice_mock('users')

def test_something(users_mock):
    # arrange
    stub = await users_mock.add_stub(
        method='GET',
        path='/v1/get-user/',
        response=MockResponse(body={'firstName': 'Bart', 'lastName': 'Simpson'}),
    )
    # act
    # do something
    # assert
    # ,        ?userId=234
    request = (await stub.get_requests())[0]
    assert request.params['userId'] == '234'


stub, 404. mountebank . , , 404 , 404, , . , ( 404). , .



mountebank , . . :



  • recordRequests — , , ;
  • --debug — .




, session , HTTP-. start, , autouse=True. - , http-, .



, :



  • , ;
  • statsd graphite ;
  • RabbitMQ , .


, , , , .





, .



, Sphinx. reStructuredText , , , .



:



  • , ;
  • , C#-;
  • ;
  • API Reference, .


, , , .



Type annotations



Python – . , , — . PEP 484.



pytest PyCharm, IDE , :





pytest IDE Jetbrains Python Community Edition. C#- Rider — , IDE, .





, . , Open Source.



, , , :



  • ;
  • ;
  • ;
  • , , -.


- , , , , .



, , , .



.




All Articles