Pular para conteúdo

FastDepends

Algumas instruções rápidas de uso básico para lidar com o FastDepends e seus testes.

Vamos imaginar uma estrutura básica:

/app
└──app.py
/tests
├──conftest.py
└──test_app.py

O código fonte

Basicamente para que algo seja injetado, esse algo precisa ser um Callable. Esse Callable deve ser passado para outro Callable como valor default envolvido por Depends. Em conjunto a isso, quem receberá a injeção precisa ser decorado com @inject:

app/app.py
from fast_depends import Depends, inject


def dependencia():
    yield 1


@inject
def sut(var: int = Depends(dependencia)):
    """Função que recebe a injeção e tbm é alvo dos testes."""
    return var

Conftest

Para alterar o valor injetado durante os testes, que é o grande valor de injeção, podemos usar uma fixture única ou um grupo de fixtures.

Prefiro separar o provider em uma fixture específica, o que garante que ele sempre será limpo em relação ao escopo básico das fixtures, ou seja, por teste.

tests/conftest.py
import pytest
from fast_depends import dependency_provider

from app.app import dependencia


@pytest.fixture
def provider():
    """Forma rápida de garantir que sempre será limpo a cada teste."""
    yield dependency_provider
    dependency_provider.clear()

Depois, cada dependência pode ser sobreescrita por uma fixture diferente, garantindo que só quem precisar daquilo alterado use:

tests/conftest.py
@pytest.fixture
def override_de_dependencia(provider):
    """troca das dependências pelo que será usado nos testes."""

    def dependencia_test():
        yield 2

    provider.override(dependencia, dependencia_test)

   return dependencia_test

⚠ Já vi alguns smells relacionados a muitas sobrescritas em uma mesma fixture, o que pode ser meio delirante na hora de usar os valores.

Testes

Com isso as funções de testes podem usar ou não o valor retornado:

tests/test_app.py
import pytest
from app.app import sut


def test_injeta_dependencia_no_sut(override_de_dependencia):
    """
    Validação simples, afinal é um bypass pra mostrar a infra.

    Você recebe a fixture quando precisa dela para alguma validação
        ou para algum uso básico.
    """
    assert sut() == override_de_dependencia


@pytest.mark.usefixtures("override_de_dependencia")
def test_injeta_mais_nao_usa():
    """
    Se não precisar da fixture, pode simplesmente chamar ela.

    O a injeção será sobreescrita, mas não necessáriamente
        será usada pelo teste
    """
    assert sut() == 2

Assim, simplificando a estrutura dos testes.