Unit Testing in Truffle

Thank you, Gabba, for your replies. I very much appreciate them. In your first reply you seem to be saying that the method I used (after searching the internet) really does not work anymore and that I really have to use a completely different route, using events. Is that correct? Furthermore, in light of the videos that I watched recently, I was wondering whether the standard method for generating transaction ID’s might involve the use of contract instances. Filip explained how to generate and display transaction hashes in one of his videos.

As far as the code is concerned, I simply took the block that was duplicated and put it intio another async function. First I tried just a regular function, but I was told that the “await” command necessitated the use of async. So I put in async, but then the program would simply stop executing the code and enter into a waiting mode after displaying the last person in the first run of the function. This entering into a pending state caused me to think that there is a problem with asynchronicity (an async function within an async function).

async function addPersonLoop(loopNumber){
for(n=0; n<loopNumber; n++){
let creatorAddress = await instance.getCreators(n);
let person = await instance.getPerson(creatorAddress);
console.log(“Person #” + n +": name: " + person.name + "; age: " + person.age + ";
address: " + creatorAddress + “\n”);
}
)

Finally, concerning your third reply, my observation was that the mapping still worked after a person had been deleted but that the person values were all zero or null. This is not surprising because there is no pop-array command in the contract, but it makes sense as you said to insert such a command in order to save space.

1 Like

Thanks @Taha and @gabba for the answers and help.

1 Like

that makes sense, thank you @FrankB FrankB

1 Like

Hi @FrankB

A transaction id will be generate when you execute a transaction, sending eth to the blockchain, writing data on your contract storage or executing an operation need a transaction so you will have to pay fees to execute this action.

On the web3js point of view you will call

instance.methods.myFunction.send()

You will have a tx_receipt, inside this receipt you can have access to logs if an event is emitted during this transaction you will see it in the receipt.

If you just want to read the blockchain you will do a call(), when you are doing a call you can get a value back.

instance.methods.getValue.call()

I don’t understand why you are creating and deleting persons in the migration file.
What is the purpose to do that in the migration file instead of the test file ?

Why are you deleting the last account ?

await instance.deletePerson(accounts[loopNumber-1]);

Your function should work if you call it with an await:
await addPersonLoop(number);

The mapping will always return 0 at any key because by default the value of a mapping are set to zero.
The advantage of a mapping over an array if you remove a value:

Pseudo code
new array[3] -> value0 / value1 / value2
new mapping(3) -> value0 / value1 / value2

If you delete the value 1 both will look like this:
new array[3] -> value0 / Empty / value2 -> size 3
new mapping(3) -> value0 / value2 -> size 2

Let me know if it’s not clear i ll try to give you a better explanation :slight_smile:

1 Like

Thank you so much—adding in another await makes sense. I’ll try that. There was no reason for adding all those commands into the migrations file other than getting practice and convincing myself that it all works the way I thought it should work. That’s how I learn. When I just try something there is always a possibility that some problem comes up and that in trying to solve it I learn something new. For example, due to the fact that I modified the migrations file, I just learned from you how to solve the functions problem that I mentioned in my post. It’s all just about experimentation. And thanks also for the additional information on the transaction issue—I very much appreciate your detailed reply.

1 Like

Hi @gabba,
good question :slight_smile: . I only later on realised calling getBalance() before and after the withdrawal would have the same effect. Because I hadn’t really understood that each transaction is immediately added to a block in truffle. So I went on to research how I could make sure I’m querying consecutive blocks. In the end I just stuck with this solution because it was more obvious for me what is happening.

1 Like
const People = artifacts.require("People");

const truffleAssert = require("truffle-assertions");

contract("People",
  async function (accounts)
  {
      it("Should allow owner to delete person",
        async function()
        {
          let instance = await People.deployed();
          instance.createPerson("Bob", 48, 190,
            {value : web3.utils.toWei("1", "ether"), from: accounts[0]});

          await truffleAssert.passes(instance.deletePerson(accounts[0],
              { from: accounts[0]}));
        });

        it("Should stop non owner to delete person",
          async function()
          {
            let instance = await People.deployed();
            //instance.createPerson("Bob", 48, 190,
            //  {value : web3.utils.toWei("1", "ether"), from: accounts[0]});

            console.log('Owner:'+accounts[1]);

            await truffleAssert.fails(instance.deletePerson(accounts[0],
              { from: accounts[1]}));

          });
});
1 Like

Hey @gabba, @filip!
I’m haveing trobble geting the two addreses too match, (Gannage and Truffle). I’m pretty sure that my code is correct however perhaps I need to change it up a bit.

Here’s my code:

it("Should increase contract balance when person is created", async function(){
    await instance.createPerson("Bob Billings", 50, 6, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
    let balanceTruffle = await instance.balance();
    let balanceGanache = await web3.eth.getBalance("0x4242a40972BC3fFbdc3558C1C94047D3D2f3835e");
    assert(balanceTruffle == balanceGanache, "Balance's do not match");
  });

This is the errer I’m getting:


Hope you can help!
Thanks!

Edit:
Never mind. I discoved that I don’t have to use a hard coded address. Now it works!

1 Like

Hey @gabba, @filip!
I am trying to find out how to find the contract owner’s address.
I’m guessing that it’s in ganache however I can’t find it. Of course it might be somewhere else.
Please help!

Edit:
Never Mind!
I’ve been looking at other people’s ansers and now it make’s sens to me that accout 0 would be the contract owner.

1 Like
// This test makes sure that the contract balance and the block chain balance match.
  it("Should increase contract balance when person is created", async function(){
    await instance.createPerson("Bob Billings", 50, 6, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
    let balanceTruffle = await instance.balance();
    let balanceGanache = await web3.eth.getBalance(instance.address);
    assert(balanceTruffle == balanceGanache, "Balance's do not match");
  });


  it("Should let the owner withdrawAll", async function(){
    await instance.withdrawAll({from: accounts[0]});
    await truffleAssert.passes(instance.withdrawAll({from: accounts[0]}));
  });
});

  it("Shouldn't allow a nonowner to withdrawAll", async function(){
    await truffleAssert.fails(instance.withdrawAll({from: accounts[3]}), truffleAssert.ErrorType.REVERT);
  });
});

