Unit Testing in Truffle

it("shouldn't delete a person unless is owner", 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 a person", async function() {
        let instance = await People.deployed();
        await instance.createPerson("Doe", 70, 185, { value: web3.utils.toWei("1", "ether"), from: accounts[1] });
        await truffleAssert.passes(instance.deletePerson(accounts[1], { from: accounts[0]}), truffleAssert.ErrorType.REVERT);
    });
1 Like

My People unit test

const People = artifacts.require('../contracts/People.sol');
const truffleAssert = require('truffle-assertions');

contract('People', (accounts) => {
    let instance;
    const owner = accounts[0];
    const notOwner = accounts[1];

    it('Should deploy smart contract', async () => {
        instance = await People.deployed();
        console.log(instance.address);
        assert(instance.address !== '', "contract failed to deploy");
    });

    it('should NOT create a person with age over 150 years', async () => {
        await truffleAssert.fails(instance.createPerson("Tom", 151, "5ft 11 inch", {value: web3.utils.toWei("1", "ether")}), truffleAssert.ErrorType.REVERT);
    })

    it('should NOT create a person without paying', async () => {
        await truffleAssert.fails(instance.createPerson("Tom", 39, "5ft 11 inch"), truffleAssert.ErrorType.REVERT);
    })

    it('should create a person', async () => {
        await truffleAssert.passes(instance.createPerson("Tom", 65, "5ft 11 inch", {value: web3.utils.toWei("1", "ether")}));
        let result = await instance.getPerson();
        assert(result.isSenior === true, 'the person should be senior');
        assert(result.age.toNumber() === 65, 'the age should be 65');
        assert(result.name === 'Tom', 'the name should be Tom');
        assert(result.height === '5ft 11 inch', 'The height should be 5ft 11 inch');
    })

    it("should only allow 'owner' to delete a person", async () => {
        await truffleAssert.fails(instance.deletePerson(owner, {from: notOwner}), truffleAssert.ErrorType.REVERT);
        await truffleAssert.passes(instance.deletePerson(owner, {from: owner}))
    })
});

updated to use different person object as described in the following video

    it("should only allow 'owner' to delete a person", async () => {
        await instance.createPerson("Somebody", 18, "5ft 10 inch", {from:notOwner, value: web3.utils.toWei("1", "ether")})
        await truffleAssert.fails(instance.deletePerson(owner, {from: notOwner}), truffleAssert.ErrorType.REVERT);
        await truffleAssert.passes(instance.deletePerson(owner, {from: owner}))
    })

Added test around withdraw function

const People = artifacts.require('../contracts/People.sol');
const truffleAssert = require('truffle-assertions');

