Unit Testing in Truffle

Hey @dan-i did you happen to get a chance to look at this? Sorry for the delay in follow up, I started to work on my 1st project and loss track of getting back to this assignment. Thanks again!

Do I understand this correctly? Typing await prior to a function call, tells the program to not proceed until the current function has finished executing?

1 Like

Is it possible to add more than one option to a function call. For example, if I want to call the createPerson function in my test from accounts[2] and send it some ether from that account as well, is it valid to express that as shown below, or is only one option allowed?

await createPerson(Edward, 30, 190,{from: “accounts[2]”}, value: 1000000000000000000);

Also, can anyone point me to the list of possible options. I’ve read through all the truffle documentation and it mentions: overwrite, gas, from, but I’m certain more exist as Filip demonstrated the value option in this assignment.

Thanks @dan-i, dan you da man!

First I was getting zero balance for both, but after adding a createPerson function call into the test both ways now show a 1 ether balance and all test pass. Amen! Thank you brother!

Is it thus safe to assume that the tests are somehow run separately and do not know of what happened outside its own “it”-clause?

Or why is the balance not increased by 1 ether when a createPerson with value 1 ether is called in a previous test section of the test-sequence?

Just curious to know better why things are going the way they are, always interested in the why, its important to get better. Thanks again!

1 Like

Hi @Paul_Mun

Some considerations.

function createPerson(string memory name, uint age, uint height) public payable costs(1 ether)

This function uses a modifier called costs and the min value is set to 1 ether.
You can therefore remove this require statement from createPerson() require(msg.value >= 1 ether);

