Unit Testing in Truffle

// SECOND ASSIGNMENT ON TESTS WITH TRUFFLE AND ASSERTIONS
// VERIFYING PAYMENTS

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

    let instance;
    let contractBalance;
    let ownerBalance;
    let senderBalance;
    let ownBalBeforeWithdrawal;
    let ownBalAfterWithdrawal;

    before(async function(){
      instance = await People.new();
      ownerBalance = await web3.eth.getBalance(accounts[0]);
      senderBalance = await web3.eth.getBalance(accounts[3]);
    });

    it("should initialize with contract's balance in 0 Wei", async function(){
      contractBalance = await web3.eth.getBalance(instance.address);;
      assert(parseInt(contractBalance) === 0,
            "Contract's balance is different from 0 Wei right after deployment");
    })

    it("should increase contract balance in 1 ether after adding a person", async function(){
      await instance.createPerson("Lucy", 22, 180, {value: web3.utils.toWei("1", "ether")});
      contractBalance = await web3.eth.getBalance(instance.address);
      assert(parseInt(contractBalance) === parseInt(web3.utils.toWei("1", "ether")), // 1000000000000000000,
            "Contract's balance has not been credited 1 ether after adding a person");
    });

    it("should keep the exact same balance in internal variable balance as in contract's balance", async function(){
      let internalBalance = await instance.balance();
      assert(parseInt(internalBalance) === parseInt(contractBalance),
            "Internal variable balance is not equal to contract's balance");
    });

    it("should revert attemp by non-owner to withdrawal funds from contract's balance", async function(){
      await truffleAssert.fails(instance.withdrawAll({from: accounts[3]}), truffleAssert.ErrorType.REVERT);
    });

    it("should keep contract balances in 1 ether after failed withdrawal attempt", async function(){
      contractBalance = await web3.eth.getBalance(instance.address);
      let internalBalance = await instance.balance();
      assert(parseInt(contractBalance) === parseInt(web3.utils.toWei("1", "ether")) &&
             parseInt(internalBalance) === parseInt(web3.utils.toWei("1", "ether")),
            "Balances in contract have changed after non-owner's withdrawal attempt");
    });

    it("should allow the owner to withdraw funds from contract's balance", async function(){
      ownBalBeforeWithdrawal = await web3.eth.getBalance(accounts[0]);
      await truffleAssert.passes(instance.withdrawAll({from: accounts[0]}));
    });

    it("should have reseted contract balances to zero after owner full withdrawal", async function(){
      contractBalance = await web3.eth.getBalance(instance.address);
      let internalBalance = await instance.balance();
      assert(parseInt(contractBalance) === 0 &&
             parseInt(internalBalance) === 0,
            "Balances in contract have not been reseted after owner's withdrawal");
    });

    it("should have increased owner's balance after withdrawal from contract", async function(){
      ownBalAfterWithdrawal = await web3.eth.getBalance(accounts[0]);
      assert(parseInt(ownBalAfterWithdrawal) > parseInt(ownBalBeforeWithdrawal),
             "Owner's balance has not increased after withdrawal from contract");
    });

  });

1 Like

Okay, so I just watched the “Owner Test Assignment Solution” video, because I couldn’t figure it out on my own, mostly because I didn’t know about the concept of using “accounts”. But my first thought when I was planing this assignment was to use msg.sender = owner function, as that made most sence to me, we would just include the Ownable.sol file on the top of the peopletest.js file and that way test if the address of the person who deletes people matches with the owner. But when I constructed that test function I got an error (msg.sender not defined), even tho I linked everything correctly and made a ownable migration file etc.

So my question is, why did it not work? And if it could work somehow, would it be simpler than writing 2 tests? It would kill 2 birds with 1 stone, right?

Hi @Glenn_CostaRica

As you are using a lot of variable to track the current state of your contract, i don’t think you need to deploy a new instance for each test.


  before(async function(){
    instance = await People.new();
    ownerBalance = await web3.eth.getBalance(accounts[0]);
    senderBalance = await web3.eth.getBalance(accounts[3]);
  });