contract('People', (accounts) => {
    let instance;
    let cleanInstance;
    let fee = web3.utils.toWei("1", "ether");
    before(async () => {
        instance = await People.deployed();
    })
    beforeEach(async () => {
        cleanInstance = await People.new();
    })

    const owner = accounts[0];
    const notOwner = accounts[1];

    it('Should deploy smart contract', async () => {
        assert(instance.address !== '', "contract failed to deploy");e
    });

    it('should NOT create a person with age over 150 years', async () => {
        await truffleAssert.fails(instance.createPerson("Tom", 151, "5ft 11 inch", {value: fee}), truffleAssert.ErrorType.REVERT);
    })

    it('should NOT create a person without paying', async () => {
        await truffleAssert.fails(instance.createPerson("Tom", 39, "5ft 11 inch"), truffleAssert.ErrorType.REVERT);
    })

    it('should create a person', async () => {
        await truffleAssert.passes(instance.createPerson("Tom", 65, "5ft 11 inch", {value: fee}));
        let result = await instance.getPerson();
        assert(result.isSenior === true, 'the person should be senior');
        assert(result.age.toNumber() === 65, 'the age should be 65');
        assert(result.name === 'Tom', 'the name should be Tom');
        assert(result.height === '5ft 11 inch', 'The height should be 5ft 11 inch');
    })

    it("should only allow 'owner' to delete a person", async () => {
        await instance.createPerson("Somebody", 18, "5ft 10 inch", {
            from: notOwner,
            value: fee
        })
        await truffleAssert.fails(instance.deletePerson(owner, {from: notOwner}), truffleAssert.ErrorType.REVERT);
        await truffleAssert.passes(instance.deletePerson(owner, {from: owner}))
    })

    it("should have correct balance", async () => {
        await cleanInstance.createPerson("Tom", 65, "5ft 11 inch", {value: fee});
        const balance = await cleanInstance.getContractBalance();
        assert.equal(balance.valueOf(), fee, 'the balance should be 1 ether');
    });

    it("should not allow non-owner to withdraw", async () => {
        await cleanInstance.createPerson("Tom", 65, "5ft 11 inch", {
            value: fee,
            from: accounts[2]
        });
        await truffleAssert.fails(cleanInstance.withdrawAll({from: notOwner}));
    });

    it("should only allow owner to withdraw", async () => {
        await cleanInstance.createPerson("Tom", 65, "5ft 11 inch", {
            value: fee,
            from: accounts[2]
        });
        let beforeWithdraw = await web3.eth.getBalance(accounts[0]);
        await truffleAssert.passes(cleanInstance.withdrawAll({from: owner}), 'The owner should be able to withdraw');
        const actualBalance = await web3.eth.getBalance(accounts[0]);
        console.log("balance before withdraw: " + beforeWithdraw + "\nbalance afetr: " + actualBalance)
        assert(beforeWithdraw < actualBalance, 'the balance should include the withdrawn fees')
    });
});

it("should update contract balance", async function() {
        let instance = await People.new();
        let initialBalance = await instance.balance();
        assert(initialBalance.toString() === web3.utils.toWei("0", "ether"));
        assert(await web3.eth.getBalance(instance.address) === "0");
        await instance.createPerson("Doe", 70, 185, { value: web3.utils.toWei("1", "ether"), from: accounts[1] });
        assert(await web3.eth.getBalance(instance.address) === web3.utils.toWei("1", "ether"), "Contract balance not set correctly");
    });

    it("should allow owner to withraw all", async function() {
        let instance = await People.new();
        await instance.createPerson("Doe", 70, 185, { value: web3.utils.toWei("1", "ether"), from: accounts[1] });
        let initialBalance = await web3.eth.getBalance(accounts[0]);
        await instance.withdrawAll({ from: accounts[0] });
        let instanceBalance = await instance.balance();
        assert(instanceBalance.toString() === web3.utils.toWei("0", "ether"));
        assert(await web3.eth.getBalance(instance.address) === web3.utils.toWei("0", "ether"), "Contract balance not empty after withdraw");
        assert(await web3.eth.getBalance(accounts[0]) > initialBalance, "Owner contract balance not updated after withdraw");
    });

