A continuación, se presenta un ejemplo completo de cómo escribir y ejecutar pruebas unitarias para un contrato inteligente en Ethereum utilizando Hardhat. Este ejemplo incluirá la creación del contrato inteligente, la configuración de los fixtures, y la escritura de diversas pruebas.
Paso 1: Configuración del proyecto
Debemos tener instalado Hardhat y haber creado un proyecto básico (si no sabes cómo hacerlo, revisar el módulo 4).
Asegúrate de tener instalado el plugin hardhat-toolbox
deployTokenFixture: Esta función despliega el contrato Token con un suministro inicial. También obtiene tres cuentas para usar en las pruebas (owner, addr1, addr2).
Primera prueba: Verificar el propietario
it("Should set the right owner",asyncfunction () {const { token,owner } =awaitloadFixture(deployTokenFixture);expect(awaittoken.balanceOf(owner.address)).to.equal(1000n*10n**18n);});
Propósito: Verificar que el propietario inicial (la cuenta que despliega el contrato) reciba el suministro total de tokens.
Proceso:
Desplegar el contrato utilizando la fixture.
Obtener el balance del propietario.
Verificar que el balance sea igual al suministro inicial (1000 tokens convertidos a la unidad mínima).
Segunda prueba: Verificar el suministro total
it("Should assign the total supply of tokens to the owner",asyncfunction () {const { token,owner } =awaitloadFixture(deployTokenFixture);constownerBalance=awaittoken.balanceOf(owner.address);expect(awaittoken.totalSupply()).to.equal(ownerBalance);});
Propósito: Asegurarse de que el suministro total de tokens se asigna correctamente al propietario.
Proceso:
Desplegar el contrato utilizando la fixture.
Obtener el balance del propietario.
Verificar que el balance del propietario sea igual al suministro total de tokens.
Tercera prueba: Transferir tokens
describe("Transactions",function () {it("Should transfer tokens between accounts",asyncfunction () {const { token,owner,addr1,addr2 } =awaitloadFixture(deployTokenFixture);awaittoken.transfer(addr1.address,50n*10n**18n);expect(awaittoken.balanceOf(addr1.address)).to.equal(50n*10n**18n);awaittoken.connect(addr1).transfer(addr2.address,50n*10n**18n);expect(awaittoken.balanceOf(addr2.address)).to.equal(50n*10n**18n);expect(awaittoken.balanceOf(addr1.address)).to.equal(0n); });
Propósito: Verificar que los tokens se transfieren correctamente entre cuentas.
Proceso:
Desplegar el contrato utilizando la fixture.
Transferir 50 tokens de owner a addr1.
Verificar que addr1 haya recibido 50 tokens.
Transferir 50 tokens de addr1 a addr2.
Verificar que addr2 haya recibido 50 tokens y que addr1 tenga un balance de 0 tokens.
Cuarta prueba: Verificar que una cuenta no transfiere si no tienen fondos suficientes
it("Should fail if sender doesn’t have enough tokens",asyncfunction () {const { token,owner,addr1 } =awaitloadFixture(deployTokenFixture);constinitialOwnerBalance=awaittoken.balanceOf(owner.address);awaitexpect(token.connect(addr1).transfer(owner.address,1n*10n**18n) ).to.be.revertedWith("Insufficient balance");expect(awaittoken.balanceOf(owner.address)).to.equal(initialOwnerBalance);});
Propósito: Asegurarse de que la transferencia falla si el remitente no tiene suficientes tokens.
Proceso:
Desplegar el contrato utilizando la fixture.
Intentar transferir 1 token desde addr1 (que no tiene tokens) al owner.
Verificar que la transacción sea revertida con el mensaje "Insufficient balance".
Verificar que el balance del owner no haya cambiado.
Quinta prueba: Actualización de balances después de transferencia
Propósito: Verificar que los balances se actualicen correctamente después de las transferencias.
Proceso:
Desplegar el contrato utilizando la fixture.
Transferir 100 tokens de owner a addr1.
Transferir 50 tokens de owner a addr2.
Verificar que el balance final del owner sea el balance inicial menos 150 tokens.
Verificar que addr1 tenga 100 tokens y addr2 tenga 50 tokens.
Paso 4: Ejecutar las pruebas
Para ejecutar las pruebas, utiliza el siguiente comando:
npxhardhattest
Este comando ejecutará todas las pruebas definidas en el archivo TokenTest.js y mostrará los resultados en la consola.
Si las pruebas han sido exitosas, obtendrás un resultado de este tipo:
¡¡Felicitaciones!! Has ejecutado tus primeras pruebas de forma exitosa. Ahora empieza a hacer pruebas con otros contratos que hayas creado.
Hardhat también te permite obtener la cobertura de tus pruebas, para ello solo necesitas ingresar el siguiente comando:
npxhardhatcoverage
y obtendrás un reporte como este
Una cosa más: Chai
En este ejemplo hemos utilizado Chai para realizar nuestras pruebas. Chai es una biblioteca de assertions (afirmaciones) que existe para Node.js que se puede combinar con cualquier framework de pruebas como Hardhat. En el contexto de pruebas para contratos inteligentes, Chai se utiliza ampliamente debido a su sintaxis amigable y capacidades de aserción robustas.
Para usar Chai en tus pruebas, primero necesitas requerir la biblioteca y luego utilizar sus métodos en tus pruebas. Aquí hay un ejemplo básico de configuración:
const { expect } =require("chai");
Chai ofrece tres estilos de aserción principales:
Assert: Estilo clásico basado en funciones.
Expect: Estilo BDD (Behavior-Driven Development) que es más legible.
Should: Estilo BDD que añade propiedades al objeto Object.prototype.
En nuestro ejemplo utilizamos el estilo expect, ya que es uno de los más utilizados en pruebas de contratos inteligentes con Hardhat.