Digix Dev Tool Release — Contest

Chris Hitchcott (Digix Core Dev)

This week I’ve been continuing the work on testing; working through the Digix Core 2 contracts and writing tests in truffle along with Sigmate and Tempo.

One thing I noticed during testing was that (whilst truffle does a lot to help by converting callbacks to promises) I was still writing a lot of boilerplate code for common tasks.

For example:

describe('someMethod - returns true under X condition', function() { 
it('returns true when passing a unsigned integer', function() {
someContract.someMethod.call(12)
.then((res) => assert.equal(res, true))
.then(() => someContract.someMethod.call(15))
.then((res) => assert.equal(res, true))
.then(() => someContract.someMethod.call(1234))
.then((res) => assert.equal(res, true))
.catch((err) => assert.ifError(err));
});
it('returns true when passing a string', function() {
someContract.someMethod.call('aa')
.then((res) => assert.equal(res, false))
.then(() => someContract.someMethod.call('bb'))
.then((res) => assert.equal(res, true))
.then(() => someContract.someMethod.call('cc'))
.then((res) => assert.equal(res, true))
.catch((err) => assert.ifError(err));
});
it('fails when passing a negative integer', function() {
someContract.someMethod.call(-1)
.then((res) => assert.fail('did not throw'))
.catch((err) => assert.ok(err));
});
})

As you can imagine, if you are testing a large number of methods, it can take an annoying amount of time to write the boilerplate code above for every single test and every single condition. You also have to be sure you’re using catch and then for each assetion to make sure you don't miss any failing tests.

As part of Digix’s quest to create better tooling for Ethereum, I’ve created a tool called Contest (contract testing — get it?) that abstracts the promises away into a clean and declarative API.

Now you can write the above like this:

contest.suite(myContract.someMethod, 'someMethod - returns true under X condition', [
['assert returns true when passing a unsigned integer', [
[[12], true],
[[15], true],
[[1234], true],
]],
['assert returns true when passing a string', [
[['aa'], true],
[['bb'], true],
[['cc'], true],
]],
['throw fails when passing a negative integer', [
[-1], [-3], [-133],
]],
])

Much more readable, no? It’s also easier to create iterative tests because you don’t need to worry about setting up control-flow; just pass in an array the promises will be wired up for you.

There’s a whole bunch of extra features not shown here including output transformation, event assertions and a shorthand API, so check out the github page if you are interested in learning more.

Creating this kind of minimal boilerplate-less library for writing contract assertions has vastly reduced time taken to write tests and will make future test-driven contract development a lot less annoying in the future.

Speaking of tests, the Digix 2.0 core contracts are getting ever closer to 100% coverage…

Test Suite running for DGX 2.0

Looking forward, it’s not hard to see how testing contracts could evolve even further; perhaps we can generate and verify tests automatically from something like the gherkin syntax:

Scenario: Interacting with ResolverClient
Scenario: Non admin fails to gain access
Given I am Jeff
And I use the contract ResolverClient
Then I cannot register contract
Scenario: Admin gains access
Given I am Ace
And I use the contract ResolverClient
And I register contract 'a:gold' as '0x123...def'
Then get contract 'a:gold' is '0x123...def'