Unit Testing in Truffle

This is my best solution so far ,but it doesn’t work correctly.
Account 1 (contract deployer ?) can basically delete everything

But account 2 (the user account) cannot delete himself.
Can someone help me ?
blz sir, muh famalie… :frowning:

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

var account_one = "0x4F6256c7Ac1Aa5E1ED2c3C8044A81c04A0f95a3D";
var account_two = "0xfA6020641bE53eFB985D3cF90F620fA8d37491F4";

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

/*Create a Person Account1*/
it("should set senior status correctly", async function(){
let instance = await People.deployed();
await instance.createPerson("account1", 65, 190,
{from: account_one, value: web3.utils.toWei("1", "ether")});
let result = await instance.getPerson();
assert(result.senior === true, "Senior level not set");

/*Create a Person Account2*/
});
it("should set senior status correctly", async function(){
let instance = await People.deployed();
await instance.createPerson("account2", 65, 190,
{from: account_two, value: web3.utils.toWei("1", "ether")});
let result = await instance.getPerson();
assert(result.senior === true, "Senior level not set");

/*Delete Account Nr. 1 from Account 1 0x4F6256c7Ac1Aa5E1ED2c3C8044A81c04A0f95a3D*/
});
it("Delete Person", async function(){
let instance = await People.deployed();
await instance.deletePerson(account_one, {from: account_one});
let result = await instance.getPerson();
assert(result.age.toNumber() === 0, "No Data here");

/*Delete Account Nr. 2 from Account 2 0xfA6020641bE53eFB985D3cF90F620fA8d37491F4*/
});
it("Delete Person", async function(){
let instance = await People.deployed();
await instance.deletePerson(account_two, {from: account_two});
let result = await instance.getPerson();
assert(result.age.toNumber() === 0, "No Data here");

})

});

Hey @enrico

it("should set senior status correctly", async function(){
  let instance = await People.deployed();
    await instance.createPerson("Bob", 65, 190, {from: accounts[1], value: web3.utils.toWei("1", "ether")});
    let result = await instance.getPerson({from:accounts[1]});
    assert(result.senior === true, "Senior level not set");
  });

The function getPerson in your smart contracts uses msg.sender to send back the data.
Becuase of this you have to call the function getPerson with the correct account, in this case accounts[1].

Happy coding,
Dani

2 Likes

Hey @Nicksta

I will be happy to take a look, could you please upload also your smart contracts?
In your previous post I saw the some tests were failing with revert() and this is quite ofter related to the contracts.

cheers,
Dani

1 Like

Hello @Obi

Can you please post you contract? I would like to read it :slight_smile:

Happy coding,
Dani

1 Like

The video mentioned that the accounts array was used in a migrations file previously but I did not encounter that in any of the prior videos.

So, I have no idea how to use a different account to test the deletePerson function and had to peek into the solution code given in the link.

