Como testar contratos inteligentes

imagem

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:

  1. Como preparar um ambiente de teste.
  2. 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 :

imagem

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:

imagem

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:

  1. Aceitação e quantidade de doações.
  2. Foi atingida uma certa quantia de doação.
  3. O que acontece se você doar mais do que precisa coletar.
  4. O valor total da doação.
  5. 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:

  1. Para procurar eventos (eventos) e verificar seus argumentos, uma pequena função findEvent foi escrita.
  2. 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:

imagem

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.

Source: https://habr.com/ru/post/pt432012/


All Articles