Unit Testing in Truffle

VALUE ASSIGNMENT TEST (second test)

here is my code for checking if a non-owner can make a withdrawal, and if the owner can make a withdrawal:

it("should not allow non-owner to make withdrawal", async function(){
let instance = await People.new();
await instance.createPerson("Bob", 65, 190, {from: accounts[1], value: web3.utils.toWei("1", "ether")});

await truffleAssert.fails (instance.withdrawAll( {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
});

it("should allow owner to make withdrawal", async function(){
let instance = await People.new();
await instance.createPerson("Bob", 65, 190, {from: accounts[1], value: web3.utils.toWei("1", "ether")});

await truffleAssert.passes (instance.withdrawAll( {from: accounts[0]}), truffleAssert.ErrorType.REVERT);
})
1 Like

VALUE ASSIGNMENT TEST (third test)

here is my code for checking that the contract balance is zero after a withdrawal, and that it matches the blockchain balance:

it("should check to make sure contract balance is zero after owner withdrawals", async function(){
let instance = await People.new();
await instance.createPerson("Bob", 65, 190, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
await truffleAssert.passes (instance.withdrawAll( {from: accounts[0]}), truffleAssert.ErrorType.REVERT);

let newContractBalance = await instance.balance();
let newBlockchainBalance = await web3.eth.getBalance(instance.address);

assert(newContractBalance == "0");
assert(newContractBalance == newBlockchainBalance);
})

VALUE ASSIGNMENT TEST (fourth & final test)

here is my code for checking that the owner’s balance increases after a withdrawal:

it("should check if Owner's account balance increased after withdrawal", async function(){
let instance = await People.new();
await instance.createPerson("Bob", 65, 190, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
let oldBalance = await web3.eth.getBalance(accounts[0]);
await instance.withdrawAll();
let newBalance = await web3.eth.getBalance(accounts[0]);
assert(oldBalance < newBalance), "Owner balance has not increased";
})

Again, after checking Filip’s solution, I see he used parseFloat and I didn’t.
I posted each “group” of solution tests, as I saw them, to show how my thinking developed.

Value Assignment:

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

contract("People", async function(accounts){
  let instance;
  before(async function(){
    instance = await People.deployed();
  });
  it("shouldn't create a persion with age above 150", async function(){
    // BEFORE EACH RUNS ON TOP
    await truffleAssert.fails(instance.createPerson("Bob", 200, 190, {value: web3.utils.toWei("1", "ether")}), truffleAssert.ErrorType.REVERT);
  });

  it("shouldn't create a person without payment", async function(){
    await truffleAssert.fails(instance.createPerson("Bob", 50, 190, {value: 1000}), truffleAssert.ErrorType.REVERT);
  });

  it("should set senior status correctly", async function(){
    await instance.createPerson("Superman", 90, 190, {value: web3.utils.toWei("1", "ether")});
    let boolean = await instance.getPerson();
    assert(boolean.senior === true, "Senior level has been set.");
  });

  it("shouldn't delete the person.", async function(){
    await instance.createPerson("Superman", 42, 190, {value: web3.utils.toWei("1", "ether"), from: accounts[4]});
    await truffleAssert.fails(instance.deletePerson(accounts[4], {from: accounts[2]}), truffleAssert.ErrorType.REVERT);
  });

  it("should delete the person.", async function(){
    await instance.createPerson("Batman", 45, 187, {value: web3.utils.toWei("1","ether"), from: accounts[1]});
    await truffleAssert.fails(instance.deletePerson(accounts[1], {from: accounts[0]}), truffleAssert.ErrorType.REVERT);
  });

  it("should correctly check the contract's balance is correctly increased", async function(){
    let instance = await People.new();
    await instance.createPerson("Flash", 23, 189, {from: accounts[4], value: web3.utils.toWei("1","ether")});
    let addedBalance = await instance.balance();
    let ganacheBalance = await web3.eth.getBalance(instance.address);
    assert(addedBalance == ganacheBalance, "Incorrect balance is being added, added balance does not match.");
    assert(addedBalance == web3.utils.toWei("1", "ether"));

  });

  it("shouldn't widthdraw", async function(){
   let instance = await People.new();
   await instance.createPerson("Green Lantern", 37, 185, {value: web3.utils.toWei("1","ether"), from: accounts[2]});
   await truffleAssert.fails(instance.withdrawAll({from: accounts[1]}), truffleAssert.ErrorType.REVERT);
  });
  it("should widthdraw", async function(){
   let instance = await People.new();
   await instance.createPerson("Doomsday", 2, 3, {value: web3.utils.toWei("1","ether"), from: accounts[2]});
   await truffleAssert.fails(instance.withdrawAll({from: accounts[0]}), truffleAssert.ErrorType.REVERT);
  });



});
1 Like

Hey @Jethrodog

Smart contracts do not handle floating point numbers therefore there is no need to use ParseFloat().
Your tests will work both with parseFloat or just by using an integer number(example: 10.00 == 10)

As you know already, in order to represent 0.1 ether in Solidity you will use 10 ** 17 :slight_smile:

Cheers,
Dani

Thanks dan-i, I appreciate the explanation.
Ben

1 Like

contract(“People”, async function(accounts){

//Create a person
it(“Shouldn’t set senior correctly”, async function(){
let instance = await People.deployed();
await instance.createPerson(“Test”, 65,190,{value: web3.utils.toWei(“1”, “ether”)});
let result = await instance.getPerson();
assert(result.senior === true , “Senior level not set”);
});

it(“Shouldn’t be able to delete person”, async function(){
let instance = await People.deployed();
await truffleAssert.fails(instance.deletePerson(accounts[1],{from: accounts[1]}), truffleAssert.ErrorType.REVERT);
await instance.deletePerson(accounts[0]);
});

it(“It Should delete person”, async function(){
let instance = await People.deployed();
await instance.deletePerson(accounts[0]);
});

});

1 Like

Hi, can anybody tell me what am i missing here:

Smart contract function:


The test:

The test not passing:

If i use msg.value < address(this).balance, and then in the test i send value: balance. It works, but im dont understand what is going on. Something about different units maybe ?

Hey @ArgenFrank

Can you console.log(balance) and post the result?

thanks

I have a question. I’m testing onlyOwner modifier. Some of the tests are not failing, but I believe they should. When I’m running contract in Remix, I can emit delete event from already created account on addresses that do not assigned to anyone yet. So, in other words, “address creator” and “deletedBy” are two different addresses and modifier “onlyOwner” fails. What am I missing?

const { iteratee } = require("lodash");
const truffleAssert = require("truffle-assertions");

const People = artifacts.require("People");
let instance;
contract("First test", async (accounts) => {
  beforeEach(async () => {
    instance = await People.deployed();
    await instance.createPerson("Test", 20, 120, {
      value: web3.utils.toWei("1", "ether"),
      from: accounts[0],
    });
  });
  it("Account deletion by owner only", async () => {
    await truffleAssert.passes(
      instance.deletePerson(accounts[0], { from: accounts[0] }),
      "This method should pass"
    );
  });
  it("1. Deletion of created account from noncreated", async () => {
    await truffleAssert.fails(
      instance.deletePerson(accounts[0], { from: accounts[1] }),
      truffleAssert.ErrorType.REVERT,
      null,
      "Ownership test fails"
    );
  });
  it("2. Deletion of noncreated account from created", async () => {
    await truffleAssert.fails(
      instance.deletePerson(accounts[1], { from: accounts[0] }),
      truffleAssert.ErrorType.REVERT,
      null,
      "Ownership test1 fails"
    );
  });
  it("3. Account deletion should fail (noncreated accounts)", async () => {
    await truffleAssert.fails(
      instance.deletePerson(accounts[3], { from: accounts[1] }),
      truffleAssert.ErrorType.REVERT,
      null,
      "Ownership test2 fails"
    );
  });
  it("Delete Event should not be emmited", async () => {
    let result = await instance.deletePerson(accounts[1], {
      from: accounts[0],
    });
    truffleAssert.eventNotEmitted(result, "personDeleted");
  });
});

Hey @Ivan_Zg

Which test is failing and please post your contracts too so that I can have a look.

Cheers,
Dani

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

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

it("shouldn't be able to delete person", async function(){
      let instance = await People.deployed();
      await instance.createPerson("Bob", 65, 190, {value: web3.utils.toWei("1", "ether"), from: accounts[0]});
      await truffleAssert.fails(instance.deletePerson(accounts[0],{from:accounts[1]}));
 });

});

Test #2 and the last one . They should fail, but don’t. Delete event should not be emitted, I think.

pragma solidity 0.5.12;

contract Ownable{
    address public owner;

    modifier onlyOwner(){
        require(msg.sender == owner,"The onlyOwner worked");
        _; //Continue execution
    }

    constructor() public{
        owner = msg.sender;
    }
}

contract People is Ownable{

    struct Person {
      uint id;
      string name;
      uint age;
      uint height;
      bool senior;
    }

    event personCreated(string name, bool senior);
    event personDeleted(string name, bool senior, address deletedBy);

    uint public balance;

    modifier costs(uint cost){
        require(msg.value >= cost);
        _;
    }

    mapping (address => Person) private people;
    address[] private creators;

    function createPerson(string memory name, uint age, uint height) public payable costs(1 ether){
      require(age < 150, "Age needs to be below 150");
      require(msg.value >= 1 ether);
      balance += msg.value;

        //This creates a person
        Person memory newPerson;
        newPerson.name = name;
        newPerson.age = age;
        newPerson.height = height;

        if(age >= 65){
           newPerson.senior = true;
       }
       else{
           newPerson.senior = false;
       }

        insertPerson(newPerson);
        creators.push(msg.sender);

        assert(
            keccak256(
                abi.encodePacked(
                    people[msg.sender].name,
                    people[msg.sender].age,
                    people[msg.sender].height,
                    people[msg.sender].senior
                )
            )
            ==
            keccak256(
                abi.encodePacked(
                    newPerson.name,
                    newPerson.age,
                    newPerson.height,
                    newPerson.senior
                )
            )
        );
        emit personCreated(newPerson.name, newPerson.senior);
    }
    function insertPerson(Person memory newPerson) private {
        address creator = msg.sender;
        people[creator] = newPerson;
    }
    function getPerson() public view returns(string memory name, uint age, uint height, bool senior){
        address creator = msg.sender;
        return (people[creator].name, people[creator].age, people[creator].height, people[creator].senior);
    }
    function deletePerson(address creator) public onlyOwner {
      string memory name = people[creator].name;
      bool senior = people[creator].senior;

       delete people[creator];
       assert(people[creator].age == 0);
       emit personDeleted(name, senior, owner);
   }
   function getCreator(uint index) public view onlyOwner returns(address){
       return creators[index];
   }
   function withdrawAll() public onlyOwner returns(uint) {
       uint toTransfer = balance;
       balance = 0;
       msg.sender.transfer(toTransfer);
       return toTransfer;
   }

}```

Hi @Ivan_Zg

I would like to see the message of the tests when these fail, however I suspect that there is an issue in the way you have coded the truffleAssert assertion error.
Just for the sake of testing please change the test (number two) to this one:

  it("1. Deletion of created account from noncreated", async () => {
    await truffleAssert.fails(
      instance.deletePerson(accounts[0], { from: accounts[1] }),
      truffleAssert.ErrorType.REVERT,
      "The onlyOwner worked"
    );
  });

Please give it a try and post a screenshot of the test result if it fails.

Value Assignment:

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

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

  let instance;

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

  it("should be able to input correct balance", async function(){
    await instance.createPerson("Bob", 65, 190, {value: web3.utils.toWei("1", "ether")});
    let balance = await instance.balance();
    let ethBalance = await web3.eth.getBalance(instance.address);
    assert(balance == ethBalance && balance == web3.utils.toWei("1", "ether"));
  });

  it("should not allow non-owner to withdraw balance", async function(){
      await truffleAssert.fails(instance.withdrawAll({from: accounts[1]}), truffleAssert.ErrorType.REVERT);
  });

  it("should allow owner to withdraw balance", async function(){
      await truffleAssert.passes(instance.withdrawAll());
  });

  it("should decrease contract balance to 0", async function(){
      instance = await People.new();
      await instance.createPerson("Bob", 65, 190, {value: web3.utils.toWei("1", "ether")});
      await instance.withdrawAll();
      let balance = await instance.balance();
      let ethBalance = await web3.eth.getBalance(instance.address);
      assert(balance == ethBalance && balance == web3.utils.toWei("0", "ether"));
  });

  it("should increase owner balance", async function(){
      instance = await People.new();
      await instance.createPerson("Bob", 65, 190, {value: web3.utils.toWei("1", "ether")});
      let initBalance = await web3.eth.getBalance(accounts[0]);
      await instance.withdrawAll();
      let newBalance = await web3.eth.getBalance(accounts[0]);
      assert(parseFloat(initBalance) < parseFloat(newBalance));
  });

});
1 Like

Ok, your test works just fine, but when I switch accounts it does not fail

 Contract: First test
    ✓ dan-i Test. Deletion of created account from noncreated (101ms)
    1) dan-i Test2. Same test, but switched accounts[0] and accounts[1]

    Events emitted during test:
    ---------------------------

    personCreated(name: Test (string), senior: false (bool))
    personDeleted(name:  (string), senior: false (bool), deletedBy: 0xb375f7662ed7391AE5975aabBA67aAc1B35cC2d6 (address))

    ---------------------------


  1 passing (705ms)
  1 failing

  1) Contract: First test
       dan-i Test2. Same test, but switched accounts[0] and accounts[1]:
     AssertionError: Did not fail
      at fails (node_modules/truffle-assertions/index.js:161:9)
      at process._tickCallback (internal/process/next_tick.js:68:7)
1 Like

Hey @Ivan_Zg

Please post the code of the test that fails, I always want to double check and be sure of the code you are running.
I will let you know asap as soon as I read it :slight_smile:

const { iteratee } = require("lodash");
const truffleAssert = require("truffle-assertions");

const People = artifacts.require("People");
let instance;
contract("First test", async (accounts) => {
  beforeEach(async () => {
    instance = await People.deployed();
    await instance.createPerson("Test", 20, 120, {
      value: web3.utils.toWei("1", "ether"),
      from: accounts[0],
    });
  });

  it("dan-i Test. Deletion of created account from noncreated", async () => {
    await truffleAssert.fails(
      instance.deletePerson(accounts[0], { from: accounts[1] }),
      truffleAssert.ErrorType.REVERT,
      "The onlyOwner worked"
    );
  });
  it("dan-i Test2. Same test, but switched accounts[0] and accounts[1]", async () => {
    await truffleAssert.fails(
      instance.deletePerson(accounts[1], { from: accounts[0] }),
      truffleAssert.ErrorType.REVERT,
      "The onlyOwner worked"
    );
  });
});

Uhm wait but the 2nd one should not fail because you are calling the method deletePerson() from account[0] which is the owner.

Yes, but why accounts [0] and [1] or [5] have the same owner? My understanding is that if the owner tries to delete one of his accounts assertion should fail (pass the test) Because he is the owner.