
Os termos do contrato inteligente não podem ser alterados. Portanto, sempre que você cria um contrato inteligente, é necessário garantir que ele funcione corretamente. O teste é uma maneira segura de testar um contrato em diferentes situações. Neste tutorial, você aprenderá quais etapas a serem seguidas.
Vou dizer:
- Como preparar um ambiente de teste.
- Como escrever testes de JavaScript e executá-los no Truffle .
O tutorial é destinado a iniciantes que começaram a se aprofundar no desenvolvimento e teste de contratos inteligentes no Ethereum. Para entender este tutorial, você deve ter pelo menos um conhecimento básico de
JavaScript e
Solidity .
1. Como preparar um ambiente de teste
Existem várias maneiras de testar contratos inteligentes, mas o
Truffle é a ferramenta de gravação de teste mais popular usando
JavaScript . No
Truffle, você pode escrever testes de unidade ou executar testes de integração completos com parâmetros reais do ambiente de produção.
Primeiro você precisa instalar a versão mais recente do Node.js no
site oficial .
Em seguida, abra um terminal e instale o
Truffle usando o seguinte comando:
npm install -g truffle
Após instalar o
Truffle , sem fechar o terminal, crie o diretório
Funding :
mkdir Funding
Em seguida, vá para o diretório com o comando:
cd Funding
Para inicializar o diretório, execute o comando:
truffle init
Após a execução do comando, as seguintes pastas e arquivos serão criados no diretório
Financiamento :

Além disso, trabalharemos com cada diretório. Enquanto isso, continuamos a preparar o ambiente de teste.
Para executar os testes, você precisa de bibliotecas Javascript.
Mocha é uma biblioteca que contém funções comuns para teste, incluindo a
descrição e a
mesma .
Chai é uma biblioteca que suporta uma variedade de funções para verificações. Existem diferentes "estilos" para verificar os resultados, e Chai nos oferece essa oportunidade. No tutorial, usaremos o
Should .
Por padrão, o Mocha faz parte do Truffle, podemos usar facilmente as funções da biblioteca. Chai deve ser instalado manualmente. Para fazer isso, use o terminal e, no diretório raiz do projeto, execute o comando:
npm install chai
Também instalei a
biblioteca chai-bignumber para comparar números com precisão arbitrária:
npm install --save-dev chai-bignumber
O ambiente de teste está pronto para isso. Agora você pode começar a desenvolver um contrato inteligente e testá-lo.
2. Como escrever testes de JavaScript e executá-los no Truffle
Para desenvolver testes, você precisa de um contrato inteligente. Desenvolveremos um contrato que permita coletar doações, definir um valor específico para obter a coleta e retirar fundos. Se alguém doar mais, a diferença entre a quantia acumulada e a quantia que precisa ser coletada será devolvida a ele.
Vá para o diretório
Financiamento -> contratos . Em
Financiamento / contratos, crie o arquivo
Funding.sol com a extensão
.sol - este será o contrato inteligente para teste.
No
Funding.sol, adicione o seguinte código:
pragma solidity 0.4.24; contract Funding { uint public raised; uint public goal; address public owner; event Donated(uint donation); event Withdrew(uint amount); modifier onlyOwner() { require(owner == msg.sender); _; } modifier isFinished() { require(isFunded()); _; } modifier notFinished() { require(!isFunded()); _; } constructor (uint _goal) public { owner = msg.sender; goal = _goal; } function isFunded() public view returns (bool) { return raised >= goal; } function donate() public payable notFinished { uint refund; raised += msg.value; if (raised > goal) { refund = raised - goal; raised -= refund; msg.sender.transfer(refund); } emit Donated(msg.value); } function withdraw() public onlyOwner isFinished { uint amount = address(this).balance; owner.transfer(amount); emit Withdrew(amount); } }
O contrato está pronto. Implementaremos um contrato inteligente por meio da migração.
As migrações são arquivos
JavaScript que ajudam a implantar contratos na rede Ethereum. Essa é a principal maneira de implantar contratos.
Vá para o diretório
Financiamento -> migrações e crie o arquivo
2_funding.js , adicionando o seguinte código:
const Funding = artifacts.require("./Funding.sol"); const ETHERS = 10**18; const GOAL = 20 * ETHERS; module.exports = function(deployer) { deployer.deploy(Funding, GOAL); };
Para executar os testes, você precisa usar o comando
truffle test . No terminal, vá para a raiz do diretório
Financiamento , criado durante a preparação do ambiente de teste e insira:
truffle test
Se a seguinte saída aparecer no terminal, tudo será feito corretamente:

Agora vamos começar a escrever testes.
Verificação do proprietário
Vá para o diretório
Financiamento -> teste e crie um arquivo
test_funding.js com a extensão
.js . Este é o arquivo no qual os testes serão gravados.
Adicione o seguinte código ao arquivo
test_funding.js :
const Funding = artifacts.require("Funding"); require("chai").use(require("chai-bignumber")(web3.BigNumber)).should(); contract("Funding", function([account, firstDonator, secondDonator]) { const ETHERS = 10**18; const GAS_PRICE = 10**6; let fundingContract = null; it("should check the owner is valid", async () => { fundingContract = await Funding.deployed(); const owner = await fundingContract.owner.call() owner.should.be.bignumber.equal(account); });
No teste, verificamos que o contrato de
financiamento armazena o endereço do proprietário que implantou o contrato. No nosso caso, a
conta é o primeiro elemento da matriz.
O Truffle permite que você use até dez endereços. Nos testes, precisamos apenas de três endereços.
Aceitação de doações e verificação do fim da captação de recursos
Nesta seção, verificaremos:
- Aceitação e quantidade de doações.
- Foi atingida uma certa quantia de doação.
- O que acontece se você doar mais do que precisa coletar.
- O valor total da doação.
- Posso continuar levantando fundos se a quantia necessária tiver sido coletada.
Vamos escrever e analisar os testes:
. . . . . . . . . . . . . . . . . . . . . . const ETHERS = 10**18; const GAS_PRICE = 10**6; let fundingContract = null; let txEvent; function findEvent(logs, eventName) { let result = null; for (let log of logs) { if (log.event === eventName) { result = log; break; } } return result; }; it("should accept donations from the donator #1", async () => { const bFirstDonator= web3.eth.getBalance(firstDonator); const donate = await fundingContract.donate({ from: firstDonator, value: 5 * ETHERS, gasPrice: GAS_PRICE }); txEvent = findEvent(donate.logs, "Donated"); txEvent.args.donation.should.be.bignumber.equal(5 * ETHERS); const difference = bFirstDonator.sub(web3.eth.getBalance(firstDonator)).sub(new web3.BigNumber(donate.receipt.gasUsed * GAS_PRICE)); difference.should.be.bignumber.equal(5 * ETHERS); });
Antes de analisar o teste, quero observar 2 pontos:
- Para procurar eventos (eventos) e verificar seus argumentos, uma pequena função findEvent foi escrita.
- Para conveniência dos testes e cálculos, foi definido o próprio custo do gás (constante GAS_PRICE).
Agora vamos analisar o teste. No teste, verificamos:
- que podemos aceitar doações chamando o método donate () ;
- que o valor doado para nós está indicado corretamente;
- aquele que doou fundos, o saldo diminuiu pelo valor doado.
it("should check if donation is not completed", async () => { const isFunded = await fundingContract.isFunded(); isFunded.should.be.equal(false); });
Nesse teste, verificamos que a captação de recursos ainda não foi concluída.
it("should not allow to withdraw the fund until the required amount has been collected", async () => { let isCaught = false; try { await fundingContract.withdraw({ gasPrice: GAS_PRICE }); } catch (err) { isCaught = true; } isCaught.should.be.equal(true); });
No teste, verificamos que não podemos sacar fundos até que a quantia que precisamos seja coletada.
it("should accept donations from the donator #2", async () => { const bSecondDonator= web3.eth.getBalance(secondDonator); const donate = await fundingContract.donate({ from: secondDonator, value: 20 * ETHERS, gasPrice: GAS_PRICE }); txEvent = findEvent(donate.logs, "Donated"); txEvent.args.donation.should.be.bignumber.equal(20 * ETHERS); const difference = bSecondDonator.sub(web3.eth.getBalance(secondDonator)).sub(new web3.BigNumber(donate.receipt.gasUsed * GAS_PRICE)); difference.should.be.bignumber.equal(15 * ETHERS); });
No teste, verificamos que, se você doar uma quantia grande, o método
donate () calcula e devolve os fundos para a pessoa que doou mais do que o necessário. Esse valor é a diferença entre o valor acumulado e o valor que você deseja coletar.
it("should check if the donation is completed", async () => { const notFunded = await fundingContract.isFunded(); notFunded.should.be.equal(true); }); it("should check if donated amount of money is correct", async () => { const raised = await fundingContract.raised.call(); raised.should.be.bignumber.equal(20 * ETHERS); }); it("should not accept donations if the fundraising is completed", async () => { let isCaught = false; try { await fundingContract.donate({ from: firstDonator, value: 10 * ETHERS }); } catch (err) { isCaught = true; } isCaught.should.be.equal(true); });
Nestes três testes, verificamos:
- que a captação de recursos está concluída;
- que o valor da doação está correto;
- que ninguém mais pode doar, uma vez que a captação de recursos foi concluída.
Retirar fundos
Na seção anterior do tutorial, coletamos a quantidade necessária, agora ela pode ser exibida:
. . . . . . . . . . . . . . . . . . . . . . it("should allow the owner to withdraw the fund", async () => { const bAccount = web3.eth.getBalance(account); const withdraw = await fundingContract.withdraw({ gasPrice: GAS_PRICE }); txEvent = findEvent(withdraw.logs, "Withdrew"); txEvent.args.amount.should.be.bignumber.equal(20 * ETHERS); const difference = web3.eth.getBalance(account).sub(bAccount); difference.should.be.bignumber.equal(await fundingContract.raised.call() - withdraw.receipt.gasUsed * GAS_PRICE); });
Ao chamar a função
retirada () , retiramos os fundos do proprietário do contrato inteligente, em nossa
conta de caso. Depois, verificamos se realmente retiramos a quantia necessária. Para fazer isso,
escreva a diferença no saldo antes e depois da retirada dos fundos na constante de
diferença . O resultado é comparado com a quantidade de doações menos a taxa da transação. Como mencionado acima, para conveniência dos testes e cálculos, defino meu próprio preço para o
gás .
Execute os testes escritos com o comando
truffle test . Se tudo foi feito corretamente, o resultado deve ser o seguinte:

Resultado
Tentei descrever as etapas do teste de contratos inteligentes em uma linguagem simples e compreensível: desde a preparação de um ambiente de teste até a escrita dos próprios testes.
Agora você pode testar qualquer contrato inteligente e garantir que ele funcione corretamente.