This will execute this action before each test ‘it’ .
But by the way you have designed your test you can just initialize your instance in the first test.
Your tests will cost less to executes than creating a new instance for each test.

2 Likes

Hi @Djuzy_J
Your People contract is already inheriting from the ownable contract you don’t have to import it in the test.
But default an array of accounts are inject by the truffle framework, also by default the first account of this array will be used to deploy the contract, so accounts[0] will be the owner of the contract.

So at the beginning of your test file you just have to declare a variable owner = accounts[0] if you want to use it later.

If you want to change it read my previous post

https://forumtest.ivanontech.com/t/unit-testing-in-truffle/10053/189

In your test msg.sender is not defined because you are mixing .sol and .js files which are not using the same language.
msg is an object available in your smart contract methods not in a javascript file.

If you still have issues can you share your test with us ?

2 Likes

@gabba Man, thank you for this check :facepunch::pray:
There is one thing I still do not understand well. I think I misunderstood how the before() function works. I thought that if I instantiated " instance = await People.new(); " inside the BEFORE function, the instance would be created only once before all the “it” tests. With this code, am I creating a new instance in each “it” ???

  before(async function(){
    instance = await People.new();
...
}

This is not my intention !! On purpose, I had not used a beforeEach() in order to avoid that. But, I guess I am not understanding well how to use the before() function… Should I declare and instantiate like this --> let instance = await People.new() before using the before() function ???
Man, I appreciate all the help :pray:

Oups my bad ahaha i though you used beforeEach :pray: apologies @Glenn_CostaRica :sweat_smile: I don’t know why i was sure it was beforeEach

2 Likes

@gabba Mannn, thanks a lot :blush: :facepunch: :orange_heart: This happens to me ALL THE TIME !! I’m a university professor, and I need to apologize so frequently to my students !!! Can’t keep track of every detail sometimes… Man, I love how dedicated you, guys, are! You read each homework, each detail! You give comments and directions to each student!! This Academy is amazing :punch::muscle: Love from Costa Rica :costa_rica: :orange_heart:

1 Like

Thx @Glenn_CostaRica we are glad to have amazing student like you here :+1:

2 Likes

Hey
when i try to run the test in Truffle I get this error, anyone know how to solve this? In my attempt to solve this problem I did copy @filip s code from github, so my code should be ok or? I can read something about this issue might can be due to the version 12 of node, should/could i change it to version 10 or would it mess it up?

Rather try than copy aka. rookie of the year:

it(“Should be owner that delete person”, async function(){
let instance = await People.deployed();
await instance.deletePerson(accounts[0], {value: web3.utils.toWei(“1”, “ether”)});
let result = await instance.getPerson(accounts[0]);
assert(result.accounts[0] === onlyOwner, “Wrong person tried to delete, needs to be owner”);
});

1 Like

Hi @Jacob_M
Yes you need to use node 10 install it with nvm it’s easier
otherwise you can check this post
https://forumtest.ivanontech.com/t/introduction-to-unit-testing/10052/42

1 Like

Owner test:

contract("PeopleAccnt1", async function(accounts) {
  it ("should create person", async function() {
    let instance = await People.deployed();
    await instance.createPerson("Bob", 65, 190, {value: web3.utils.toWei("1", "finney"), from: accounts[0]});
    let result = await instance.getPerson();
    assert(result.age.toNumber() === 65, "Age was not set correctly");
  });
});

contract("PeopleAccnt2", async function(accounts) {
  it ("shouldn't delete person", async function() {
    let instance = await People.deployed();
    await truffleAssert.fails(instance.deletePerson(accounts[1], {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
  });
});
1 Like

hey @filip i am getting following error

Using network ‘ganache’.

Compiling your contracts…

Compiling .\contracts\Migrations.sol
Compiling .\contracts\Ownable.sol
Compiling .\contracts\People.sol
Artifacts written to C:\Users\gandh\AppData\Local\Temp\test-2020425-19132-1h3o3su.gzdd
Compiled successfully using:

  • solc: 0.5.12+commit.7709ece9.Emscripten.clang

Contract: People
1) should’t create a person with age over 150 years
> No events were emitted
2) shouldn’t create a person without payment
> No events were emitted
3) should set senior status correctly
> No events were emitted

0 passing (180ms)
3 failing

  1. Contract: People
    should’t create a person with age over 150 years:
    AssertionError: Expected to fail with [object Object], but failed with: Error: Returned error: VM Exception while processing transaction: revert
    at fails (node_modules\truffle-assertions\index.js:152:13)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)
    at Context. (test\peopletest.js:7:5)

  2. Contract: People
    shouldn’t create a person without payment:
    ReferenceError: instance is not defined
    at Context. (test\peopletest.js:10:31)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

  3. Contract: People
    should set senior status correctly:
    ReferenceError: instance is not defined
    at Context. (test\peopletest.js:13:5)
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