1 Like

@JRB
Great to see you progress so much!
Do you have a question here?

1 Like

Hey @Taha!
Nope! Not right now.
Thanks for asking!

1 Like
const People = artifacts.require("People");
const truffleAssert = require("truffle-assertions");

contract("People", async function(accounts) {

    it("shouldn't delete person if not called by contract owner", async function() {
        let instance = await People.deployed();
        await instance.createPerson("Bob", 65, 190, {value:web3.utils.toWei("1", "ether"), from:accounts[1]});
        await truffleAssert.fails(instance.deletePerson(accounts[1], {from:accounts[1]}), truffleAssert.ErrorType.REVERT);
    });

    it("should delete person if called by contract owner", async function() {
        let instance = await People.deployed();
        await instance.createPerson("Bob", 65, 190, {value:web3.utils.toWei("1", "ether"), from:accounts[1]});
        await truffleAssert.passes(instance.deletePerson(accounts[1], {from:accounts[0]}));
    });
})

Editted by @gabba : When you are sharing code please use the preformatted text tag @leemac :wink:

2 Likes

Hi All,

Im a bit stuck with this, as far as I can tell this test should fail but it passes. Any ideas?


it("shouldn't be possible to withdraw except for the contract owner",
            async function()
            {
              let instance = await People.deployed();
              await instance.createPerson("Bob", 48, 190,
                  {value : web3.utils.toWei("1", "ether"), from: accounts[1]});
              await truffleAssert.fails(instance.withdrawAll(), { from: accounts[2]});
            });

Output:

AssertionError: Did not fail
      at fails (node_modules/truffle-assertions/index.js:161:9)
      at runMicrotasks (<anonymous>)
      at processTicksAndRejections (internal/process/task_queues.js:97:5)
      at Context.<anonymous> (test/PeopleTest.js:66:15)

