Uso de Brownie para testing en Python

Uso de Brownie para testing en Python

En el ecosistema de desarrollo de smart contracts sobre Ethereum, la calidad y fiabilidad de nuestro código es esencial. Brownie emerge como una de las herramientas más potentes para el testing de contratos escritos en Solidity y Vyper, aprovechando el familiar entorno de Python y la robustez de pytest. En este artículo exploraremos su instalación, configuración, flujo de trabajo, características avanzadas y buenas prácticas para integrar Brownie en proyectos profesionales.

Índice de contenido

  • ¿Qué es Brownie
  • Requisitos e instalación
  • Estructura de un proyecto Brownie
  • Escribir y ejecutar tests
  • Fixtures y parametrización
  • Integración con redes locales y forking
  • Medición de cobertura y reportes
  • Plugins útiles
  • Integración en CI/CD
  • Buenas prácticas y recomendaciones
  • Recursos y enlaces de interés

1. ¿Qué es Brownie

Brownie es un framework de desarrollo y testing para contratos inteligentes, creado por Luigi De Rosa. Ofrece:

  • Integración nativa con pytest.
  • Consola interactiva para debugging.
  • Soporte para múltiples compiladores (Solidity, Vyper).
  • Simulación de nodos locales con ganache-cli.
  • Facilidad para deployments y scripts de migración.

2. Requisitos e instalación

Antes de comenzar, necesitamos un entorno de Python 3.7 y Node.js (para Ganache). A continuación, ejecutamos:

pip install eth-brownie
npm install -g ganache-cli

Verificamos la instalación:

brownie --version
ganache-cli --version

3. Estructura de un proyecto Brownie

Al crear un proyecto mediante brownie init, obtenemos:

Carpeta/Archivo Descripción
contracts/ Código fuente Solidity/Vyper.
scripts/ Scripts de despliegue y utilidades.
tests/ Tests escritos en Python con pytest.
build/ Artefactos compilados (.json).
brownie-config.yaml Configuración global del proyecto.

4. Escribir y ejecutar tests

Cada test es una función normal de pytest, usando accounts, contract y fixtures propias de Brownie:

from brownie import accounts, MyContract, reverts

def test_deploy_and_basic_behavior():
    # Despliegue usando la cuenta 0
    deployer = accounts[0]
    # Despliega el contrato
    instancia = MyContract.deploy({from: deployer})
    
    # Verifica el valor por defecto
    assert instancia.value() == 0
    
    # Interacción y evento
    tx = instancia.setValue(42, {from: deployer})
    assert ValueChanged in tx.events
    assert instancia.value() == 42

def test_error_when_unauthorized(accounts, MyContract):
    owner = accounts[0]
    attacker = accounts[1]
    contrato = MyContract.deploy({from: owner})
    
    # Esperamos revert
    with reverts(Ownable: caller is not the owner):
        contrato.changeOwner(attacker, {from: attacker})

Para ejecutar todos los tests:

brownie test

5. Fixtures y parametrización

Brownie expone fixtures como accounts, project, web3, entre otras. Podemos parametrizar tests con pytest:

import pytest

@pytest.mark.parametrize(initial,delta,expected, [
    (0, 5, 5),
    (10, -3, 7),
    (100, 0, 100),
])
def test_multiple_values(initial, delta, expected, accounts, MyContract):
    c = MyContract.deploy({from: accounts[0], args: [initial]})
    c.adjust(delta, {from: accounts[0]})
    assert c.value() == expected

6. Integración con redes locales y forking

Para simular redes realistas o testnets, Brownie permite configurar networks en brownie-config.yaml:

networks:
  default: development
  development:
    cmd_settings:
      gas_price: auto
  mainnet-fork:
    cmd_settings:
      fork: https://mainnet.infura.io/v3/YOUR_INFURA_KEY

Y ejecutamos:

brownie console --network mainnet-fork

7. Medición de cobertura y reportes

Brownie cuenta con un plugin de cobertura, basta instalar pytest-cov y luego:

pip install pytest-cov
brownie test --coverage

Obtendremos un reporte detallado del porcentaje de líneas de Solidity cubiertas por los tests, lo cual es crucial para auditorías.

8. Plugins útiles

  • brownie-cov: cobertura de tests.
  • eth-gas-reporter: métricas de consumo de gas.
  • brownie-upgrades: contratos proxy y migraciones.
  • pytest-random: orden aleatorio de tests para detectar dependencias ocultas.

9. Integración en CI/CD

Para automatizar tests en GitHub Actions, un workflow básico:

name: Brownie CI

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Setup Python
        uses: actions/setup-python@v4
        with:
          python-version: 3.8
      - name: Install dependencies
        run: 
          pip install eth-brownie pytest-cov
          npm install -g ganache-cli
      - name: Start Ganache
        run: ganache-cli -p 8545  sleep 5
      - name: Run tests
        run: brownie test --network development --coverage

10. Buenas prácticas y recomendaciones

  • Escribir tests aislados, sin dependencias de orden.
  • Parametrizar escenarios límite (overflow, underflow).
  • Simular múltiples cuentas y roles (owner, user, attacker).
  • Verificar eventos y estados posteriores a cada transacción.
  • Usar coverage y gas-reporter antes de cada release.
  • Integrar linting de Solidity (solhint/solium) y formateo (prettier-plugin-solidity).

11. Recursos y enlaces de interés

Con estos conocimientos y herramientas, podrás elevar la calidad de tus smart contracts, detectar errores con antelación y garantizar seguridad y fiabilidad en tus despliegues sobre Ethereum y redes compatibles. ¡Feliz testing con Brownie!


Comentarios

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *