Se você perguntasse a um programador/desenvolvedor qual é a pior parte do trabalho, acho que a maioria responderia escrever testes. Você precisa criar dados simulados e fazer alteraçoes de codificação para que coisas específicas funcionem corretamente. Além disso, você precisa escrever muitos casos diferentes para um único recurso/função. Leva muito tempo para escrever bons testes. Mas com a introdução do async/await no node 8, escrever testes se torna muito melhor! É o que usamos para criar mais de 100 testes para o HelloSugoi.
Descubra e revise os melhores softwares Blockchain
Público:
Principalmente para desenvolvedores que estão escrevendo aplicativos Ethereum no Solidity e estão escrevendo testes para seu código em Javascript. E para qualquer pessoa curiosa sobre como fazer async/await e como isso pode ajudar a escrever testes melhores.
Requisitos/instalação:
Com o node 8, async/await foi habilitado por padrão. Antes da versão 7.6, você podia usar async/await desde que adicionasse o sinalizador --harmony-async-await
. Para a versão anterior, você também pode usar o Babel para transpilar para javascript compatível com ES5. Sugiro instalar o Node 8 ou superior para que este projeto funcione corretamente. Você pode usar homebrew ou nvm para gerenciar a versão que está usando.
Além de ter o node instalado, você precisa ter o Truffle e o Ganache-cli instalados globalmente.
-
npm install -g
truffle -
npm install -g
ganache-cli
Truffle é usado para compilar e migrar contratos Solidity para redes Ethereum. Ganache-cli é usado como um nó Ethereum falso que retorna imediatamente em vez de 15 segundos. Para confirmar que você tem tudo instalado, tente o seguinte:
- Node ≥ 7.6 confirme digitando
node -v
na linha de comando - npm ≥ 4 confirme digitando
npm -v
na linha de comando -
Truffle ≥ 3.3 confirme digitando
truffle -v
na linha de comando -
Ganache-cli ≥ 5.0.0 confirme digitando
ganache-cli -v
na linha de comando
Exemplo de configuração do projeto:
Eu tenho um repositório de projeto de exemplo com todo o nosso código. Se você quiser trabalhar nele, por favor clone e instale suas dependências.
- git clone https://github.com/sogoiii/trufa-async-await-tests
cd truffle-async-await-tests
-
npm install
ouyarn install
truffle compile
truffle test
O quarto comando executa todos os testes e eles devem ser concluídos com êxito.
Como funciona o Async/Await:
Conceitualmente, async/await faz com que o código JavaScript assíncrono pareça um código síncrono. Vamos comparar os dois:
const status = () => {
return MetaCoin.deployed()
.then(instance => {
return instance.getBalance.call(accounts[0]);
})
.then(balance => {
assert.equal(balance.valueOf(), 10000, "10000 não estava na primeira conta");
})
}
const status2 = function async () {
let meta = await MetaCoin.deployed();
let balance = await meta.getBalance.call(accounts[0]);
assert.equal(balance.valueOf(), 10000, "10000 não estava na primeira conta")
}
Comparação simples do teste truffle como cadeia de promise ou async/await
A partir do exemplo acima, acho que o método status2
parece mais limpo que o método status
. Ele literalmente tem 4 linhas de código pouco expressivo, que é mais do que as 3 linhas de lógica em status2
. Acho fácil a escolha!
Para usar async/await, você precisa adicionar o moniker async
à declaração da função. Isso diz ao interpretador v8 para procurar a chave await
, caso contrário, o nó gera um erro perguntando o que é await
. Você coloca await
na frente de qualquer função/método promise (é um objeto usado para processamento assíncrono. Uma Promise (de "promessa") representa um valor que pode estar disponível agora, no futuro ou nunca), não um retorno de chamada! Se sua função é um retorno de chamada, sugiro usar um pacote como bluebird ou o método nativo promisify que foi introduzido no Node 8. Felizmente, Truffle retorna promises em vez de retornos de chamada.
Uma função assíncrona (async function(){})
também retorna uma promise! Isso é bom porque você pode encadear _promises _ou funções assíncronas, se desejar. Como no exemplo abaixo:
const operationOne = function async (data) {
let step1 = await somePromise(data)
let step2 = await anotherPromise(step1)
return step2.data
}
actionOne()
.then(operationOne)
.then(data => {
//more work
})
Async retorna uma promise
Um tempo atrás, o async/await teve um desempenho muito grande. Era grande o suficiente para que não usar async/await fosse razoável. Desde o lançamento da v8 5.5, o desempenho só melhorou. A menos que você esteja fazendo algo computacionalmente pesado, usar async/await pode ser uma boa alternativa.
Exemplo (Caso Fácil):
Truffle é legal porque .deployed
retorna uma promise. Lembre-se, só posicionar await na frente das promises!
Abra o arquivo test/metacoin.js
e veja o primeiro teste.
var MetaCoin = artifacts.require("./MetaCoin.sol");
// ... mais código
contract('MetaCoin', function(accounts) {
it("deve colocar 10000 MetaCoin na primeira conta", async function() {
let meta = await MetaCoin.deployed();
let balance = await meta.getBalance.call(accounts[0]);
assert.equal(balance.valueOf(), 10000, "10000 não estava na primeira conta")
});
// ... mais código
})
Exemplo de teste fácil Truffle
O primeiro teste faz apenas 3 operações. A primeira é obter o objeto JavaScript do contrato implantado e defini-lo para a variável meta
. Em seguida, ele chama a função de getBalance.call(
), função por trás da palavra chave await
. Novamente, isso ocorre porque .call
retorna uma promise. Por fim, simplesmente verificamos se a função retornou o valor esperado dentro de um assert. Mole-mole! Toda a sua verificação é se o saldo de meta
é 1000
conforme o título do teste.
Ao executar truffle test test/metacoin.js
na linha de comando, você verá como todos os testes são executados com êxito.
Exemplo (caso de erro):
Para capturar erros com async/await, você precisa encapsular a chamada dentro de um try/catch.
it("deve falhar porque a função não existe no contrato", async function () {
let meta = await MetaCoin.deployed();
try {
await meta.someNonExistentFn.call();
} catch (e) {
return true;
}
throw new Error("Eu nunca deveria ver isto!")
})
Exemplo de teste de falha no Truffle
No teste acima, estou chamando uma função que sei que não existe no contrato Solidity. Await
lançará um erro que é capturado no try/catch. Como quero verificar se a chamada someNonExistentFn
gera um erro, retorno true dentro do catch para sair do teste. Se await meta.someNonExistentFn()
fosse bem-sucedido, o bloco catch não seria chamado e a próxima linha throw new Error(...
) seria atingida. Dizendo-me assim que a função dele falhou em lançar.
Exemplo (viagem no tempo):
Às vezes, um contrato Solidity pode ter uma função que só funciona quando chamada no futuro. Em MetaCoin.sol
eu adiciono o modificadorde função
onlyAfterDate
para specialFn
onde eu defino quando no futuro uma função pode ser chamada.
contract MetaCoin {
//...Código
uint endTime;
//...Código
modifier onlyAfterDate() {
if(now <= endTime) {
throw;
}
_;
}
//...Código
function MetaCoin() {
balances[tx.origin] = 10000;
endTime = 1 days; // I set endTime
}
//...Código
function specialFn() onlyAfterDate returns(bool) { // adicionar modificador
return true;
}
}
MetaCoin.sol apenas com informações de tempo
Dentro do construtor MetaCoin, defini manualmente endTime
como 1 days
. As variáveis de tempo no Solidity são uint
de segundos de era. O modificador de função onlyAfterDate
usa endTime
, o que significa que qualquer função com onlyAfterDate
só poderá ser chamada em 1 days
.
Para testar essa funcionalidade, não vamos esperar 1 dia inteiro! E o que acontece quando o tempo é mais dinâmico, em vez de codificado? Ganache-cli adicionou um método RPC chamadoevm_increaseTime
que viajará no tempo pela blockchain.
const timeTravel = function (time) {
return new Promise((resolve, reject) => {
web3.currentProvider.sendAsync({
jsonrpc: "2.0",
method: "evm_increaseTime",
params: [time], // 86400 é o número de segundos no dia
id: new Date().getTime()
}, (err, result) => {
if(err){ return reject(err) }
return resolve(result)
});
})
}
Ganache-cli evm_increaseTime como uma promise
Tudo o que temos a fazer é realizar uma chamada RPC para a web3 com evn_increaseTime
e parâmetros. Eu embrulhei a web3 baseada em callback em uma promise para que pudéssemos usá-la com async/await.
Com essa nova função, nós simplesmente esperamos
um tempo e boom! Alguns dias depois! Se você estiver no Testrpc 4.0.0 (e versão mais antiga do ganache-cli), há um bug em que um bloco deve ser minerado paraevm_timeTravel
entrar efeito. Você pode revisar o bug aqui.
it("deve chamar specialFn com sucesso porque passou tempo suficiente", async function () {
let meta = await MetaCoin.new();
await timeTravel(86400 * 3) //3 days later
await mineBlock() // workaround for https://github.com/ethereumjs/testrpc/issues/336
let status = await meta.specialFn.call();
assert.equal(status, true, "specialFn deve ser chamado após 1 dia")
})
Viagem no tempo bem sucedida com Truffle e Ganache-cli
Grande Comparação:
Os dois trechos abaixo testam exatamente a mesma coisa. A única diferença é que um é escrito como uma cadeia de promise e o outro com async/await. Apenas conduzindo o ponto para casa!
it("deve enviar as moedas corretamente", function() {
var meta;
// Obter saldos iniciais da primeira e segunda conta.
var account_one = accounts[0];
var account_two = accounts[1];
var account_one_starting_balance;
var account_two_starting_balance;
var account_one_ending_balance;
var account_two_ending_balance;
var amount = 10;
return MetaCoin.deployed().then(function(instance) {
meta = instance;
return meta.getBalance.call(account_one);
}).then(function(balance) {
account_one_starting_balance = balance.toNumber();
return meta.getBalance.call(account_two);
}).then(function(balance) {
account_two_starting_balance = balance.toNumber();
return meta.sendCoin(account_two, amount, {from: account_one});
}).then(function() {
return meta.getBalance.call(account_one);
}).then(function(balance) {
account_one_ending_balance = balance.toNumber();
return meta.getBalance.call(account_two);
}).then(function(balance) {
account_two_ending_balance = balance.toNumber();
assert.equal(account_one_ending_balance, account_one_starting_balance - amount, "A quantidade não foi retirada corretamente do remetente");
assert.equal(account_two_ending_balance, account_two_starting_balance + amount, "A quantia não foi enviada corretamente para o receptor");
});
});
Grande Teste Truffle em Cadeia de Promise
it("deve enviar as moedas corretamente", async function () {
// Obter saldos iniciais da primeira e segunda conta.
var account_one = accounts[0];
var account_two = accounts[1];
var amount = 10;
let meta = await MetaCoin.deployed();
let balance1 = await meta.getBalance.call(account_one);
let balance2 = await meta.getBalance.call(account_two);
let account_one_starting_balance = balance1.toNumber();
let account_two_starting_balance = balance2.toNumber();
await meta.sendCoin(account_two, amount, {from: account_one});
let balance3 = await meta.getBalance.call(account_one);
let balance4 = await meta.getBalance.call(account_two);
let account_one_ending_balance = balance3.toNumber();
let account_two_ending_balance = balance4.toNumber();
assert.equal(account_one_ending_balance, account_one_starting_balance - 10, "A quantidade não foi retirada corretamente do remetente");
assert.equal(account_two_ending_balance, account_two_starting_balance + 10, "A quantia não foi enviada corretamente para o receptor");
});
Grande Teste Truffle Async/Await
Conclusão:
Espero que você possa ver o quanto os testes podem ser melhores com async/await. Muitos boilerplate (se refere a seções de código que devem ser incluídas em muitos lugares com pouca ou nenhuma alteração) desaparecem, o que aumenta a legibilidade. Capturar erros pode ser mais tedioso, mas eles são gerenciáveis. Além disso, se você precisar viajar no tempo para seus testes, o Ganache-cli fornece um método para atualizar o tempo. Se você já estiver usando promises, a integração com async/await é muito fácil. Se você tiver muitos retornos de chamada, o uso require('utils').promisify
facilita sua vida. Vê a tendência?
Junte-se ao canal Coinmonks no Telegram e ao canal do Youtube receba notícias diárias sobre criptomoedas
Leia também
- Copy Trading | Software de imposto de criptos
- Grid Trading | Carteira de hardware para criptomoedas
- Sinais de criptomoedas no Telegram | Bot de negociação de criptomoedas
- Melhor Corretora de Criptomoedas | Melhor Corretora de Criptomoedas na Índia
- Análise da Bitget | Gemini vs BlockFi | Negociação de Futuros OKEx
- Melhores Bots de Negociação de Criptomoedas nos EUA | Análise da Changelly
- Ganhe renda passiva usando arbitragem de criptomoedas na Índia
- Análise da Huobi | Negociação de Margem OKEx | Negociação de Futuros
- Melhores APIs de criptomoeda para desenvolvedores
- Melhor plataforma de empréstimo de criptomoedas
- Um guia definitivo sobre [Tokens Alavancados](https://medium.com/coinmonks/leveraged-token-3f5257808b22
Artigo escrito por Angello Pozo e traduzido por Marcelo Panegali. A versão original pode ser encontrada aqui.
Latest comments (0)