Took me a while, but I got it to work! Here is my solution for testing the deletePerson function:

    it("Owner should be able to delete person", async function () {
        let instance = await People.deployed();
        await instance.createPerson("Albert", 35, 180, {from: accounts[1], value: web3.utils.toWei("1", "ether")})
        await truffleAssert.passes(instance.deletePerson(accounts[1], {from: accounts[0]}), truffleAssert.ErrorType.REVERT);
    });
    it("Non-owner should not be able to delete person", async function () {
        let instance = await People.deployed();
        await instance.createPerson("Mya", 50, 160, { from: accounts[1], value: web3.utils.toWei("1", "ether") });
        await truffleAssert.fails(instance.deletePerson(accounts[1], {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
    });
This is for the Value Assignment
Unit Testing. Not sure about the last 2 unit tests (Check Balance after withdrawal?) .
});
  it("should reduce account by 1 Eth after person created", async function(){
    let instance = await People.new();
    await instance.createPerson("Genni", 35, 54,
        {from: accounts[1], value: web3.utils.toWei("1", "ether")});

    let balance = await instance.balance();
    let oldBalance = parseFloat(balance);
    let newBalance = await web3.eth.getBalance(instance.address);

  assert(oldBalance == web3.utils.toWei("1", "ether") && oldBalance == newBalance)
  });
  it("should only allow the owner to withdraw balance", async function(){
    let instance = await People.new();
    await instance.createPerson("Dave", 52, 510,
        {from: accounts[2], value: web3.utils.toWei("1", "ether")});

    await truffleAssert.passes(instance.withdrawAll(
        {from: accounts[0]}));
  });
  it("should not allow a non-owner to withdraw balance", async function(){
    let instance = await People.new();
    await instance.createPerson("Genni", 35, 54,
        {from: accounts[1], value: web3.utils.toWei("1", "ether")});

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

Owner Test Assignment

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

it("shouldn't be possible to delete a person without beeing the owner", async function() {
    let instance = await People.deployed();
    await instance.createPerson("frodo", 135, 50, {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 person from the owner", async function() {
    let instance = await People.deployed();
    await instance.deletePerson(accounts[2],{from: accounts[0]});
    let result = await instance.getPerson({from: accounts[2]});
    assert(result.name === "" && result.age == 0, "Person was NOT deleted");
});

} );

it("should delete person", async function(accounts){
    let instance = await People.deployed();
    await instance.deletePerson(accounts[2], {from: accounts[0]});
    let result = await instance.getPerson(accounts[0]);
    assert(result.name === "Bob", "person not deleted")
  });

Hi,

I want to understand the search terms to google while solving assignments.

For eg while finding the solution for the deletePerson assignment what would be some of the search terms inorder to find passes assertion property.

it("should find contract address", async function(){
    let instance = await People.new();
    let initialBalance = await instance.balance();
    var contractAbi = web3.eth.contract(People.abi);
    let contractAddress = contractAbi.at("address");
    assert(initialBalance.toString() === web3.utils.toWei("0", "ether"));
    assert(await web3.eth.getBalance(contractAddress) === "0");
    await instance.createPerson("Ramesh", 25, 195, { value: web3.utils.toWei("1", "ether"), from: accounts[1] });
    assert(await web3.eth.getBalance(contractAddress) === web3.utils.toWei("1", "ether"), "Contract balance is wrong");
    initialBalance += await web3.eth.getBalance(initialBalance);
    await initialBalance.withdrawAll(contractAddress, { from: accounts[0] });
  });

Here is my solution for the value assignment:

//When a person gets added, the balance of the contract gets increased and the balance variable matches the balance that is registered on the blockchain
    it("The balance should increase when a new person gets created", async function () {
        let priorBalance = await web3.eth.getBalance(People.address);
        await instance.createPerson("Alexander", 100, 195, { value: web3.utils.toWei("1", "ether") });
        let newBalance = await web3.eth.getBalance(People.address);
        assert(parseInt(newBalance) === parseInt(priorBalance) + parseInt(web3.utils.toWei("1", "ether")), "The balance did not increase");
        
    });
    it("The balance on the blockchain should match the contract's internal balance variable", async function () { 
        await instance.createPerson("Peter", 45, 127, { value: web3.utils.toWei("1", "ether") });
        let contractBalance = await instance.balance(); //Why paretheses after balance? Not a function.
        let blockchainBalance = await web3.eth.getBalance(People.address);
        assert(parseInt(contractBalance) === parseInt(blockchainBalance), "The contract balance does not match the internal balance variable");
    });
    //The contract owner can withdraw the balance, the balance is reduced to 0, the balance variable matches the balance on the blockchain, and the owner's balance increases
    it("The contract owner can withdraw the balance", async function () {
        await instance.createPerson("Napoleon", 149, 300, { value: web3.utils.toWei("1", "ether") });
        await truffleAssert.passes(instance.withdrawAll({ from: accounts[0] }), truffleAssert.ErrorType.REVERT);
    });
    it("When the contract owner withdraws the balance, the balance is reduced to 0 and the balance matches the internal balance variable", async function () {
        await instance.createPerson("Allison", 33, 120, { value: web3.utils.toWei("1", "ether") });
        await instance.withdrawAll();
        let contractBalance = await instance.balance()
        let blockchainBalance = await web3.eth.getBalance(People.address);
        assert(parseInt(blockchainBalance) === 0 && parseInt(contractBalance) === parseInt(blockchainBalance), "The balance was not reduced to 0, the internal balance variable does not match the on-chain balance");
    });
    it("When the contract owner withdraws the balance, their balance increases", async function () {
        await instance.createPerson("Allison", 33, 120, { value: web3.utils.toWei("1", "ether") });
        let priorOwnerBalance = await web3.eth.getBalance(accounts[0]);
        await instance.withdrawAll();
        let newOwnerBalance = await web3.eth.getBalance(accounts[0]);
        assert(parseInt(newOwnerBalance) > parseInt(priorOwnerBalance), "The owner's balance did not increasee");
    });

@filip, I have a syntax question. One thing I still don’t understand is why we need to use parentheses when we reference the contract balance variable that we declared in the contract:

let contractBalance = await instance.balance()

Isn’t that syntax reserved for functions? Why is using instance.balance not legitimate?

Hey @BlockchainAnarchist

If you set the visibility of a variable to ‘public’, Solidity will automatically create for you a getter function, which is indeed what you call by doing await instance.balance().

Docs: https://docs.soliditylang.org/en/develop/contracts.html#visibility-and-getters

Ah, that’s what I was forgetting! Thank you!

1 Like

Thanks, I did this and cleared the first error. Now this:
Compiling your contracts…

Compiling .\contracts\Migrations.sol

Success

Contract: Helloworld
1) should initialize correctly
> No events were emitted

0 passing (416ms)
1 failing

  1. Contract: Helloworld
    should initialize correctly:
    AssertionError: Unspecified AssertionError
    at Context. (test\helloworldtest.js:10:5)
    at process._tickCallback (internal/process/next_tick.js:68:7)

This works, but it shouldn’t:
assert(message != “Hello Again!”);

Here is my Helloworld_deploy.js – looks identical to Filip’s
const Helloworld = artifacts.require(“Helloworld”);

module.exports = function(deployer, network, accounts){
deployer.deploy(Helloworld).then(function(instance){
// Value is in wei, not Ether
instance.setMessage(“Hello again!”, {value: 1000000, from: accounts[0]}).then(function(){
console.log(“Success”);
//instance.getMessage().then(function(message){
// console.log("Current message: " + message);
}).catch(function(err){
console.log("error: " + err);
});
}).catch(function(err){
console.log("Deploy failed " + err);
});
};

Ha, I must have looked at this 20 times: “Hello again!” != “Hello Again!”

1 Like

Value Assignment

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

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

it("Contract’s balance & user balance should update correctly ", async function(){
let instance= await People.deployed();
let contractAddress= await People.address;
let userBal= await web3.eth.getBalance(accounts[1]);
let contractBal= await web3.eth.getBalance(contractAddress);
await instance.createPerson(“Betty”, 32, 165,{value: web3.utils.toWei(“1”,“ether”), from: accounts[1]});
let userBalUpdate= await web3.eth.getBalance(accounts[1]);
let contractBalUpdate= await web3.eth.getBalance(contractAddress);
assert((userBalUpdate<userBal)&&(contractBalUpdate>contractBal),“Balance not updating correctly”);
});
it(“Balance should be zero after owner withdrawall”, async function(){
let instance= await People.new();
let contractAddress= await instance.address;
await instance.createPerson(“Miami”, 28, 165, {value: web3.utils.toWei(“1”,“ether”), from: accounts[1]});
let contractBal= await web3.eth.getBalance(contractAddress);
await truffleAssert.passes(instance.withdrawAll(),“Withdraw fail”);
await truffleAssert.passes(contractBal.toString()===‘0’,“contract balance is not zero”);

});

});

