Unit Testing in Truffle

Hey, I’ve tried that and it still gives me the same error.
I’ve tried uninstall and reinstall nad a bunch of other things but nothing have helped to solve this issue:(

1 Like

Lycka till! Jag ger upp denna kursen, det blir fel vid varje körning och det är för rörigt i instruktionerna.

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 have 1 ether after createPerson", async function(){
    await truffleAssert.passes(instance.createPerson("Bob",100,190,{value: web3.utils.toWei("1", "ether"),from:accounts[1]}),"Failed to createPerson");
    let peopleBalance = await web3.eth.getBalance(instance.address);
    assert (peopleBalance == web3.utils.toWei("1", "ether"),"Balance should be 1 ether. Balance currently is: " + peopleBalance);
  });
  it ("should not be withdrawable by non-owner", async function(){
    await truffleAssert.fails(instance.withdrawAll({from: accounts[1]}),truffleAssert.ErrorType.REVERT);
  })
  it ("contract balance subtracted, owner balance increased", async function(){
    let peopleBalance = await web3.eth.getBalance(instance.address);
    let ownerBalanceBefore = await web3.eth.getBalance(accounts[0]);
    assert (peopleBalance == web3.utils.toWei("1", "ether"),"Balance should be 1 ether. Balance currently is: " + peopleBalance);
    // let result = await instance.withdrawAll();
    let result = await instance.withdrawAll();
    let ownerBalanceAfter = await web3.eth.getBalance(accounts[0]);
    let gasPrice = await web3.eth.getGasPrice();
    peopleBalance = await web3.eth.getBalance(instance.address);
    assert (peopleBalance == 0, "Balance should be zero. Balance currently is: " + peopleBalance);
    assert ((ownerBalanceAfter - ownerBalanceBefore + result.receipt.gasUsed*gasPrice) ==  web3.utils.toWei("1", "ether"),
    "owner balance not increased correctly. " +
    "gasUsed: " + result.receipt.gasUsed +
    ", gasPrice: " + gasPrice +
    ", ownerBalanceBefore: " + ownerBalanceBefore +
    ", ownerBalanceAfter: " + ownerBalanceAfter);
  })
});

Ja man blir frustrerad men finns nog en lösning till allt gubben:)!

1 Like

WithdrawAll tests…

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

contract("People", (accounts) => {
  let instance;
  let ownerAddress = accounts[0];
  let userAddress = accounts[1];

  beforeEach(async () => {
    instance = await People.new({ from: ownerAddress });
  });

  it("Should deposit eth when creating a Person.", async () => {
    assert((await web3.eth.getBalance(instance.address)) == "0");
    await truffleAssertions.passes(
      instance.createPerson("mock_name", 22, 156, {
        value: web3.utils.toWei("1", "ether"),
        from: userAddress,
      })
    );
    assert((await web3.eth.getBalance(instance.address)) == "1000000000000000000");
  });

  it("Should only permit the owner to withdraw all.", async () => {
    instance.createPerson("mock_name", 22, 156, {
      value: web3.utils.toWei("1", "ether"),
      from: userAddress,
    });
    truffleAssertions.fails(instance.withdrawAll({ from: userAddress }));
    let ownerBalanceBefore = parseInt(await web3.eth.getBalance(ownerAddress));
    truffleAssertions.passes(await instance.withdrawAll({ from: ownerAddress }));
    let ownerBalanceAfter = parseInt(await web3.eth.getBalance(ownerAddress));
    assert(ownerBalanceBefore != ownerBalanceAfter);
    assert((await web3.eth.getBalance(instance.address)) == "0");
  });
});

Could someone put in the step by step instructions to get the solidity coding going on this new solidity 201 setup. I dont always have the chance to continue the work the next day and sometimes venture into other things and it’s a bit frustrating to look through old videos to find the right order.

So what is the order?

  1. run ganache, connect your folder and save & restart.
  2. launch console and start truffle console in your current directory.
  3. start coding,
  4. migrate
  5. test

Those are just guesses now. What is the exact procedure. Im a bit lost here and not sure what does what. truffle test seemed to run the tests in the end. Please help a brother out. Sure it will be useful for others to to have it written as step by step and save many peeps some time.

Thanks in advance!

Seems fine. Just skip step 4 if no changes to solidity file

Yeah bro! Here we go! I finally got it to work:

it(“should allow owner to delete a person”, async function (){
let instance = await People.deployed();
await instance.createPerson(“Bob Stroolmalainen”, 42, 180, {value: web3.utils.toWei(“1”, “ether”)});
await truffleAssert.passes(instance.deletePerson(accounts[0], {from: accounts[0]}), truffleAssert.ErrorType.REVERT);
});
it(“shouldn’t allow other than owner to delete a person”, async function (){
let instance = await People.deployed();
await instance.createPerson(“Bob Stroolmalainen”, 42, 180, {value: web3.utils.toWei(“1”, “ether”)});
await truffleAssert.fails(instance.deletePerson(accounts[0], {from: accounts[1]}), truffleAssert.ErrorType.REVERT);
});

1 Like

Added two test cases. Firs one, you are the owner and can delete persons. Second one you are not the owner and can not delete persons.

it(“should verify that owner can remove persons”, async function(){
let instance = await People.deployed();
await instance.createPerson(“Bob”, 20, 190, {value: web3.utils.toWei(“1”, “ether”), from:accounts[0]});
await instance.deletePerson(accounts[0], {from:accounts[0]});
});
it ("should avoid person removal if not requested by the contract owner ", async function(){
let instance = await People.deployed();
await instance.createPerson(“Bob”, 20, 190, {value: web3.utils.toWei(“1”, “ether”), from:accounts[0]});
await truffleAssert.fails(instance.deletePerson(accounts[0], {from:accounts[1]}), truffleAssert.ErrorType.REVERT);
});