3

Hi @Gandharv_dalal can you share your test file with us ?

those errors are pretty straightforward

1 - AssertionError: Expected to fail with [object Object], but failed with: Error: Returned error: VM
You are using an object in you assert instead of an error code.

2/3 - ReferenceError: instance is not defined
instance is not defined -> it means that instance.createPeople can’t be called because instance doesn’t exist. How are you deploying your instance ? Are you doing the same thing as in the video ?

2 Likes
const People = artifacts.require("People");
const truffleAssert = require("truffle-assertions");

contract("People", async function (accounts)
{
	it("Should not create a person with age over 150 years", async function() {
		let instance = await People.deployed();
		await truffleAssert.fails(instance.createPerson("Bob",200,190, {value: web3.utils.toWei("1", "ether")}), truffleAssert.ErrorType.REVERT);		
	});
	it("Should not create a person without payment", async function(){
		let instance = await People.deployed();
		await truffleAssert.fails(instance.createPerson("Alice",23,200,{value: "1000" }), truffleAssert.ErrorType.REVERT);
	});
	it("Should set senior status correctly", async function() {
		let instance = await People.deployed();
		await instance.createPerson("Bob",65,190, {value: web3.utils.toWei("1", "ether")});
		let result = await instance.getPerson();
		assert(result.senior === true, "Senior Level Not Set");
	});
	it("Should set age correctly", async function(){
		let instance = await People.deployed();
		let result = await instance.getPerson();
		assert(result.age.toNumber()===65, "Age not set correctly");
	});
	it("Should not allow users other than the owner to delete and should delete correctly when owner deletes", async function(){
		let instance = await People.deployed();
		let owner = accounts[0];
		let user1 = accounts[1];
		let user2 = accounts[2];

		await instance.createPerson("Bob",65,190, {value: web3.utils.toWei("1", "ether"), from:user1});
		await instance.createPerson("Alice",25,150, {value: web3.utils.toWei("1", "ether"), from: user2});
		await truffleAssert.fails(instance.deletePerson(user2,{from: user1}), truffleAssert.ErrorType.REVERT);

		await instance.deletePerson(user2,{from:owner});
		let result = await instance.getPerson({from:user2});
		assert(result.age.toNumber()===0, "Age not set correctly when owner deletes");

	});
	
});

Combined the test into one test. It is the last “it” command.
Please let me know if this is good/accurate.
Thank you!

1 Like

Value Assignment Solution