Here is the snippet of the code (:
it(“should set senior status”, 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 not allow non-owner to delete”, async function(){
let instance = await People.deployed();
await truffleAssert.fails(instance.deletePerson(accounts[0], {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
});

it(“should allow owner to delete”, async function(){
let instance = await People.deployed();
await truffleAssert.passes(instance.deletePerson(accounts[0], {from: accounts[0]}), truffleAssert.ErrorType.REVERT);
});

1 Like

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

it(“contract balance should match the old balance + additional value”, async function(){
await instance.createPerson(“Bob”, 65, 190, {value: web3.utils.toWei(“1”, “ether”)});
let newBalance = parseFloat(await instance.getBalance());
assert(newBalance == currentBal+1000000000000000000, “Balance is incorrect”);
});

it(“contract balance should match the on-chain balance”, async function(){
let balanceOnChain = await web3.eth.getBalance(instance.address);
let newBalance = parseFloat(await instance.getBalance());
assert(balanceOnChain == newBalance, “Balance is incorrect”);
});

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

it(“should allow owner to withdraw the entire balance”, async function(){
await truffleAssert.passes(instance.withdrawAll_transfer({from: accounts[0]}),truffleAssert.ErrorType.REVERT);
});

it(“contract balance and on-chain balance should equal to zero after owner withdraws all”, async function(){
await instance.createPerson(“Bob”, 65, 190, {value: web3.utils.toWei(“1”, “ether”)});
await instance.withdrawAll_transfer({from: accounts[0]});
let balanceOnChain = await web3.eth.getBalance(instance.address);
let newBalance = parseFloat(await instance.getBalance());
assert(newBalance == 0 && balanceOnChain == 0, “Balance is incorrect”);
});

1 Like

Owner test assignment

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

contract("People", async function(accounts){
  it("shouldn't 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("shouldn't create a person without correct payment", async function(){
    let instance = await People.deployed();
    await truffleAssert.fails(instance.createPerson("Bob", 50, 190, {value: 1000}),
    truffleAssert.ErrorType.REVERT);
  });
  it("should set senior staus 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("shouldn't delete a person if not the owner", async function(){
    let instance = await People.deployed();
    // person has already been created in a pervious test, so don't need the line below:
    //await instance.createPerson("Bob", 50, 190, {value: web3.utils.toWei("1", "ether")});
    await truffleAssert.fails(instance.deletePerson(accounts[0], {from: accounts[1]}),
    truffleAssert.ErrorType.REVERT);
    });
  it("should delete a person if the owner", async function(){
    let instance = await People.deployed();
    await truffleAssert.passes(instance.deletePerson(accounts[0]), "The owner has deleted this person");
  });
  console.log("The owner (accounts[0)] is: " + accounts[0]); // just for clarity
  console.log("The non-owner (accounts[1]) is: " + accounts[1]); // just for clarity
});

1 Like

Value Assignment

Probably not the most elegant of solutions, but here we are:

  it("should increase the contract balance", async function(){
    let initialBalance = await web3.eth.getBalance(instance.address); //should be zero for a new instance
    await instance.createPerson("Felix", 31, 185, {value: web3.utils.toWei("1", "ether")});
    let newBalance = await web3.eth.getBalance(instance.address);
    assert(newBalance > initialBalance, "New contract balance has not increased");
  });
  it("contract balance should be zero after withdrawAll", async function(){
    await instance.createPerson("Crombie", 31, 185, {value: web3.utils.toWei("1", "ether")});
    let initialBalance = await web3.eth.getBalance(instance.address);
    let ownerBalance = await web3.eth.getBalance(accounts[0]);
    // owner withdraws contract balance
    await instance.withdrawAll();
    let newBalance = await web3.eth.getBalance(instance.address);
    let newOwnerBalance = await web3.eth.getBalance(accounts[0]);
    await truffleAssert.passes((newBalance == 0) && (newOwnerBalance > ownerBalance), "Contract balance has not withdrawn");
  });

And I have this at the beginning of the test contract:

  let instance;

  beforeEach(async function(){
    instance = await People.new();
  });
1 Like

@dan-i

Hi Dani,
its just the “Hello Word” contract from Etherium 101 ^^

https://github.com/filipmartinsson/solidity-0.5.12-course/tree/master/Inheritance

My solution for Owner Test Assignment:

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

contract(“People”, async function(accounts){
it(“should create person successfully”, async function(){
let instance = await People.deployed();
await instance.createPerson(“Person1”, 25, 195, {value: web3.utils.toWei(“1”, “ether”)});
let result = await instance.getPerson();
assert((result.name === “Person1”) && (result.age.toNumber() === 25) && (result.height.toNumber() === 195), “Person not created successfully”);
});
it(“shouldn’t delete a person if not contract owner”, async function(){
let instance = await People.deployed();
await truffleAssert.fails(instance.deletePerson(accounts[0], {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
});

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

    //Person record before delete
    console.log("Person name: " + result_beforeDelete.name);
    console.log("Person age: " + result_beforeDelete.age.toNumber());
    console.log("Person height: " + result_beforeDelete.height.toNumber());

    await truffleAssert.passes(instance.deletePerson(accounts[0]), truffleAssert.ErrorType.REVERT);

    //Person record after delete
    let result_afterDelete = await instance.getPerson();
    assert((result_afterDelete.name === "") && (result_afterDelete.age.toNumber() === 0) && (result_afterDelete.height.toNumber() === 0), "Person not deleted");
    console.log("Person name: " + result_afterDelete.name);
    console.log("Person age: " + result_afterDelete.age.toNumber());
    console.log("Person height: " + result_afterDelete.height.toNumber());
});

});

1 Like

Hey @ajphminer

So, I have no idea how to use a different account to test the deletePerson function and had to peek into the solution code given in the link.

As you correctly said, accounts is an array of accounts.
Each index / position of the array contains indeed one account.

As you correctly did, this is the way to use different accounts:

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

You can just change the index to use a different account -> accounts[0] , accounts[1] etc…

happy coding,
Dani

1 Like

Hey @Obi

If you did not specify it, accounts[0] is always the one that deploys your contracts.
Lets look at your tests.

var account_one = "0x4F6256c7Ac1Aa5E1ED2c3C8044A81c04A0f95a3D";
var account_two = "0xfA6020641bE53eFB985D3cF90F620fA8d37491F4";

This is not needed as you can just use accounts[_index] in order to be able to use different accounts.

The function deletePerson() states:

function deletePerson(address creator) public onlyOwner

So only the owner will be able to call it. Who is the owner of the contract in your case?

Cheers,
Dani

1 Like

Owner Assignment
contract(‘People’, async function(accounts){
it(‘Only Owner has access to deletePersons’, async function(){
const owner = accounts[0];
let instance = await People.deployed();
instance.createPerson(‘Bob’, 78, 180, {value: web3.utils.toWei(“1”, “ether”), from: accounts[1]})
await truffleAssert.fails(instance.deletePerson(accounts[1], {from: owner})) ;

})
it(' Person deleted correctly', async function(){
    const owner = accounts[0];
    let instance = await People.deployed();
     await instance.deletePerson(accounts[1], {from: owner});
     let result = await instance.getPerson({from: accounts[1]})

    const {name, age, height, senior} = result;
    assert(age.toNumber() == 0, height.toNumber() == 0, 'Person not deleted');
})

}

1 Like
import "./Owner.sol";

pragma solidity 0.5.8;

contract People is Owner{

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

}
pragma solidity 0.5.8;


contract Owner{

address payable public owner;

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

}

Hi @Nicksta

You are not setting the owner in your Owner.sol contract.
Just add

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


Cheers,
Dani

1 Like

I guess I was a little bit code blind :slight_smile:

Thank you very much!! Really great help

1 Like

My Value assignment solution:

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

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

let instance;
let contractAddress;
let initialContractBalance;
let initialOwnerBalance;

before(async function(){
	instance = await People.deployed();
	contractAddress = People.address;
    initialOwnerBalance = await web3.eth.getBalance(accounts[0]);
    //console.log("initialOwnerBalance is: " + initialOwnerBalance);
	//console.log("Deployed contract's address is: " + contractAddress);
	initialContractBalance = await instance.balance();
	//console.log("Initial Contract balance is: " + initialContractBalance);
});

it("Test1: should increase contract balance after insert new person and match the blockchain contract balance", async function(){
    await instance.createPerson("Person121", 25, 195, {value: web3.utils.toWei("1", "ether")});
    let result = await instance.getPerson();
    assert((result.name === "Person121") && (result.age.toNumber() === 25) && (result.height.toNumber() === 195), "Person not created successfully");
    let newBalance = await instance.balance();
    //console.log("Updated Contract balance after insert new person is: " + newBalance.toString());
    assert(newBalance > initialContractBalance, "Contract balance not updated after create new person");

    let onChainBalance = await web3.eth.getBalance(People.address);
    //console.log("Onchain contract balance is: " + onChainBalance);
    assert(newBalance.toString() === onChainBalance, "The contract balance do not match");
});

it("Test2: should not allow to withdraw funds if not contract owner", async function(){
    newBalance = await instance.balance();
    console.log("Contract balance before withdraw attempt by non contract owner is: " + newBalance);
    await truffleAssert.fails(instance.withdrawAll({from: accounts[1]}), truffleAssert.ErrorType.REVERT);
    newBalance = await instance.balance();
    console.log("Contract balance after withdraw attempt by non contract owner is: " + newBalance);
});

 it("Test3: should withdraw funds if contract owner", async function(){
    await truffleAssert.passes(instance.withdrawAll());

	//check balance is set to zero after withdrawl
	let newBalance = await instance.balance();
    let onChainBalance = await web3.eth.getBalance(People.address);
    //console.log("After withdraw all funds: Contract balance: " + newBalance.toNumber() + ", On Chain balance :" + onChainBalance);
	assert((newBalance.toNumber() === 0) && (onChainBalance === "0"), "Withdraw funds by owner failed. Balance not set to zero");

    //console.log("initialOwnerBalance: " + initialOwnerBalance);
    ownerBalance = await web3.eth.getBalance(accounts[0]);
    //console.log("Contract owner balance after withdraw all: " + ownerBalance);
    await truffleAssert.passes(ownerBalance > initialOwnerBalance, truffleAssert.ErrorType.REVERT);
});

});

1 Like

of the three functions with onlyowner modifier i decided to test the deleteperson function

the first test creates a person, then deletes them, then checks if their age is 0 and assumes they’ve been deleted (not the most secure way of validating but it works here).

the second test creates a person then catches the revert error when another address attempts to delete that person.

here’s my test code:

it(“should allow owner to delete person”, async function(){
let instance = await People.deployed();
await instance.createPerson(“Hog”, 65, 1, {value: web3.utils.toWei(“1”, “ether”), from: accounts[0]});
await instance.deletePerson(accounts[0], {from: accounts[0]});
let result = await instance.getPerson({from: accounts[0]});
assert(result.age.toNumber() === 0, “person not deleted correctly”);
});
it(“shouldn’t allow non-owner to delete person”, async function(){
let instance = await People.deployed();
await instance.createPerson(“Hog”, 65, 1, {value: web3.utils.toWei(“1”, “ether”), from: accounts[0]});
await truffleAssert.fails(instance.deletePerson(accounts[0], {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
});

1 Like

Hi Filip,
I got confused. I have two questions.

1.What are the difference between the two below?
instance = await People.new()
instance = await People.deployed()

  1. Await and async
    I think I understand what it means in general, but can you recommend an article or a lecture which covers this topic in solidity?
1 Like

Hey @acey

Well done here.

he first test creates a person, then deletes them, then checks if their age is 0 and assumes they’ve been deleted (not the most secure way of validating but it works here).

When you delete data in Solidity (it can be from a struct, or array), the system only sets all the value to 0 for that struct or array.
So the best way to check if something has been deleted is indeed to check if the value of that strcut are set to 0.

Cheers,
Dani