Unit Testing in Truffle

okay, thanks. for the response.
Whats the difference between Person.deployed() and Person.new() ?

Hi @hashmiabrar1,

.deployed uses an instance of the project that has already been deployed.
.new() creates a brand new instance.

Docs: https://www.trufflesuite.com/docs/truffle/reference/contract-abstractions

Happy coding,
Dani

1 Like

Sure here are the screenshots, as you can see in the events emitted, deletedBy address is: 0x627306090abaB3A6e1400e9345bC60c78a8BEf57 but there is no such address in ganache.
Maybe I’m thinking wrong?

Hey @Elekko

Can you please post your smart contract?

Cheers,
Dani

Basically just a copy of Filips:


import "./Ownable.sol";
pragma solidity 0.5.12;

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 getContractOwner() public view returns(address){
      return owner;
    }

    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;
   }

}

Hey @Elekko

I deployed and tested your contract and I get the right owner account when the test fails (you should correct that test anyway).

Check my screenshot below:

My tests:

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

contract("People", accounts => {

  console.log(`The contract owner is, ${accounts[0]}`);
  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(); //the result of createPerson above is living from above, i.e. no new contract is created
    assert(result.age.toNumber() == 65, "Age not set correctly");
  });

  it("should only delete person if the caller is not owner", async function(){
    let instance = await People.deployed();
    await truffleAssert.fails(instance.deletePerson(accounts[0]), truffleAssert.ErrorType.REVERT);
  });

})

Are you sure that the ganache instance in your screenshot is the one associated to your project?

Just console.log accounts[0] as I do in my test and check if the address matches with the one emitted in the error logs.

If you want the last test to fail, just run the tx from an account that is not the owner:

it("should only delete person if the caller is not owner", async function(){
    let instance = await People.deployed();
    await truffleAssert.fails(instance.deletePerson(accounts[0],{ from:accounts[1] }), truffleAssert.ErrorType.REVERT);
  });

Cheers,
Dani

Thanks, this is wierd, I got the same contract owner as in the emit when I console logged it:

The contract owner is, 0x627306090abaB3A6e1400e9345bC60c78a8BEf57


  Contract: People
    ✓ shouldn't create a person with age over 150 years (84ms)
    ✓ shouldn't create a person without payment (80ms)
    ✓ should set senior status correctly (149ms)
    ✓ should set age correctly (49ms)
    1) should only delete person if the caller is not owner

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

    People.personCreated(
      name: 'George' (type: string),
      senior: true (type: bool)
    )

    People.personDeleted(
      name: 'Bob' (type: string),
      senior: true (type: bool),
      deletedBy: 0x627306090abaB3A6e1400e9345bC60c78a8BEf57 (type: address)
    )

However, in index 0 in ganache, the address is: 0x3A5B7bc2338c329FaBfBb526276e82760401f223 as in the previous screenshot. I migrated it again and TX count increased in the index 0 account. So it is using the Ganache instance that is associated to my project, i.e. I’m using the truffle.js…

it("should not delete Person unless you are the owner", async function() {
    let instance = await People.deployed();
    await truffleAssert.fails(instance.deletePerson("creator"));
    let result = await instance.getPerson();
  });

lol, seems to look better when I reinstalled Ganache.

Owner Test Assignment
Testing the onlyOwner function modifier

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

