(Part 7) Ethereum Testing - JavaScript Asynchronous Functions Applied To Truffle Testing (PT 7)

in #utopian-io6 years ago

Repository

https://github.com/igormuba/EthereumTesting/tree/master/class7

What Will I Learn?

  • What are asynchronous functions in JavaScript
  • How async functions work, when and how to use
  • Applying async functions in Truffle testing

Requirements

  • Internet connection.
  • Code editor.
  • Browser.

Difficulty

  • Intermediary.

Introduction

On the previous tutorials, we have tested a few functions from a decentralized exchange we have built ourselves. So far, all of our tests were synchronous. You can see that by the syntax we have used to describe the test cases

it("Should do something", ()=>
    Exchange.deployed().then(instance=>{
        //operations we need
        return instance.getter();
    }).then(returnedVariable=>{
        //assertions and other code
    })
);

Because of the nature of the Ethereal blockchain, we can't know how long it will take to get a return from a call, that is why we need to use then. On the first chunk of code, we make the operations we need. After we finish the transactions and such operations, we can make a call to a function from the contract that returns a variable to us.

If you try to work with the returned variable this way:

it("Should do something", ()=>
    Exchange.deployed().then(instance=>{
        //operations we need
        let returnedVariable = instance.getter();
        //assertions and other code
    })
);

You will get an "error" similar to this:
Captura de Tela 20190317 às 13.24.33.png

That is because you have sent a request to the blockchain, but when you logged the result to the console you haven't received a response yet. And this is why so far we have used the .then statement.

But there is another way to do that and I am going to show how to do that in this tutorial. To learn how to use it, we will turn our test cases into async functions and use the await word instead of .then.

Asynchronous functions

The previous section explained a bit about asynchronous functions applied to Ethereum blockchain, but they are not exclusive to Truffle testing. While coding in JavaScript (either for testing, front or backend) you will probably. face the necessity to use asynchronous functions because that is how the language is supposed to work. Javascript is a purely interpreted language, so you don't have the comfort of a compiler running and telling you where your code is flawed. By using asynchronous functions you can avoid some bugs.

For example, if you are building a web page, and you need some code to be loaded first. If you have many scripts that use the outputs from each other to do their logic, you can face bugs if you are not prepared. One script might be lighter and be loaded before the others. If such a case happens, the scripts that depend on that one will break.

In our case, working on our tests for the contract, we can easily see that! Like on the example I gave on the previous section. If you make a call to a contract and you have to work with a received variable, the code that needs that variable to work will break, like on the example I gave, where it does not show the result, but instead, shows the promise of a result!

Syntax

The syntax we will use to work with asynchronous function consist of two words:
async: Tells JavaScript that we are defining a function that should understand how to work with asynchronous functions. You can't call async or wait methods from a function that is not asynchronous!
await: Tells JavaScript to wait for a result before it continues to execute the code. This literally stops the execution of the code until we get a result.

Regarding the await, you might want to be careful with that. If you call that, the code will stop executing until it gets the result. In our case, that is exactly what we want because we are testing! We can't proceed with the tests without the results of balances and operations. But in a production environment, you might make some customers angry if your code requires the waiting of large and slow operations.

With those two words, we can replace the .then syntax.

How to make an asynchronous call

To use the await keyword, you must call it from an async context. See the example below:

async function afunc(){
//before
let variable = await doSomething();
//after
}

We declare a function as async by placing the keyword before the function declaration. Inside the function, we can make asynchronous calls by literally telling JavaScript to wait for a result with the keyword await.

In that case, the code after the call can be programmed to use the variable without the worry that it might be null.

Applied to Truffle

To apply the syntax to Truffle, we need to pay attention to 3 changes we will need to make:

  • The contract declaration: When we make a bracket to code the test cases for a contract, we pass the cases inside a function. This function, that "hosts" the test cases will have to be defined as async.
  • The test case: The test case also has a function that is self-executed. This is the function that hosts the logic for the specific test case. It also has to be asynchronous to allow us to make the await calls inside it.
  • The calls: Finally, after we convert the "outer" context, we call make the calls inside each of our test case asynchronous.

To do that, we will have to refactor our code, remove the then, make the self evoking functions async and making the await calls.

Truffle Asynchronous test

Now that you understand how those functions work and when/how/why to use it, let's see how the syntax looks applied to our Truffle test cases.

Remember how we used previously:

contract("Exchange", accounts=>{
it("Should do something", ()=>
    Exchange.deployed().then(instance=>{
        //operations we need
        return instance.getter();
    }).then(returnedVariable=>{
        //assertions and other code
    })
);
});