1 Like

Re the example video, it seems that because the return value of the Github People file is a tuple/array of values, I found it useful to put in a small edit so the returned senior bool value was checked via “result[3]” rather than “result[senior]”

//Owner Test Assignment, my try:

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);
//if the above doesn’t fail we have an error;
});
it (“shouldn’t create a person without payment”, async function(){
let instance = await People.deployed();
await truffleAssert.fails(instance.createPerson(“Bob”,50,190, {value:1000}),
truffleAssert.ErrorType.REVERT);
//if the above doesn’t fail we have an error;
});
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[3] === true, “Senior level not set”);
//if the above doesn’t fail we have an error;
});
it (“should not allow non-owners to withdraw all funds”, async function(){
let instance = await People.deployed();
await truffleAssert.fails(instance.withdrawAll({from:accounts[1]}));

});
it (“should allow owner to withdraw all funds”, async function(){
let instance = await People.deployed();
await truffleAssert.passes(instance.withdrawAll({from:accounts[0]}));

});
});

//value 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);
//if the above doesn’t fail we have an error;
});
it (“shouldn’t create a person without payment”, async function(){
let instance = await People.deployed();
await truffleAssert.fails(instance.createPerson(“Bob”,50,190, {value:1000}),
truffleAssert.ErrorType.REVERT);
//if the above doesn’t fail we have an error;
});
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[3] === true, “Senior level not set”);
//if the above doesn’t fail we have an error;
});