contract("People", async function(accounts){
   it("shouldn't be possible to delete a person without being the contract owner", async function(){
        let instance = await People.deployed();
        await instance.createPerson("Dell", 30, 210, {value: web3.utils.toWei("1","ether"), from: accounts[2]});
        await truffleAssert.fails(instance.deletePerson(accounts[2], {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
    });
    it("should be possible to delete a persen by the contract owner", async function(){
        let instance = await People.deployed();
        //await instance.createPerson("Dell", 30, 210, {value: web3.utils.toWei("1","ether"), from: accounts[2]});
        await instance.deletePerson(accounts[2], {from: accounts[0]});
        const {0: Name, 1: Age, 2: Height, 3: Senior} = await instance.getPerson({from: accounts[2]});
        assert(Name === "" && Age == 0, "Person not deleted");
    });
});
1 Like

Value assignment:

contract("People", async function(accounts){
it("should increase the balance after a createPerson function", async function(){
        let instance = await People.new();
        const previousBalance = parseFloat(await instance.balance());
        await instance.createPerson("Bob", 38, 180, {value: web3.utils.toWei("1", "ether")});
        let newBalance = parseFloat(await web3.eth.getBalance(instance.address));
        assert(previousBalance < newBalance, "Amount spent to createPerson hasn't been added to the balance.");
    });
    it("should only allow the owner to withdraw fund", async function(){
        let instance = await People.new();
        await instance.createPerson("Bob", 38, 180, {value: web3.utils.toWei("1", "ether")});
        await truffleAssert.passes(instance.withdrawAll({from: accounts[0]}));
    });
    it("shouldn't allow anyone who isn't the owner to withdraw fund", async function(){
        let instance = await People.new();
        await instance.createPerson("Bob", 38, 180, {value: web3.utils.toWei("1", "ether")});
        await truffleAssert.fails(instance.withdrawAll({from: accounts[1]}), truffleAssert.ErrorType.REVERT);
    });
    it("should have: balance = 0 after the withdrawAll function", async function(){
        let instance = await People.new();
        await instance.createPerson("Bob", 38, 180, {value: web3.utils.toWei("1", "ether")});
        await instance.withdrawAll();
        let previousBalance = parseFloat(await instance.balance());
        let newBalance = parseFloat(await web3.eth.getBalance(instance.address));
        assert(previousBalance == newBalance && previousBalance == web3.utils.toWei("0", "ether"), "The balance isn't equal to 0.");
    });
1 Like

Value assigment

Part of necessary code:

  let instance;
  let listedAccounts;
  let balance;
  let bcbalance;
  before(async function(){ //runs once before we start any test.
    instance = await People.deployed();
    listedAccounts = await web3.eth.getAccounts();
  });

  beforeEach(async function(){
    balance = await instance.balance();
    bcbalance = await web3.eth.getBalance(instance.address);
  })

and it tests…

... it("should be able to delete person if the caller is owner", async function(){
    let instance = await People.new();
    await instance.createPerson("Donkey", 65, 190, {from: accounts[2], value: web3.utils.toWei("1", "ether")})
    await truffleAssert.passes(instance.deletePerson(accounts[2], {from: accounts[0]}), truffleAssert.ErrorType.REVERT);
  });
  it("should have same balance in the blockchain as in the contract after deposit", async function(){
    assert(balance == bcbalance, "Local variable balance vs Blockchain balance not equal");
  });
  it("should only withdraw cash if owner called it", async function(){
    await truffleAssert.passes(instance.withdrawAll({from: accounts[0]}), truffleAssert.ErrorType.REVERT);
  });
  it("should have same balance in the blockchain as in the contract after withdrawal", async function(){
    assert(balance == bcbalance, "Local variable balance vs Blockchain balance not equal");
  });
});
1 Like

The order of tests is important.

  it("shouldn't be possible for somenone to delete a person that he did not created", async function() {
    let instance = await People.deployed();
    await truffleAssert.fails(instance.deletePerson(accounts[0], {from: accounts[1]}));
  })

  it("should be possible for someone to delete a person that he created", async function() {
    let instance = await People.deployed();
    await truffleAssert.passes(instance.deletePerson(accounts[0]));
  })
1 Like

Thanks bbss, you’re right

1 Like
  it("adding a person costs 1 ether", async function() {
    let instance = await People.new();
    let clientAddress = accounts[1];
    let beforeInsertBalance = await web3.eth.getBalance(clientAddress);

    await instance.createPerson("Bob", 40, 190, {from: clientAddress, value: web3.utils.toWei("1", "ether")});
    let afterInsertBalance = await web3.eth.getBalance(clientAddress);

    assert(Number(beforeInsertBalance) > Number(afterInsertBalance) + Number(web3.utils.toWei("1", "ether")), 
      "Adding a person should cost 1 ether, plus the tranzaction cost")
  })

  it("adding a person adds 1 ether to the People contract", async function() {
    let instance = await People.new();
    await instance.createPerson("Bob", 40, 190, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
    let contractBalance = await instance.balance()
    assert(Number(contractBalance) == Number(web3.utils.toWei("1", "ether"), "Adding a person adds 1 ether to the contract."))
  })

  it("the contract owner should be able to withdraw the balance", async function() {
    let instance = await People.new();
    let beforeWithdrawBalance = await web3.eth.getBalance(accounts[0]);

    await instance.createPerson("Bob", 40, 190, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
    await instance.withdrawAll();
    let afterWithdrawBalance = await web3.eth.getBalance(accounts[0]);

    assert(Number(afterWithdrawBalance) > Number(beforeWithdrawBalance), "The balance should had ended un in owner's contract")
  })
1 Like
const createPersonBalance = (web3.eth.getBalance('0x63F74F5d709A45c33190737c8Ac5C1fa0666f387'));

it("balance should be added to contract once person is added", async function() {
    await truffleAssert.passes(instance.createPerson("Bob", 65, 190, {value: web3.utils.toWei("1", "ether")}));
    assert(web3.eth.getBalance('0x63F74F5d709A45c33190737c8Ac5C1fa0666f387'), '1000000000000000000');
  });

  it("onlyOwner should be able to withdrawAll and balance back to 0", async function() {
    await truffleAssert.passes(instance.withdrawAll());
    assert(web3.eth.getBalance('0x63F74F5d709A45c33190737c8Ac5C1fa0666f387'), createPersonBalance === "0");
  });
1 Like

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

// testing the contract balance after create user
contract(“People”, async function(accounts) {

it("should increase the contract address balance correctly after createPerson", async function(){
    let instance = await People.deployed();
    let Balance = await web3.eth.getBalance(People.address);
    await instance.createPerson("billygates", 44, 178, {value: web3.utils.toWei("1","ether"), from: accounts[1]});
    let newBalance = await web3.eth.getBalance(People.address);
    assert(newBalance == parseInt(Balance) + parseInt(web3.utils.toWei("1","ether")), "Did not transfer the funds correctly");        
});
it("shouldn't be possible to withdraw contract balance exept for the owner", async function(){
    let instance = await People.deployed();
    await truffleAssert.fails(instance.withdrawAll({from: accounts[1]}));
});
it("shoud be possible to withdraw contract balance for the owner", async function(){
    let instance = await People.deployed();
    await truffleAssert.passes(instance.withdrawAll({from: accounts[0]}));
});

});

1 Like
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 persn with age over 150 years", async function(){
        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("Bob", 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 possible to delete a different account's person", async function(){
        await instance.createPerson("Bob", 50, 180, {value:web3.utils.toWei("1", "ether"), from: accounts[0]});
        await truffleAssert.fails(instance.deletePerson(accounts[0], {from: accounts[3]}), truffleAssert.ErrorType.REVERT);
    });
    it("should increase the balance when a person is created", async function(){
        let Balance = await web3.eth.getBalance(People.address);
        await instance.createPerson("Bob", 50, 180, {value:web3.utils.toWei("1", "ether"), from: accounts[0]});
        let newBalance = await web3.eth.getBalance(People.address);
        assert (newBalance == parseInt(Balance) + parseInt(web3.utils.toWei("1", "ether")), "Funds were not transferred correctly");
    });
    it("should have a contract balance that is the same as the local variable balance", async function(){
        let Balance = await instance.balance();
        let contractBalance = await web3.eth.getBalance(People.address);
        assert (Balance == contractBalance, "balances do not match up");
    });
    it("should not allow a non-owner to withdraw funds", async function(){
        await truffleAssert.fails(instance.withdrawAll({from: accounts[3]}), truffleAssert.ErrorType.REVERT);
    });
    it("should allow owner to withdraw funds", async function(){
        await truffleAssert.passes(instance.withdrawAll({from: accounts[0]}));
    })

})
1 Like

UPDATE2 could fix the compiler issue as well. It seems that it just took another migrate --reset to change the compiler version after the edit from the truffle-config.js. Always --reset!! very important, and secondly, NEVER GIVE UP haha

UPDATE: I could fix the error. The problem was that the contracts have not been in the contracts folder. cheez I encountered another issue though with the compiler solidity version 0.5.12, as truffle responded that 0.5.8 compiler is used, I tried to change it in the truffle-config.js but with no luck. Then I changed contracts to 0.5.8 and could finally migrate. I hope to being able to run the tests now. Keeping you updated…

Having an issue here with migrating the Peoples contract. The console prints:

Error: Error: Could not find artifacts for Peoples from any sources
at Object.run (C:\Users\sound\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\migrate\index.js:96:1)
at processTicksAndRejections (internal/process/task_queues.js:93:5)
at runMigrations (C:\Users\sound\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate.js:253:1)
at C:\Users\sound\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\commands\migrate.js:216:1

How can I fix this?
I can’t find the error.

Summary

2_Peoples_migration.js:

const Peoples = artifacts.require("Peoples");

module.exports = function(deployer) {
  deployer.deploy(Peoples);
};

Peoples.sol:

import "./Ownable.sol";
pragma solidity 0.5.12;

contract Peoples 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;
   }

}