Thanks,
Steve.

Hey @filip,
could you explain in which testing cases I use the truffleAssert statement ?
I dont really get it.

Thanks in advance! :slight_smile:

If anyone else has similar issue it was :

instance.withdrawAll(), { from: accounts[2]} <- wrong
instance.withdrawAll( {from: accounts[2] } <- right
:roll_eyes:

it("Should increase balance by 1 ether when person is created",
            async function()
            {
              let instance = await People.deployed();

              let oldBalance = await web3.eth.getBalance(People.address)
              await instance.createPerson("Bob", 48, 190,
                {value : web3.utils.toWei("1", "ether"), from: accounts[1]});

              let newBalance = await web3.eth.getBalance(People.address);

              assert(parseInt(newBalance) === parseInt(oldBalance) + parseInt(web3.utils.toWei("1", "ether")),
                "New Balance 1 more Eth than old");
            });

          it("shouldn't be possible to withdraw contract funds exept for the contract owner",
            async function()
            {
              let instance = await People.deployed();
              await instance.createPerson("Bob", 48, 190,
                  {value : web3.utils.toWei("1", "ether"), from: accounts[0]});
              await truffleAssert.fails(instance.withdrawAll({ from: accounts[1]}));
            });

          it("shoud be possible to withdraw contract funds just for the contract owner",
            async function()
            {
              let instance = await People.deployed();
              let instanceBalance = await instance.balance();
              await truffleAssert.passes(instance.withdrawAll({ from: accounts[0]}));
              let ownerBalance = await web3.eth.getBalance(accounts[0]);
              assert (parseInt(instanceBalance) === parseInt(instanceBalance));
            });

          it("should now have zero balance",
              async function()
              {
                let instance = await People.deployed();
                let blockChainBalance = await web3.eth.getBalance(People.address)
                let instanceBalance = await instance.balance();

                console.log(':'+instanceBalance+':');
                console.log(':'+blockChainBalance+':');

                assert (parseInt(instanceBalance) === parseInt(blockChainBalance));
                assert (parseInt(instanceBalance) === 0);
              });
1 Like

@sahowe1
Great work! you did it by yourself :slight_smile:

1 Like

Hi @sherlock

What do you exactly mean ?
You will use the truffleAssert statement in every tests.

If the test should succeed you will use

await truffleAssert.passes(true , true)

otherwise

await truffleAssert.fails(true , false)
1 Like
    it("should not delete a person if it is not owner of the contract", async function() {
        let instance = await People.deployed();

        await instance.createPerson("Evil Juan", 22, 140, {
            value: web3.utils.toWei("1", "ether"),
            from: accounts[0]
        });

        await truffleAssert.fails(instance.deletePerson(accounts[0], { from: accounts[1]}))
    });

    it("should delete a person if it is owner of the contract", async function() {
        let instance = await People.deployed();

        await instance.createPerson("Good Juan", 22, 140, {
            value: web3.utils.toWei("1", "ether"),
            from: accounts[0]
        });

        await truffleAssert.passes(instance.deletePerson(accounts[0], { from: accounts[0]}))
    });
1 Like
it("should increse the balance", async function(){
    let instance = await People.deployed();
    let balance = await web3.eth.getBalance(People.address);
    await instance.createPerson("Donald", 33, 189, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
    let newBalance = await web3.eth.getBalance(People.address);
    await truffleAssert.passes(newBalance > balance, "Transaction failed");
  });
  it("shouldnt be withdrawed by anyone but owner", async function(){
    let instance = await People.deployed();
    await instance.createPerson("Mini", 44, 188, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
    await truffleAssert.fails(instance.withdrawAll({from: accounts[1]}));

  });
  it("should be withdrawed by owner", async function(){
    let instance = await People.deployed();
    await instance.createPerson("Mini", 44, 188, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
    await truffleAssert.passes(instance.withdrawAll({from: accounts[0]}));
    let finalBalance = await web3.eth.getBalance(People.address);
    await assert(parseInt(finalBalance) === 0, "Withdraw failed");
  });
1 Like