Unit Testing in Truffle

@dan-i … anyhow … you got me thinking … why do we need to downgrade Node.js in order for this test to work? what is the incompatibility between node.js and truffle or ganache?? :face_with_monocle: :nerd_face: … I was busy learning how to create a front-end app with react at codeacademy!!!

Here is my answer to the assignment:

  it("person deleted by owner is ok", async function(){
    let instance = await People.deployed();
    await instance.createPerson("javier", 37, 180, {from: accounts[2], value: web3.utils.toWei("1", "ether")});
    await truffleAssert.fails(instance.deletePerson(accounts[2], {from: accounts[2]}), truffleAssert.ErrorType.REVERT);
  });
  it("person not deleted by owner is ok", async function(){
    let instance = await People.deployed();
    await instance.createPerson("javier", 37, 180, {value: web3.utils.toWei("1", "ether")});
    await truffleAssert.reverts(instance.deletePerson(accounts[2], {from: accounts[1]}), "only owner");

sadly, these 2 tests failed :sweat_smile: … I suspect that it has to do with this message that I am getting:

I really struggled with this assignment and my code would not work and at time of posting this I have no idea why.

The confirmation of deletion code works fine but I cannot understand why when I call the deletePerson function with what is clearly address[1], the function completes with address[0] being called as the deletor. I must be missing something.

const People = artifacts.require(“People”);
const truffleAssert = require(“truffle-assertions”);
contract(“People”, async function(accounts){
it(“Should not create person if over 150”, 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(“Should not create person if insufficient 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 bool if age above 65”, async function(){
let instance = await People.deployed();
console.log("Creator = ", accounts[0]);
await instance.createPerson(“Bob”, 70, 190, {value: web3.utils.toWei(“1”, “ether”), from:accounts[0]});
let result = await instance.getPerson();
assert(result.senior === true, “Senior value not set”);
});
it(“Should set age correctly”, async function(){
let instance = await People.deployed();
let result = await instance.getPerson();
assert(result.age.toNumber() === 70, “Age not set correctly”);
});
it(“Check delete operation only works for owner”, async function(){
let instance = await People.deployed();
console.log("Deletor = ", accounts[1]);
await truffleAssert.fails(instance.deletePerson(accounts[1]), truffleAssert.ErrorType.REVERT);
});
it(“Check delete works for owneroperation only works for owner”, async function(){
let instance = await People.deployed();
let result = await instance.getPerson();
console.log('Age = ’ , result.age.toNumber())
await instance.deletePerson(accounts[0]);
result = await instance.getPerson();
console.log('Age = ’ , result.age.toNumber())
assert(!(result.age.toNumber() === 70), “Person not deleted”);
});
});

Looking back at this I can see the two errors I have made. Firstly I had misunderstood what the onlyOnwer function does. Firstly as I was not using the from parameter then all my deleltePerson calls were being done as account[0]. I was also wrongly assuming that as account[0] I should not be able to call deletePerson(account[1]). Solution now makes sense to me.

Hey @javier_ortiz_mir

You have to downgrade as node as somewhere a bug with truffle.
Because there is nothing we can do about it, we have to downgrade.
Hopefully will be fixed in future releases.

Hey, please post the whole test file.

thanks

Hey @cryptophile

What errors do you get when running your tests?

cheers,
Dani

My first attempt, although it doesn’t seem the Solidity style :blush:

contract('People', async (accounts) => {
  let instance;

  beforeEach(async () => {
    instance = await People.deployed();
  });

  it('should delete a person', async () => {
    await instance.createPerson('Sam4', 30, 170, {value: web3.utils.toWei('2', 'ether')});
    let person = await instance.getPerson();
    assert.equal(person[0], 'Sam4', 'The name should be Sam4.');

    await instance.deletePerson(accounts[0]);
    person = await instance.getPerson();
    assert.equal(person[0], '', 'The name should be an empty string because the person was deleted.');
  });

  it('should allow only contract owners to delete persons', async () => {
    await instance.createPerson('Sam5', 30, 180, {value: web3.utils.toWei('1', 'ether')});
    await instance.deletePerson(accounts[1]);
    let person = await instance.getPerson();
    assert.equal(person[0], 'Sam5', 'Person should not be deleted by anyone other than the owner.')

    await instance.deletePerson(accounts[0]);
    person = await instance.getPerson();
    assert.equal(person[0], '', 'The name should be an empty string because the person was deleted by the contract owner.')
  })
});
1 Like

Hey @Samy

You are indeed writing your tests in Javascript not Solidity :slight_smile:
You will surely get used to JS as you can do really amazing things with it.

If you need help we are here :slight_smile:

Happy learning,
Dani

Manage to write some of the second assignment tests, but something funny happens and I can’t figure it out why…
If I uncomment the first test(“should not allow non-owner to delete people”) all others are failing. On the other hand, if I comment all other tests and run only the first one, it works just fine…
Also not sure yet how to get the blockchain balance…

contract('People', async (accounts) => {
  let instance;

  beforeEach(async () => {
    instance = await People.deployed();
  });

  // it('should not allow non-owner to delete people', async () => {
  //   await instance.createPerson('Sam5', 30, 170, {from: accounts[1], value: web3.utils.toWei('1', 'ether')});
  //   truffleAssert.fails(instance.deletePerson(accounts[1]), truffleAssert.ErrorType.REVERT);
  // });

  it('should allow owner to withdraw all balance', async () => {
    await instance.createPerson('Sam6', 18, 180, {from: accounts[1], value: web3.utils.toWei('1', 'ether')});
    truffleAssert.passes(instance.withdrawAll());
  });

  it('should not allow not-owner to withdraw all balance', async () => {
    await instance.createPerson('Sam7', 18, 180, {from: accounts[1], value: web3.utils.toWei('1', 'ether')});
    truffleAssert.fails(instance.withdrawAll({from: accounts[1]}), truffleAssert.ErrorType.REVERT);
  });

  it('should increase contract balance when creating a new person', async () => {
    const contractAddress = await instance.address;
    const balanceBeforeCreatePerson = await web3.eth.getBalance(contractAddress);
    await instance.createPerson('Sam8', 18, 180, {from: accounts[1], value: web3.utils.toWei('1', 'ether')});
    const balanceAfterCreatePerson = await web3.eth.getBalance(contractAddress);
    assert.isTrue(balanceAfterCreatePerson > balanceBeforeCreatePerson, 'Contract balance has increased.');
  });

  it('should have a contract balance of 0 after withdraw all balance', async () => {
    await instance.createPerson('Sam9', 18, 180, {from: accounts[1], value: web3.utils.toWei('1', 'ether')});
    await instance.withdrawAll();
    const balanceAfterWithdrawAll = await web3.eth.getBalance(instance.address);
    assert.equal(balanceAfterWithdrawAll, '0', 'Contract balance should be 0 after withdrawAll');
  });

  it('should increase the owner balance after withdraw all balance', async () => {
    const ownerBalanceBeforeCreatePerson = Number(web3.utils.fromWei(await web3.eth.getBalance(accounts[0])));
    await instance.createPerson('Sam10', 18, 180, {from: accounts[1], value: web3.utils.toWei('1', 'ether')});
    await instance.withdrawAll();
    const ownerBalanceAfterCreatePerson = Number(web3.utils.fromWei(await web3.eth.getBalance(accounts[0])));
    assert.isTrue(ownerBalanceAfterCreatePerson > ownerBalanceBeforeCreatePerson, 'Owner balance has increased');
  });

  // it('contract balance should be equal with the blockchain balance', async () => {
  //   // TODO
  // });

  // it('should have a blockchain balance of 0 after withdraw all balance', async () => {
  //   // TODO
  // });
});```

Update:
After reviewing the assignment solution, the difference between contract and blockchain(real) balance become clear and I completed the rest of the tests.
But I still have no idea why the commented test(“should not allow non-owner to delete people”) is making all other tests to fail…

contract('People', async (accounts) => {
  let instance;

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

  // it('should not allow non-owner to delete people', async () => {
  //   await instance.createPerson('Sam5', 30, 170, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
  //   truffleAssert.fails(instance.deletePerson(accounts[1]), truffleAssert.ErrorType.REVERT);
  // });

  it('should allow owner to withdraw all balance', async () => {
    await instance.createPerson('Sam6', 18, 180, { from: accounts[2], value: web3.utils.toWei('1', 'ether') });
    truffleAssert.passes(instance.withdrawAll({ from: accounts[0] }));
  });

  it('should not allow not-owner to withdraw all balance', async () => {
    await instance.createPerson('Sam7', 18, 180, { from: accounts[2], value: web3.utils.toWei('1', 'ether') });
    truffleAssert.fails(instance.withdrawAll({ from: accounts[2]}), truffleAssert.ErrorType.REVERT);
  });

  it('should in crease contract balance when creating a new person', async () => {
    const contractAddress = await instance.address;
    const balanceBeforeCreatePerson = await web3.eth.getBalance(contractAddress);
    await instance.createPerson('Sam8', 18, 180, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
    const balanceAfterCreatePerson = await web3.eth.getBalance(contractAddress);
    assert(balanceAfterCreatePerson > balanceBeforeCreatePerson, 'Contract balance has not increased.');
  });

  it('should reset the contract balance to 0 after withdraw all', async () => {
    await instance.createPerson('Sam9', 18, 180, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
    await instance.withdrawAll();

    const contractBalance = parseFloat(await instance.balance());
    
    assert(contractBalance == web3.utils.toWei('0', 'ether'), 'Contract balance not equal to 0.');
  });

  it('should reset the blockchain balance to 0 after withdraw all', async () => {
    await instance.createPerson('Sam10', 18, 180, { from: accounts[2], value: web3.utils.toWei('1', 'ether') });
    await instance.withdrawAll();

    const blockchainBalance = await web3.eth.getBalance(instance.address);

    assert(blockchainBalance == web3.utils.toWei('0', 'ether'), 'Contract balance not equal to 0.');
  });

  it('should have contract balance equal to blockchain balance', async () => {
    await instance.createPerson('Sam11', 18, 180, { from: accounts[2], value: web3.utils.toWei('1', 'ether') });
    await instance.withdrawAll();

    const contractBalance = parseFloat(await instance.balance());
    const blockchainBalance = await web3.eth.getBalance(instance.address);
    
    assert(contractBalance == blockchainBalance, 'Contract balance not equal to blockchain balance.');
  });

  it('should increase the owner balance after withdraw all', async () => {
    await instance.createPerson('Sam12', 18, 180, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
    const balanceBeforeWithdraw = parseFloat(web3.utils.fromWei(await web3.eth.getBalance(accounts[0])));

    await instance.withdrawAll();
    const balanceAfterWithdraw = parseFloat(web3.utils.fromWei(await web3.eth.getBalance(accounts[0])));

    assert(balanceAfterWithdraw > balanceBeforeWithdraw, 'Owner balance has not increased.');
  });
});
const People = artifacts.require("People");
const truffleAssert = require("truffle-assertions");

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

   let instance;

   before(async function(){
      instance = await People.deployed();
   });
   //beforeEach();
   //after();
   //afterEach();

   it(" testing height controls... ", async function(){
      //in asserting a failure with .fails we are expecting an error because that is the normal behavior for the function
      await truffleAssert.fails(instance.createPerson("Bob", 200, 190, {value: web3.utils.toWei("1", "ether")}), truffleAssert.ErrorType.REVERT);
   });
   it(" testing payment controls... ", async function(){
      await truffleAssert.fails(instance.createPerson("Bob", 50, 190, {value: 1000}));
   });
   it(" testing senior status controls... ", async function(){
      await instance.createPerson("Bob", 66, 190, {value: web3.utils.toWei("1", "ether")});
      let result = await instance.getPerson();
      assert(result.senior === true, "Senior level not set");
   });
   it(" testing age setting... ", async function(){
      let result = await instance.getPerson();
      assert(result.age.toNumber() === 66, "Age not set correctly");
   });
   it(" testing onlyOwner... ", async function(){
      let instance = await People.new();
      await instance.createPerson("Anne", 42, 130, {from:accounts[1], value: web3.utils.toWei("1", "ether")});
      await truffleAssert.passes(instance.deletePerson(accounts[1], {from: accounts[0]}));
   });

   it(" testing balance update upon person creation... ", async function(){
      //gather prior state variables
      let priorBalance = await web3.eth.getBalance(People.address+'');
      let priorBalancePublicVar = parseInt(await instance.balance(), 10);
      //initiate transaction of 6 ether
      await instance.createPerson("Ether", 42, 140, {from: accounts[1], value: web3.utils.toWei("6", "ether")});
      //gather later state variables
      let laterBalancePublicVar =  parseInt(await instance.balance(), 10);
      let laterBalance = await web3.eth.getBalance(People.address+'');
      //assert logic of delta between Public Var and Web3.eth.getBalance temp vars
      assert((laterBalance-priorBalance)===(laterBalancePublicVar-priorBalancePublicVar));
   });
});

1 Like

Hey Samy, can you post your smart contract?

Here’s my solution, I only did one test and haven’t looked at Filip’s solution yet - hope mine checks all the boxes!

  it("should accept transactions only from the owner", async function(){ //Check both that non-owner can't use and owner can use. We have already created a person from this address in our previous tests, so we don't need to create a new person here.
    let instance = await Peoplecontract.deployed();
    await truffleAssert.fails(instance.deletePerson(accounts[0], {from: accounts[1]}), truffleAssert.ErrorType.REVERT); //We send a transaction from account 1, which should fail, since the contract was deployed by account 0.
    await instance.deletePerson(accounts[0], {from: accounts[0]}); // Here we call the deleteperson function from the owner account - account 0.
    let result = await instance.getPerson();
    assert(result.age.toNumber() === 0 && result.name == "" && result.height.toNumber() === 0, "Owner wasn't able to delete person properly."); //If the deletion worked, all numeric properties for a person created by this address should now be 0 and strings should be empty.
  })
1 Like

Here are my tests for createPerson increasing contract balance and withdrawAll:

it("should increase contract balance when person is added by 1 ETH", async function(){
    let instance = await Peoplecontract.new();
    let balance = await web3.eth.getBalance(instance.address);
    await instance.createPerson("Lily", 26, 160, {value: web3.utils.toWei("1", "ether")}); // Create a person so balance should increase by 100
    let newbalance = await web3.eth.getBalance(instance.address);
    assert(newbalance - balance == 1000000000000000000, "Balance didn't increase by 1 ETH.") // If current contract balance - balance b4 creating a person isnt 1ETH, then something went wrong.
  })
  it("should let contract owner withdraw balance", async function(){
    let instance = await Peoplecontract.new();
    await instance.createPerson("Lily", 26, 160, {value: web3.utils.toWei("1", "ether")}); // Create a person, so balance contract balance is now 1ETH.
    await instance.withdrawAll({from: accounts[0]}); //Withdraw balance from contract.
    let balance = await web3.eth.getBalance(instance.address);
    await console.log(balance);
    assert(balance == 0, "Failed to withdraw balance") // contract balance should be 0 after we have withdrawn all funds. Need to use == or "0" because getBalance returns a string
  })

Value Assignment
it(“should correctly set the balance in the contract”, async function() {
await instance.createPerson(“Bob”, 65, 190, {from: accounts[1], value: web3.utils.toWei(“1”, “ether”)});
truffleAssert.passes(web3.eth.getBalance(“0x0bc2aaB96D50A49B2640343fd649C82Bec5c5532”) === 1);
});
it(“should correctly set equal the balance in the variable and the contract”, async function() {
await instance.createPerson(“Bob”, 65, 190, {from: accounts[1], value: web3.utils.toWei(“1”, “ether”)});
truffleAssert.passes(web3.eth.getBalance(“0x0bc2aaB96D50A49B2640343fd649C82Bec5c5532”) === People.balance);
});
it("should correctly change balance ", async function() {
await instance.createPerson(“Bob”, 65, 190, {from: accounts[1], value: web3.utils.toWei(“1”, “ether”)});
let result = await instance.withdrawAll();
truffleAssert.passes(People.balance === 0, “Transfer not correct”);
});
it(“should take money out of the contract”, async function() {
await instance.createPerson(“Bob”, 65, 190, {from: accounts[1], value: web3.utils.toWei(“1”, “ether”)});
await instance.withdrawAll();
truffleAssert.passes(web3.eth.getBalance(“0x0bc2aaB96D50A49B2640343fd649C82Bec5c5532”) === 0);
});
it(“should correctly increase balance in owner’s account”, async function() {
await instance.createPerson(“Bob”, 65, 190, {from: accounts[1], value: web3.utils.toWei(“1”, “ether”)});
result = await instance.withdrawAll();
truffleAssert.passes(result === web3.eth.getBalance(“0x3E10e7D7f98F0db6d84C9D5297bBD46c69B343e1”));
});

1 Like

Sure @dan-i . Pushed the fully updated contract to GitHub.
And here is the completed peopletest.js:

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

contract('People', async (accounts) => {
  let instance;

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

  it('should not create a person older than 150 years', async () => {
    await truffleAssert.fails(
      instance.createPerson('Sam1', 151, 180, {value: web3.utils.toWei('1', 'ether')}),
      truffleAssert.ErrorType.REVERT
    );
  });

  it('should not create a person without payment', async () => {
    await truffleAssert.fails(
      instance.createPerson('Sam2', 40, 180, { value: 1000 }),
      truffleAssert.ErrorType.REVERT
    );
  });

  it('should set senior status correctly', async () => {
    await instance.createPerson('Sam3', 70, 170, {value: web3.utils.toWei('1', 'ether')});
    const person = await instance.getPerson();
    assert.equal(person[3], true);
  });

  it('should set the age correctly', async () => {
    await instance.createPerson('Sam4', 70, 170, {value: web3.utils.toWei('1', 'ether')});
    const person = await instance.getPerson();
    assert.equal(person[1], 70);
  });

  it('should allow owner to delete people(the owner is the first address in the accounts array)', async () => {
    await instance.createPerson('Sam5', 30, 170, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
    truffleAssert.passes(instance.deletePerson(accounts[0]), truffleAssert.ErrorType.REVERT);
  });

  // it('should not allow non-owner to delete people', async () => {
  //   await instance.createPerson('Sam5', 30, 170, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
  //   truffleAssert.fails(instance.deletePerson(accounts[1]), truffleAssert.ErrorType.REVERT);
  // });

  it('should allow owner to withdraw all balance', async () => {
    await instance.createPerson('Sam6', 18, 180, { from: accounts[2], value: web3.utils.toWei('1', 'ether') });
    truffleAssert.passes(instance.withdrawAll({ from: accounts[0] }));
  });

  it('should not allow not-owner to withdraw all balance', async () => {
    await instance.createPerson('Sam7', 18, 180, { from: accounts[2], value: web3.utils.toWei('1', 'ether') });
    truffleAssert.fails(instance.withdrawAll({ from: accounts[2]}), truffleAssert.ErrorType.REVERT);
  });

  it('should in crease contract balance when creating a new person', async () => {
    const contractAddress = await instance.address;
    const balanceBeforeCreatePerson = await web3.eth.getBalance(contractAddress);
    await instance.createPerson('Sam8', 18, 180, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
    const balanceAfterCreatePerson = await web3.eth.getBalance(contractAddress);
    assert(balanceAfterCreatePerson > balanceBeforeCreatePerson, 'Contract balance has not increased.');
  });

  it('should reset the contract balance to 0 after withdraw all', async () => {
    await instance.createPerson('Sam9', 18, 180, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
    await instance.withdrawAll();

    const contractBalance = parseFloat(await instance.balance());
    
    assert(contractBalance == web3.utils.toWei('0', 'ether'), 'Contract balance not equal to 0.');
  });

  it('should reset the blockchain balance to 0 after withdraw all', async () => {
    await instance.createPerson('Sam10', 18, 180, { from: accounts[2], value: web3.utils.toWei('1', 'ether') });
    await instance.withdrawAll();

    const blockchainBalance = await web3.eth.getBalance(instance.address);

    assert(blockchainBalance == web3.utils.toWei('0', 'ether'), 'Contract balance not equal to 0.');
  });

  it('should have contract balance equal to blockchain balance', async () => {
    await instance.createPerson('Sam11', 18, 180, { from: accounts[2], value: web3.utils.toWei('1', 'ether') });
    await instance.withdrawAll();

    const contractBalance = parseFloat(await instance.balance());
    const blockchainBalance = await web3.eth.getBalance(instance.address);
    
    assert(contractBalance == blockchainBalance, 'Contract balance not equal to blockchain balance.');
  });

  it('should increase the owner balance after withdraw all', async () => {
    await instance.createPerson('Sam12', 18, 180, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
    const balanceBeforeWithdraw = parseFloat(web3.utils.fromWei(await web3.eth.getBalance(accounts[0])));

    await instance.withdrawAll();
    const balanceAfterWithdraw = parseFloat(web3.utils.fromWei(await web3.eth.getBalance(accounts[0])));

    assert(balanceAfterWithdraw > balanceBeforeWithdraw, 'Owner balance has not increased.');
  });
});

Hey @Samy

I have tested you repo and the tests pass even by commening out this test

  // it('should not allow non-owner to delete people', async () => {
  //   await instance.createPerson('Sam5', 30, 170, { from: accounts[1], value: web3.utils.toWei('1', 'ether') });
  //   truffleAssert.fails(instance.deletePerson(accounts[1]), truffleAssert.ErrorType.REVERT);
  // });

What’s your error message?

In the test “should set correctly to senior”, Filip uses the age 65 in the instance to confirm it is a senior, which makes sense because 65 is a senior, however, when I tested my test by flipping the if age >= 65 to if age is <= 65, the test still succeeded because both of the codes include =65, so would it be a better idea to run the test with an instance of an age larger than 65 just incase the mistake someone makes in their code is accident flipping the < >?

In the image above, I don’t see the “should not allow non-owner to delete people” test to be run. Maybe it was skipped for some reason… With that test commented I get the same result(12 passing), but when I uncomment it, I get the following:

And “should not allow non-owner to delete people” is the last passing test. It makes the other tests fail…

1 Like