1 Like

On the value assignement part I got all but the contract balance to match with the balance onchain, its the last test. Can someone tell me why it doesnt work, and how I could print out the values of balanceonchain and contractBalance to the terminal to see what values the represent. console.log doesnt work with truffle…

it(“Balance should increase by 1 ether when a person is added”, async function (){
let instance = await People.deployed();
let balanceonchainBefore = web3.eth.getBalance(People.address);
await instance.createPerson(“Bob Ahmedsson”, 42, 180, {value: web3.utils.toWei(“1”, “ether”)});
let balanceonchainAfter = web3.eth.getBalance(People.address);
assert(balanceonchainBefore + parseInt(web3.utils.toWei(“1”, “ether”) == balanceonchainAfter), “Balance after adding a person should increas by 1 ether”);
});
it(“owner should be able to withdraw contract balance”, async function (){
let instance = await People.deployed();
await truffleAssert.passes(instance.withdrawAll({from: accounts[0]}), truffleAssert.ErrorType.REVERT);
});
it(“actual contract balance should match with balance variable”, async function (){
let instance = await People.deployed();
let balanceonchain = web3.eth.getBalance(People.address);
contractBalance = await instance.getBalance();
assert(balanceonchain == contractBalance, “Balance on blockchain and stored contract balance do not match”);
});

Hey @Kraken

Console.log works in truffle and you should be able to print balanceonchain and contractBalance.
Proceed so that you can check the two results.
Let me know.

Hey there, here is the people.sol file and Ownable.sol file as well. Thanks!

people.sol file:

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

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

Ownable.sol file:

pragma solidity 0.7.6;

contract Ownable {

  address public owner;

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

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

Hey @Lamar, did you end up fixing this? I could be wrong but I think I had this similar problem you are describing. I had to go into the truffle-config.js file and un-comment out this part and add this in:

  networks: {
    development: {
     host: "127.0.0.1",
     port: 7545,
     network_id: "5777",
    },

If this is what your Ganache is showing for the RCP Server, Port, Network ID then I would put that in and give it a try. I hope this is helps!

1 Like

Hey @dan-i, Could you please tell me how to use console.log in this case?

I tried both in the javascript code and straight from the truffle console. Also googled, but no simple answer and nothing that worked. I get it, you probably want us to find the answer on google instead, thats all cool but a bit frustrating when you have to google so much and there is rarely one clear answer and your small detour turns into a bluepill forrest, loosing track of the simple thing you needed solved.

How would you print the values of my variables in this case?

Many thanks!

Hi @Kraken

You can console.log your variables by console.log(contractBalance).

Regards

Hey guys,
whenever i try to run the test on cmd i got this error message.
Does anyone know what it could be wrong?

Thank you @filip

Hello @ChrisPap

That’s a bug with the node version you are currently using.
Follow this FAQ to fix the issue: FAQ - How to downgrade Node.Js

Sorry @dan-i , Im a retard. I cant get it to work. My peopletest.js:

it("actual contract balance should match with balance variable", async function (){
        let instance = await People.deployed();
        let balanceonchain = web3.eth.getBalance(People.address);
        contractBalance = await instance.getBalance();
        console.log(contractBalance);
        console.log(balanceonchain);
        
        assert(balanceonchain == contractBalance, "Balance on blockchain and stored contract balance do not match");
    });

the results when I runt test from truffle console:

truffle(ganache)> test
Using network ‘ganache’.

Compiling your contracts…

Compiling ./contracts/people.sol
Artifacts written to /var/folders/9m/rsmr48t16kl7qqm_lnrmn_j00000gn/T/test-202105-1185-ht4zel.4oyol
Compiled successfully using:

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

Contract: People
✓ shouldn’t create a person with age over 150 years (75ms)
✓ shouldn’t create a person without proper payment (74ms)
✓ should set senior status correctly (137ms)
✓ should set age correctly (44ms)
✓ should allow owner to delete a person (201ms)
✓ shouldn’t allow other than owner to delete a person (181ms)
✓ Balance should increase by 1 ether when a person is added (125ms)
✓ owner should be able to withdraw contract balance (49ms)
BN { negative: 0, words: [ 0, <1 empty item> ], length: 1, red: null }
Promise { ‘0’ }
1) actual contract balance should match with balance variable
> No events were emitted

8 passing (999ms)
1 failing

  1. Contract: People
    actual contract balance should match with balance variable:
    AssertionError: Balance on blockchain and stored contract balance do not match
    at Context. (test/peopletest.js:61:9)
    at runMicrotasks ()
    at processTicksAndRejections (internal/process/task_queues.js:97:5)

Hey @Kraken

No worries I am here to help!

Look at your console log:

console.log(contractBalance);
console.log(balanceonchain);

These display

BN { negative: 0, words: [ 0, <1 empty item> ], length: 1, red: null }
Promise { ‘0’ }

The 1st one is a big number.
The 2nd one is a promise.

Consider the 2nd one.
Whenever your console.log result is a promise, it means that you are displaying data that are not ready yet.

In order to wait for result we use await.

So
let balanceonchain = web3.eth.getBalance(People.address);
becomes
let balanceonchain = await web3.eth.getBalance(People.address);

Try again and check if that fixed.

It might be useful also to convert the big number to number.

assert(balanceonchain == parseInt(contractBalance), “Balance on blockchain and stored contract balance do not match”);

1 Like

Thanks a lot for your reply @dan-i!

1 Like