Here is how we will change the contract description and the test case description, in order for. us tube able to call async functions inside it:

contract("Exchange", async accounts=>{
it("Should do something", async ()=>

);
});

If you just add the keyword async on the functions, the code will still work exactly as before:
Captura de Tela 20190317 às 14.51.15.png

But we are still using .then in order to wait for the blockchain results!

When you remove the .then you won't be working with return, but you will be storing the instances, variables and returned values in variables you create in JavaScript. Just like you work with on the contract deployment.

Example of a test using async/await

Here is an example from the exchange. The test case to check if the user has allowed the exchange to spend from his token balance:

it("Should allow exchange to spend user balance", async ()=>
    Erc20.deployed().then(instance=>{
        instance.approve(exchangeAddress, 100);
        return instance.allowance(accounts[0], exchangeAddress);
    }).then(exchangeAllowance=>{
        assert.equal(exchangeAllowance.valueOf(), 100, "the exchange is not allowed to move 100 tokens");
    })
);

Most of the code there is not necessary if we use the async keyword. We can create a variable and assign an instance to it:

let instance = Erc20.deployed();

But the await isn't just to assign values to variables. We can call the await to a function to make the code wait for the execution to complete. This is a helpful feature for the next step. We have created the instance, now we need to call its methods. In this case, the method to approve the exchange to spend 100 tokens from the user balance, so:

await instance.approve(exchangeAddress, 100);

And finally, instead of passing the returned value to another function on the then and executing another function there, we can make a call to the exchange and stored the returned value. Again, we tell JavaScript to wait until we get a result, else we will compare to an empty promise:

let exchangeAllowance = await instance.allowance(accounts[0], exchangeAddress);

The assertion doesn't change as we have named our variables with the same names as on the other method.

The final code for this test case converted to use async/await looks like:

it("Should allow exchange to spend user balance", async ()=>{
    let instance = await Erc20.deployed();
    await instance.approve(exchangeAddress, 100);
    let exchangeAllowance = await instance.allowance(accounts[0], exchangeAddress);
    assert.equal(exchangeAllowance.valueOf(), 100, "the exchange is not allowed to move 100 tokens");
});

And it gives us the expected result:
Captura de Tela 20190317 às 15.03.31.png

The code is shorter and cleaner, and that is an advantage. The disadvantage is that the code takes almost twice as long to run! It took 125ms to run, while before it took under 80ms!

Curriculum

Last 2 tutorials:

The first class of the series:

Beneficiaries

This post has as beneficiaries

using the SteemPeak beneficiary tool:
captura4.png

Sort:  

Thank you for your contribution @igormuba.
After reviewing your tutorial we suggest the following points listed below:

  • Again an excellent tutorial presented to the open source community.

  • In this your contribution you put fewer screenshots.

  • The structure of your tutorial is very well, the reader can have a clear reading and understand well what you are explaining.

Thank you for your work in developing this tutorial.
Looking forward to your upcoming tutorials.

Your contribution has been evaluated according to Utopian policies and guidelines, as well as a predefined set of questions pertaining to the category.

To view those questions and the relevant answers related to your post, click here.


Need help? Chat with us on Discord.

[utopian-moderator]

Thank you for your review, @portugalcoin! Keep up the good work!

Hi, @igormuba!

You just got a 0.33% upvote from SteemPlus!
To get higher upvotes, earn more SteemPlus Points (SPP). On your Steemit wallet, check your SPP balance and click on "How to earn SPP?" to find out all the ways to earn.
If you're not using SteemPlus yet, please check our last posts in here to see the many ways in which SteemPlus can improve your Steem experience on Steemit and Busy.

Hi @igormuba!

Your post was upvoted by @steem-ua, new Steem dApp, using UserAuthority for algorithmic post curation!
Your post is eligible for our upvote, thanks to our collaboration with @utopian-io!
Feel free to join our @steem-ua Discord server

Hey, @igormuba!

Thanks for contributing on Utopian.
We’re already looking forward to your next contribution!

Get higher incentives and support Utopian.io!
Simply set @utopian.pay as a 5% (or higher) payout beneficiary on your contribution post (via SteemPlus or Steeditor).

Want to chat? Join us on Discord https://discord.gg/h52nFrV.

Vote for Utopian Witness!

Coin Marketplace

STEEM 0.19
TRX 0.15
JST 0.029
BTC 63061.76
ETH 2602.70
USDT 1.00
SBD 2.75