it (“should not allow non-owner to delete people”, async function(){
let instance = await People.deployed();
await instance.createPerson(“Lisa”, 35, 160, {from: accounts[1], value: web3.utils.toWei(“1”,“ether”)});
await truffleAssert.fails(instance.deletePerson(accounts[1], {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
});

it (“should allow owner to delete people”, async function(){
let instance = await People.new();
await instance.createPerson(“Lisa”, 35, 160, {from: accounts[1], value: web3.utils.toWei(“1”,“ether”)});
await truffleAssert.paases(instance.deletePerson(accounts[1], {from: accounts[0]}), truffleAssert.ErrorType.REVERT);
});

/*
using this.address for the assignment
else use let instace = await People.deployed();
let contractAddress = await instance.address;
*/

it ("should increment the reconciled contract balance upon adding a person", async function(){
  let instance = await People.deployed();
  let contractAddress = await instance.address;
  let initialBalance = await web3.eth.getBalance(contractAddress);
  await assert (web3.eth.getBalance(contractAddress) === instance.balance, "-initial balances reconcile...");
  await instance.createPerson("Lisa", 35, 160, {from: accounts[1], value: web3.utils.toWei("1","ether")});
  let currentBalance = await instance.balance;
  assert (currentBalance === initialBalance + web3.utils.toWei("1","ether"), "-balance not up by 1 ether...");
  assert (currentBalance === web3.eth.getBalance(contractAddress), "-contract balance data not reconciled to blockchain.");
})


it ("should not allow non-owners to withdraw all funds", async function(){
  let instance = await People.deployed();
  await truffleAssert.fails(instance.withdrawAll({from:accounts[1]}),"funds withdrawn by non-owner");


});
it ("should allow owner to withdraw all funds", async function(){
  let instance = await People.deployed();
  let contractAddress = await instance.address;
  let initialBalance = await web3.eth.getBalance(contractAddress);
  let initialOwnerBalance = await web3.eth.getBalance(accounts[0]);
  await assert (web3.eth.getBalance(contractAddress) === instance.balance, "-initial balances reconcile..");
  await truffleAssert.passes(instance.withdrawAll({from:accounts[0]}), "funds not withdrawable by owner");
  let currentBalance = await instance.balance;
  assert (currentBalance === 0, "-contract balance not settled...");
  await assert (currentBalance === web3.eth.getBalance(contractAddress), "-contract balance data not reconciled to blockchain.");
  await assert ( web3.eth.getBalance(accounts[0]) === initialBalance + initialOwnerBalance, "-owner not credited all the funds");
  });

});