1

I am using Mocha and Chai for writing tests for a smart contract deployed on the development blockchain with truffle.

I have a contract named Election which contains two candidates.

The test code is as follows:

it("Checking the properties for candidates", () => {
    return Election.deployed().then((app) => {
      return [app.candidates(1), app];
    }).then(params => {
      const [candidate1, app] = params;
      assert.equal(candidate1.id, 0);
      return [app.candidates(1), app];
    }).then(params => {
      const [candidate2, app] = params;
      assert.equal(candidate2.id, 1);
    });
  });

The test cases pass when I am not using the array destructuring to return app.candidates() and an instance of the app. In that case I had to declare a global variable, assign it to app and use it in every scope. But I want to avoid defining a global variable. I came across this post on SO which suggests using ES6 destructuring.

But here I am getting candidate1.id and candidate2.id both as undefined.

What am I doing wrong here?

Abrar
  • 6,874
  • 9
  • 28
  • 41
  • Does `app.candidates(…)` return a promise? If so, you were forgetting a call to `Promise.all`. If not, you don't need to chain multiple `then` callbacks at all, just put everything in a single one! – Bergi Jan 01 '19 at 14:57
  • 1
    Closely related: [How do I access previous promise results in a `.then()` chain?](https://stackoverflow.com/q/28250680/1048572) – Bergi Jan 01 '19 at 14:57
  • `app.candidates()` does not return promise. The link is helpful but I am already using `return` as shown in that link – Abrar Jan 01 '19 at 17:12
  • If it doesn't return a promise, then what does it return? Does it return a candidate object with an `id` property? – Bergi Jan 01 '19 at 17:15
  • Yes it returns an object with id property – Abrar Jan 01 '19 at 17:18
  • 1
    Are you sure? Because then the code should work as is. Try logging the values before you put it in the array and and after destructuring. – Bergi Jan 01 '19 at 17:31
  • Hey @Bergi. My bad. `app.candidates()` returns a promise. I was wrong assuming it otherwise – Abrar Jan 01 '19 at 17:32

1 Answers1

2

Why are you returning from an it? It's not needed, they should only throw.

I strongly recommend avoiding this .then syntax and npm i chai-as-promised --save-dev then install it like this:

const chai = require('chai');
const chaiAsPromised = require('chai-as-promised');

// Must install Chai as Promised last - https://github.com/domenic/chai-as-promised/#node
chai.use(chaiAsPromised);

// Disable object truncating - https://stackoverflow.com/a/23378037/1828637
chai.config.truncateThreshold = 0;

global.expect = chai.expect;

Then you would do:

it("Checking the properties for candidates", async () => {
    expect(Election.deployed()).to.eventually.satisfy(app => app.candidates(1).id === 0)
        and.satisfy(app => app.candidates(2).id === 1);
});

If app.candidates returns a promise, maybe can even do this, I'm not sure about async function to argument of satisfy though.

it("Checking the properties for candidates", async () => {
    expect(Election.deployed()).to.eventually.satisfy(async app => {
        await expect(app.candidates(1)).to.eventually.be.an('object').that.has.property('id', 0);
        await expect(app.candidates(2)).to.eventually.be.an('object').has.property('id', 1);
    });
});
Noitidart
  • 35,443
  • 37
  • 154
  • 323
Kyefus
  • 546
  • 5
  • 5
  • 1
    "*Why are you returning from an it?*" - I'm pretty sure you need to return a promise as otherwise the test doesn't know when it is finished. – Bergi Jan 01 '19 at 17:17
  • The code definitely looks cleaner without the `then` usage. But I was trying to utilize the es6 array destructing to return multiple values from a promise – Abrar Jan 01 '19 at 17:19
  • 1
    @Abrar it seems you only need multiple values because you want access to the `app` argument. But with this change you don't need the multiple values. – Kyefus Jan 01 '19 at 17:22
  • @Bergi Thanks for the catch, I made the it callback `async` now. – Kyefus Jan 01 '19 at 17:23
  • Thanks @Kyefus. It worked! Just to clarify I was wrong assuming that `app.candidates()` does not return a promise. My bad – Abrar Jan 01 '19 at 17:31
  • @Abrar did that `async satisfy` work I'm curious, I didn't know that would work. – Noitidart Jan 01 '19 at 17:49
  • 1
    Yes, the tests passed with `async satisfy` – Abrar Jan 01 '19 at 18:20
  • Oh amazing thank you for sharing @Abrar and Kyefus!! @Abrar there is actually a bug in the `async satisfy` you need an `await` on each of those `expect`s. I edited the solution above. – Noitidart Jan 02 '19 at 00:55
  • 1
    That's weird. The tests actually passed without throwing any errors without using `async`. Any idea why that happened? @Noitidart – Abrar Jan 02 '19 at 03:58
  • @Abrar the tests may pass, but would have asynchronously went ahead and then completed retroactively. With the `await`s now, the tests that come after will wait for the older to complete. OR `satisfy` does not support async tests. Can you try to make one of the expects inside `satisfy` fail and see if the fail happens before other tests that come after it? Also can provide `--bail` option in `mocha.opts` or CLI when running `mocha test` to make it abort testing after first error, lets see if it aborts after first error within `satisfy`. – Noitidart Jan 02 '19 at 12:10