Your struct Person has an id that is never used

 struct Person {
      uint id;

In function deletePerson you assert assert(people[creator].age == 0);

This is not a good way to assert because the age can be set to 0 by the user.
When a new user is created (by calling createPerson()) the only limitation to age is: require(age < 150, "Age needs to be below 150"); therefore 0 is a valid entry.

I can suggest to set the id in struct person (maybe starting from 1) so that you can assert the id == 0 when deleting a person.

Cheers,
Dani

1 Like

Hi @EdwardSantos

Async functions are extremely important, I suggest you to take or Javascript course so that all these concepts will be clear.

Happy learning,
Dani

Hey @Kraken

Is it thus safe to assume that the tests are somehow run separately and do not know of what happened outside its own “it”-clause?

This depends on how you write the tests.
In you case yes, they run individually.

Consider your code

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

What you are doing here let instance = await People.deployed(); is creating a new instance of the contract in each test.

This is something I do not usually do.
I do suggest to deploy your contract once (in the before function()) and then use the same instance for all your tests.

Awesome, thank you so much! This helped me fix my problem! :+1:

1 Like

You’re my savior man!!!
thank u so much mate!

It works fine now when i use truffle develop!!

Thanks alot !

1 Like

Of course no problem! I was stuck on that for a solid couple of hours so I figured I’d help anyone out who was stuck on it too! :smiley::+1:

2 Likes

Thanks alot, I did search everywhere but couldn’t find the solution, so you really saved med mate!

it(“verify contract balance”, async function() {
let balance = await web3.eth.getBalance(People.address);
console.log("contract balance "+ balance);
await ins.createPerson(“HK-add Balance1”, 35, 123, {value : web3.utils.toWei(“1”, “ether”)});
let newBalance = await web3.eth.getBalance(People.address);
assert(parseInt(newBalance) > parseInt(balance), “New balance should be higher to original balance”);
});

it(“Owner can withdrawAll”, async function() {
let balance = await web3.eth.getBalance(People.address);
console.log("contract balance "+ balance);
await ins.createPerson(“HK-add Balance1”, 35, 123, {value : web3.utils.toWei(“1”, “ether”)});
await ins.withdrawAll({from: accounts[0]});
let newBalance = await web3.eth.getBalance(People.address);
console.log("New balance "+ newBalance);
assert(parseInt(newBalance) === 0, “New balance should be 0”);
});

1 Like

Hi @dan-i , thanks again. I was thinking something along those lines, that let instance = await sounds like its awaiting for a new instance of the contract being deployed.

One of the videos had a section where you could get rid of duplicate code by using this before-method. So actually there is another benefit, everything does not get reset if yo do this way and there is only one instance of the contract running, which makes testing easier.

Thanks again. I will correct and proceed.

My Solution to deletePerson:

I use the flow of the test , since a person was already created in a prior test I simply test to delete that one first with the shouldn’t delete then with the should delete

  it("shouldn't delete a person without being 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 when owner", async function() {
    let instance = await People.deployed();
    await truffleAssert.passes(instance.deletePerson
      (accounts[0],{from: accounts[0]})
    );
  });
1 Like

Value assignment: Since we use often the value of one eth to wei I have declared a variable at the top of the script that I just reuse:

var oneEth = web3.utils.toWei("1", "ether"); 
...
...
...
 it("Should balance", async function () {
    let instance = await People.new();
    await instance.createPerson
      ("Bob", 65, 190, {from: accounts[1], value: oneEth});
    let balance = await instance.balance();
    let addressBalance = await web3.eth.getBalance(instance.address);
    assert(balance == oneEth, "Balance should be 1 eth");
    assert(balance == addressBalance,"Both balances should be equal" );
  });

  it("Should owner widhtdraw and balance", async function () {
    let instance = await People.new();
    let ownerBalance = await web3.eth.getBalance(accounts[0]);
    await instance.createPerson
      ("Bob", 65, 190, {from: accounts[1], value: oneEth});
    await truffleAssert.passes(instance.withdrawAll({from: accounts[0]}));
    let newOwnerBalance =  await web3.eth.getBalance(accounts[0]);
    assert(newOwnerBalance > ownerBalance , "Owner balanche should be increased");
    let addressBalance = await web3.eth.getBalance(instance.address);
    assert(addressBalance == 0, "Contract balance should be 0");
  });

  it("Shouldn't  non-owner widhtdraw", async function () {
    let instance = await People.new();
    await instance.createPerson
      ("Bob", 65, 190, {from: accounts[1], value: oneEth});
    await truffleAssert.fails(instance.withdrawAll
      ({from: accounts[1]}),truffleAssert.ErrorType.REVERT);
    let addressBalance = await web3.eth.getBalance(instance.address);
    assert(addressBalance != 0, "Contract balance should not be 0");
  });

1 Like
 function createPerson(string memory name, uint age, uint height) public payable costs(1 ether)

Whats the meaning of costs(1 ether) inside the function header?



  it("should not allow deletePerson", async () => {
    truffleAssert.fails(instance.deletePerson(accounts[0], { from: accounts[1] }));

  });

  it("should allow deletePerson", async () => {
    truffleAssert.passes(instance.deletePerson(accounts[0], { from: accounts[0] }));
  });
it("check Balance", async () => {
    let tmpInstance = await People.new();
    const oneEth = web3.utils.toWei("1", "ether");
    await tmpInstance.createPerson("Julian", 70, 186, { value: oneEth, from: accounts[0] });

    const contractBalance = await web3.eth.getBalance(tmpInstance.address);
    assert(contractBalance === oneEth, "Balance does not match");
  });

  it("Ckeck Withdrawl with owner", async () => {
    let tmpInstance = await People.new();
    const oneEth = web3.utils.toWei("1", "ether");

    // send value
    await tmpInstance.createPerson("Julian", 70, 186, { value: oneEth, from: accounts[0] });
    truffleAssert.passes(instance.withdrawlAll({ from: accounts[0] }));
  });

  it("Ckeck balance after Withdrawl with owner", async () => {
    let tmpInstance = await People.new();

    // send value
    await tmpInstance.createPerson("Julian", 70, 186, { value: web3.utils.toWei("10", "ether"), from: accounts[1] });
    const balanceBefore = (await web3.eth.getBalance(accounts[0]));


    // withdrawl
    await tmpInstance.withdrawlAll();
    const balanceAfter = (await web3.eth.getBalance(accounts[0]));
    assert(balanceBefore < balanceAfter, "Owner balance does not increased");
  });

  it("Ckeck balance after Withdrawl of contract", async () => {
    let tmpInstance = await People.new();
    const oneEth = web3.utils.toWei("1", "ether");
    // send value
    await tmpInstance.createPerson("Julian", 70, 186, { value: oneEth, from: accounts[0] });

    // withdrawl
    await tmpInstance.withdrawlAll();

    const realBalance = await web3.eth.getBalance(tmpInstance.address);
    const contractBalance = parseFloat(await tmpInstance.balance());
    assert(contractBalance == 0, "Balance does not match in the Contract");
    assert(contractBalance == realBalance, "Balance does not match Contract with Blockchain");
  });```

Hi @niore

That’s a modifier.
Filip explains it perfectly here, check it out :slight_smile:
https://academy.ivanontech.com/products/ethereum-smart-contract-programming-101-2/categories/4223200/posts/14181421

Think this should work

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 allow create a person without payment", async function(){
    let instance = await People.deployed();
    await truffleAssert.fails(instance.createPerson("John", 50, 190, {value: 1000}), truffleAssert.ErrorType.REVERT);
  });

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

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

  it("should allow owner to delete person", async function(){
    let instance = await People.deployed();
    await instance.createPerson("Alice", 65, 190, {value: web3.utils.toWei("1", "ether"), from: accounts[1]});
    await instance.deletePerson(accounts[1],{from: accounts[0]});
    let result = await instance.getPerson({from: accounts[1]});
    assert(result.name !== "Alice", "Person wasn't deleted");
  });

  it("shouldn't allow non-owner to withdrawAll", async function(){
    let instance = await People.deployed();
    await truffleAssert.fails(instance.withdrawAll({from: accounts[1]}), truffleAssert.ErrorType.REVERT);
  });

  it("should allow owner to withdrawAll", async function(){
    let instance = await People.deployed();
    await instance.createPerson("Alice", 65, 190, {value: web3.utils.toWei("1", "ether"), from: accounts[1]});
    await instance.withdrawAll({from: accounts[0]});
    let newbalance = await web3.eth.getBalance(instance.address);
    assert(newbalance == 0, "Balance isn't zero after withdrawAll but is: " + newbalance);
  });


});

1 Like