https://github.com/digixglobal/contest
[deprecated] Delightful testing and scripting for Web3 contracts
https://github.com/digixglobal/contest
Last synced: 3 months ago
JSON representation
[deprecated] Delightful testing and scripting for Web3 contracts
- Host: GitHub
- URL: https://github.com/digixglobal/contest
- Owner: DigixGlobal
- License: bsd-3-clause
- Created: 2016-10-20T03:40:42.000Z (over 8 years ago)
- Default Branch: master
- Last Pushed: 2017-06-14T08:47:11.000Z (about 8 years ago)
- Last Synced: 2025-03-14T02:42:37.277Z (3 months ago)
- Language: JavaScript
- Homepage:
- Size: 80.1 KB
- Stars: 7
- Watchers: 3
- Forks: 4
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Contest
☠️ Contest is now deprecated. We recommend using node 7.10 + and the `async` pattern.
### Web3 contract scripting and assertions
Simplified API for testing contracts; generates mocha tests.
## Features
* Minimalist syntax for contract testing
* Handle thrown calls
* Assert transaction success/failure
* Easily assert events data## Installation
You should be using truffle.
```
npm install --save-dev @digix/contest
``````javascript
import Contest from '@digix/contest';
const contest = new Contest({ debug: true, timeout: 2000 }); // `debug` defaults to false
```## Usage
Contest wires up a series of promises for you with a convenient chaining syntax.
Each chain should end in `done` - see the example below for usage.
Before calling methods, you must have a contract deployed:
* Contract
* `.artifact(truffleArtifact)` for contracts deployed with truffle v3
* `.deploy(contract, [ params ])` deploys a new instance of contract with given params
* `.use(contractInstance)` use an existing instance of a an already-deployed contractOnce you have set a contract you can begin scripting against it:
* Methods
* `.call(method, statement, samples)` call method with a series of statements
* `.tx(method, statement, samples, transformers)` same as above, initiate transaction
* If the first word in `statement` is `throw`, contest will expect the call/tx to throw.
* `samples` in the format `[ sample, sample, sample ]`, or pass a single `sample` for a single test
* `sample` for non-assertions (call/throw), use format `[input1, input2]`, for assertions, use `[[input1, input2], [output1, output2]]`
* `output` expected output to match; if output is a function, it will consume the method's output and resolve true/false
* `transformers` an array of functions that transform the outputs before asserting; e.g. `[v => v.toNumber(),v => '0x' +v]`
* Events
* `.watch(method, statement, samples)` the next block must be a `tx`, it will match each sample in samples
* `sample` for `watch` is in the format `{ _param1: output1, _param2: output2 }`
* Misc
`.wait(blocks, seconds)` alias for [tempo's waitForBlocks](https://github.com/DigixGlobal/tempo)
* Test
* `.describe(description)` new describe block; for organixation only
* `.then(promise)` return a promise or execute arbitrary code
* `done()` end each chain with `done` to execute chain## Helpers
Contest also includes some common test helpers related to Ethereum. See `./src/helpers.js` for details.
* `BIG_INT` BigNumber string representing maximum (256 integer)
* `BIG_INT_MINUS_TWO`
* `ONE_DAY_IN_SECONDS`
* `asyncIterator(iterator, fn, callback)`
* `randomInt(min, max)`
* `randomHex(length, prefix)` - (`prefix` bool adds `0x`)
* `randomAddress(prefix)`Import them as such: `import { BIG_INT, randomHex } from '@digix/contest/src/helpers';`
## Example
```javascript
const MetaCoin = artifacts.require('./MetaCoin.sol');new Contest()
.artifact(MetaCoin)
// create a describe block to oragnise tests
.describe('Account Balances')
// pass an object to assert key/value paris for a method
.call('getBalance', 'initializes with correct balances', {
[accounts[0]]: balance, // ES6 => { '0x123': 100, '0x456': 0 }
[accounts[1]]: 0, // will envoke and compare result of getBalance('0x456')
})
// notice that we're passing an array here instead of an object, use multi-input-output syntax
.call('getBalance', 'some other statement', [
[[accounts[0]], [bal => bal > 2]], // pass a function to resolve to `true` rather than equality assertion
[[accounts[1]], [0],
])
.describe('Library Import')
// call a different method
.call('getBalanceInEth', 'returns correct value from inherited method', {
[accounts[0]]: balance,
[accounts[1]]: 0,
})
// use `tx` to create a transaction. multiple transactions are executed in series
.tx('sendCoin', 'transfer succeeds', [
[accounts[1], 10, { from: accounts[0] }],
[accounts[0], 10, { from: accounts[1] }],
})
.describe('Transfer balances')
// listen for events with `watch`. it will match the outputs series with the next `tx` block
.watch('Transfer', 'fires the events correctly', [
{ _from: accounts[0], _to: accounts[1], _value: 10 },
{ _from: accounts[1], _to: accounts[0], _value: 10 },
{ _from: accounts[0], _to: accounts[1], _value: 10 },
])
.tx('sendCoin', 'transfer succeeds', [
[accounts[1], 10, { from: accounts[0] }],
[accounts[0], 10, { from: accounts[1] }],
[accounts[1], 10, { from: accounts[0] }],
])
// if you don't pass a 2nd statement param, it will not create a test, but will executed before the next block with a statements
// the next block will do a series back and forth transactions without asserting
.tx('sendCoin', [
[accounts[1], 10, { from: accounts[0] }],
[accounts[0], 10, { from: accounts[1] }],
[accounts[1], 10, { from: accounts[0] }],
[accounts[0], 10, { from: accounts[1] }],
])
// the keyword `throws` will cause the test pass only if the method throws
.tx('sendCoin', 'throws when using bad numbers' [
[accounts[1], -22, { from: accounts[0] }],
})
.call('getBalance', 'balances after transaction are correct', {
[accounts[0]]: balance - transfer,
[accounts[1]]: transfer,
})
.done();
```## Roadmap
* Global config for re-runs (e.g. try different gas amounts on every test
* Generate tests from Cucumber? Imagine a future where contracts are verified against english as such:```cucumber
Scenario: Interacting with ResolverClientScenario: Non admin fails to gain access
Given I am Jeff
And I use the contract ResolverClient
Then I cannot register contractScenario: Non admin fails to gain 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'
```## Tests
* Mocked contract: `npm run test`
* Truffle environment: `cd ./test/truffle; testrpc & truffle test`## License
BSD-3-Clause, 2016