Ownable.sol :

pragma solidity 0.5.12;

contract Ownable{
    address public owner;

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

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

UPDATE: WOOHOOO!! I had a little breakthrough over here. So I switched from ganache to truffle develop (run: truffle develop in the console) and I could migrate and run the CreatePeople test successfully!! Celebrating

I would still like to know how to fix the issue with ganache…

@filip could you help me out with this?
I tried to update ganache and truffle to current version. Now I get the problem that it says

truffle(ganache)> migrate --reset

Compiling your contracts...
===========================
> Everything is up to date, there is nothing to compile.

Unknown network "ganache". See your Truffle configuration file for available networks.

truffle(ganache)> truffle test
Unknown network "ganache". See your Truffle configuration file for available networks.

So I literally can*t test with ganache, which starts to drive me crazy :exploding_head:
How to fix this issue?

Summary

UPDATE: Have found this post, could this relate to this error?

Having this issue when I want to run the CreatePerson test:

Anyone a clue?

TypeError [ERR_INVALID_REPL_INPUT]: Listeners for `uncaughtException` cannot be used in the REPL
    at process.<anonymous> (repl.js:307:15)
    at process.emit (events.js:327:22)
    at process.emit (C:\Users\sound\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\source-map-support\source-map-support.js:465:1)
    at processEmit [as emit] (C:\Users\sound\AppData\Roaming\npm\node_modules\truffle\build\webpack:\~\signal-exit\index.js:155:1)
    at _addListener (events.js:358:14)
    at process.addListener (events.js:406:10)
    at Runner.run (C:\Users\sound\AppData\Roaming\npm\node_modules\truffle\node_modules\mocha\lib\runner.js:868:11)
    at Mocha.run (C:\Users\sound\AppData\Roaming\npm\node_modules\truffle\node_modules\mocha\lib\mocha.js:612:17)
    at C:\Users\sound\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\test.js:128:1
    at new Promise (<anonymous>)
    at Object.run (C:\Users\sound\AppData\Roaming\npm\node_modules\truffle\build\webpack:\packages\core\lib\test.js:127:1)
    at processTicksAndRejections (internal/process/task_queues.js:93:5)

Here is peoplestest.js:

const Peoples = artifacts.require("Peoples");
const truffleAssert = require("truffle-assertions");
contract("Peoples", async function(){
  it("shouldn't create person with age over 150 years", async function(){
    let instance = Peoples.deployed();
    await truffleAssert.fails(instance.createPerson(Bob, 200, 190, {value: web3.utils.toWei("1", "ether")}));
  })
});

I found this but it did not work for me.