it("Should allow only the owner to withdraw", async function(){
		let instance = await People.deployed();
		let owner = accounts[0];
		let user1 = accounts[1];
		let user2 = accounts[2];

		await truffleAssert.fails(instance.withdrawAll({from: user1}), truffleAssert.ErrorType.REVERT);

	});
	it("Should update the contract balance to 0 after withdrawing", async function(){
		let instance = await People.new();
		let owner = accounts[0];
		let user1 = accounts[1];
		let user2 = accounts[2];

		await instance.createPerson("Bob",65,190, {value: web3.utils.toWei("1", "ether"), from: user1});
		await instance.createPerson("Alice",25,150, {value: web3.utils.toWei("1", "ether"), from: user2});
		await instance.withdrawAll();
		
		let newBalance = await instance.balance();
		newBalance = parseFloat(newBalance);
		assert(newBalance == 0, "Post withdrawal balance is incorrect");

	});

	it("Should allow the owner to withdraw from contract", async function(){

		let instance = await People.new();
		let owner = accounts[0];
		let user1 = accounts[1];
		let user2 = accounts[2];

		await instance.createPerson("Bob",65,190, {value: web3.utils.toWei("1", "ether"), from: user1});
		await instance.createPerson("Alice",25,150, {value: web3.utils.toWei("1", "ether"), from: user2});
		await truffleAssert.passes(instance.withdrawAll({from:owner}));

	});

	it("Should update the owner's balance after a successful withdrawal", async function(){
		let instance = await People.new();
		let owner = accounts[0];
		let user1 = accounts[1];
		let user2 = accounts[2];
		let oldBalance = parseFloat(await web3.eth.getBalance(accounts[0]));
		
		await instance.createPerson("Bob",65,190, {value: web3.utils.toWei("1", "ether"), from: user1});
		await instance.createPerson("Alice",25,150, {value: web3.utils.toWei("1", "ether"), from: user2});
		await instance.withdrawAll();

		let newBalance = parseFloat(await web3.eth.getBalance(accounts[0]));

		assert(oldBalance < newBalance , "New balance is not greater than the old balance");

	});
1 Like
  it("Should set owner correctly", async function(){
    let instance = await People.deployed();
    let result = await instance.owner.call();

    assert(result == accounts[0], "Owner was not set correctly");
  });
  it("Should allow owner to call owner-only functions", async function(){
    let instance = await People.deployed();
    await truffleAssert.passes(
      instance.getCreator(0)
    );
  });
  it("Should not allow normal users to call owner-only functions", async function(){
    let instance = await People.deployed();
    await truffleAssert.fails(
      instance.getCreator(0, {from: accounts[1]}),
      truffleAssert.ErrorType.REVERT
    );
  });
  it("Should add funds to balance correctly", async function(){
    let transactionValue = web3.utils.toWei("2", "Ether");
    await instance.createPerson(
      "Bob",
      20,
      180,
      {value:transactionValue, from: accounts[1]}
    );
    let contractBalance = await web3.eth.getBalance(instance.address);
    let internalBalance = await instance.balance();
    assert(contractBalance == internalBalance, "contract balance is wrong");
  });
  it("Should update contract balance to 0 after withdrawing", async function(){
    await instance.withdrawAll();
    let newBalance = await instance.balance.call();

    assert(newBalance == 0, "contract balance after withdrawl is wrong");
  });

Hi @anton6

How do you know that this test is correct ?

  it("Should add funds to balance correctly", async function(){
    let transactionValue = web3.utils.toWei("2", "Ether");
    await instance.createPerson(
      "Bob",
      20,
      180,
      {value:transactionValue, from: accounts[1]}
    );
    let contractBalance = await web3.eth.getBalance(instance.address);
    let internalBalance = await instance.balance();
    assert(contractBalance == internalBalance, "contract balance is wrong");
  });

If no funds are sent to you contract
0 === 0 will succeed
You are not checking that 2 ETH have been sent

2 Likes

Thank you! :grinning:

const People = artifacts.require(“People”);
const truffleAssert = require(“truffle-assertions”);

contract(“Ownable”, async function(accounts){
it(“Should only delete if owner”, async function() {
let instance = await People.deployed();

    //create a person with the first account
    await instance.createPerson("edson", 123, 180, {value: web3.utils.toWei("1", "ether"), from: accounts[0]});

    //try to delete first account, with second account. Shouldn't be allowed
    await truffleAssert.fails(instance.deletePerson(accounts[0], {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
});

it("Should delete if owner", async function() {
    let instance = await People.deployed();

    //Delete person created above by using first account which is owner.
    await truffleAssert.passes(instance.deletePerson(accounts[0], {from: accounts[0]}));
})

})

1 Like