1

I have the following simple smart contract defined:

contract LoanApplication {

  struct Applicant {
    bytes32 ssn;
    bool hasApplied;

  }

  struct Loan {
    string loanID;
    uint applicantCount;
    uint256 rate;
    uint term;
  }

 mapping (address => Applicant) public applicants;

 Loan[] public loans;

 address public loansAdmin;

 constructor(string[] memory loanIDs) {

    loansAdmin = msg.sender;

   for (uint i=0; i < loanIDs.length; i++) {
        // Create Loan object and append to loans array
        loans.push(Loan({
            loanID: loanIDs[i],
            applicantCount:0,
            rate: 10,
            term: 30
            }));
    }
 
 }

 function receiveApplication(address applicant) external {
    require(
        msg.sender == loansAdmin, "Only administrator can receive application"
        );

     require(
        !applicants[applicant].hasApplied,
        "Applicant previously applied"
        );

    applicants[applicant].hasApplied = true;

 
   }

}

This is my Javascript test for it:

const LoanAppContract = artifacts.require("LoanApplication");

contract("LoanApplicationTest", accounts => {


    const loanIDs = ["Loan1", "Loan2", "Loan3"];


    beforeEach (async () =>  {
       loanApp = await LoanAppContract.new(loanIDs);
    });
     describe("initialization", () => {
        ...


      });



     describe("register application",() => {
        it("receive app",  async() => {


          let applicantAddr = accounts[1]; 
          console.log("applicantAddr=" + applicantAddr);

          let applicant = await loanApp.applicants(applicantAddr);


          assert.isTrue(!applicant.hasApplied);


          await loanApp.receiveApplication(applicantAddr, {from: accounts[0]});


          let applicantRefreshed = await loanApp.applicants(applicantAddr);

          //assert.isTrue(applicant.hasApplied);

          assert.isTrue(applicantRefreshed.hasApplied);


    });

  });

});

I am trying to test out the receiveApplication function in the contract. The loans Administrator sets the hasApplied flag in the application object corr. to the address passed in to true

My question is for these lines:

// Obtain applicant 
let applicant = await loanApp.applicants(applicantAddr);

// Verify that hasApplied is set to false
assert.isTrue(!applicant.hasApplied);

Now call the receiveApplication function. The hasApplied flag is now set to true

 await loanApp.receiveApplication(applicantAddr, {from: accounts[0]});

My question is this:

Why does this test fail ?

I'm expecting

assert.isTrue(applicant.hasApplied)

to succeed, indicating that hasApplied is true, but it seems like the applicant reference is stale ?

Instead I have to do

let applicantRefreshed = await loanApp.applicants(applicantAddr);
assert.isTrue(applicantRefreshed.hasApplied);

for the test to succeed. I'm fairly new to Solidity and Javascript - been a Python and Java developer to date.

Appreciate help to clarify this.

I’m running my test using truffle, thx

2
  • Your question is missing an important detail - which test framework and web3 library are you using?
    – cameel
    Jan 9 at 9:15
  • I’m using truffle thx
    – femibyte
    Jan 9 at 12:38

1 Answer 1

2

Essentially because you are already using an abstraction layer that gives you what you need, when you need, giving you fresh values when called. It's up to the abstract instance of the contract (loanApp in your case) to work behind the scene, call the node via RPC to know the state and send back the result to you. And this is why you need await and you cannot use the value just in time.

The smart contract can be modified externally by the developer application/testing environment, so trying to keep a sort of live sync is not trivial. You can try Drizzle for example to do that, but in any case, it's dedicated to the UI, not to the test and development phase.

Anyway, if you want a more succinct syntax for your case without the need to use many temp variables, you can write something like this:

let applicantAddr = accounts[1]; 
console.log("applicantAddr=" + applicantAddr);
assert.isTrue(!(await loanApp.applicants(applicantAddr)).hasApplied);
await loanApp.receiveApplication(applicantAddr, {from: accounts[0]});
assert.isTrue((await loanApp.applicants(applicantAddr)).hasApplied);
1
  • Ok this is helpful and informative. Thx for the more succinct code suggestion too
    – femibyte
    Jan 9 at 12:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.