This suite of test cases is the complete demonstartion
of simple Ethereum testing system for Solidity smart
contracts.
- No Peer end point required
- Start Testing in 3 seconds.
- Completely based javascript
- Paralel running
- Cool report generated:
- Running smooth on this OS list.
To define a test in that suite paradigm
all you need is the test case file and
the json file to configure the sandbox
to run it:
[test-folder]
|-- .
|-- math-test.js
|-- ethereum.json
git clone https://github.com/ether-camp/ethereum-testing-reference
cd ethereum-testing-reference
npm install
npm test
git clone https://github.com/ether-camp/ethereum-testing-reference
cd ethereum-testing-reference
npm install
npm install mocha -g
mocha test test/math/math-test.js
yes you can and obviously on any
continius integration system.
here is demo sample, just for
that subject:
https://github.com/ether-camp/ethereum-test-travis
- Place
Math.sol
in directorycontract
- Test it using this code:
var Workbench = require('ethereum-sandbox-workbench');
var workbench = new Workbench();
workbench.startTesting('Math', function(contracts) {
it('sum-test', function() {
...
});
});
Testing for simple contract calls like:
function sum(uint a, uint b) returns (uint result){
result = a + b;
}
Testing for contract state management.
The simple voting system:
function voteYes(){
if (finished) throw;
if (voted[msg.sender]) throw;
voted[msg.sender] = true;
++votedYes;
}
This is a MultiSig wallet contract, similar to the one mist uses.
The only difference is that on MultiSig transactions requiring multiple confirmations,
the requests can arrive on different blocks.
This test case also demonstrates how to test events.
Contract call to contract,
NameReg usage...
While doing testing we want to focus mostly on
our buisness logic and forget everything that
is related to infrastrucure: no network , no
peers and as much as possible no config involved.
The perfect test case should look like this:
1. Start Sandbox
2. Deploy contract
3. Test method-a call
4. Assert results.
5. Test method-b call
6. Assert results.
...
end. Tear all down
// Compile the contracts
var compiled = helper.compile('./contract', ['Math.sol']);
// Before starting any testase
sandbox.start(__dirname + '/ethereum.json', done);
// Deploy the contract transaction
sandbox.web3.eth.contract(JSON.parse(compiled.contracts['Math'].interface)).new({
/* contract creator */
from: "0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826",
/* contract bytecode */
data: '0x' + compiled.contracts['Math'].bytecode
}, function(err, contract) {
// callback for deployment finish
if (err) {
done(err);
} else if (contract.address){
// save contract reference
// in global var
math = contract;
done();
}
});
Check the full example here: ethereum.json
"0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826": {
"name": "fellow-1",
"balance": 1000000000000000000000000,
"nonce": "1430",
"pkey": "cow",
"default": true
}
"0x0860a8008298322a142c09b528207acb5ab7effc": {
"balance": 0,
"source": "predeployed/name_reg.sol"
}
When starting the workbench, it supports the following configuration object:
var Workbench = require('ethereum-sandbox-workbench');
var workbench = new Workbench({
defaults: { //defaults object passed to ether-pudding
from: '0x' //i.e., default from address for contract transactions
},
contractsDirectory: '', //the directory in which the .sol files are stored
ethereumJsonPath: '', //if not exists, the generated ethereum.json will be stored here
initialState: {} //possible to specify ethereum.json state here instead of in the file
});
The workbench has a mocking framework. It creates a proxy contract that has the same interface but the responses are tailored to your choosing.
- Create a new proxy using
newMock
. - Use
mockCallReturnValue
with the wanted return value on the mocked function.
var Workbench = require('ethereum-sandbox-workbench');
var workbench = new Workbench({
defaults: {
from: '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826'
}
});
workbench.startTesting('GoldPrice', function(contracts) {
it('tests-mock', function() {
return contracts.GoldPrice.newMock()
.then(function(contract) {
return mockContract.price.mockCallReturnValue(299);
})
.then(function(receipt) {
return mockContract.price.call();
})
.then(function(value) {
assert(value.equals(299));
});
});
});
- Create a new proxy using
newMock
. - Use
mockTransactionForward
with the wanted transaction response on the mocked function. Works with the following options object:
{
data: '', //if data is supplied, this is what is sent and other fields are ignored.,
//if data is not supplied, use the following fields:
contract: '', //contract object returned from the workbench
functionName: '', //function to mock
args: '' //arguments to pass in the forwarded transaction
}
var Workbench = require('ethereum-sandbox-workbench');
var workbench = new Workbench({
defaults: {
from: '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826'
}
});
workbench.startTesting(['GoldPrice', 'GoldPriceChecker'], function(contracts) {
it('tests-mock', function() {
return contracts.GoldPrice.newMock()
.then(function(contract) {
return mockContract.notifyCallback.mockTransactionForward('0x11111111111111111111111111111111', {
contract: goldPriceChecker, //goldPriceChecker was created with `new`
functionName: 'setCallbackPrice',
args: [23]
});
//now when notifyCallback is invoked in a transaction, it will send a follow-up transaction to the goldPriceChecker contract
});
});
});
- Create a new proxy using
newMock
. - Use
mockCallReturnValue
ormockTransactionForward
as in previous example with an additional parameter as a list of arguments.
var Workbench = require('ethereum-sandbox-workbench');
var workbench = new Workbench({
defaults: {
from: '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826'
}
});
workbench.startTesting('GoldPrice', function(contracts) {
it('tests-on-args', function() {
var mockContract;
return contracts.GoldPrice.newMock()
.then(function(contract) {
mockContract = contract;
return mockContract.getPriceWithParameter.mockCallReturnValue(10);
})
.then(function(receipt) {
return mockContract.getPriceWithParameter.mockCallReturnValue(20, [5]);
})
.then(function(receipt) {
return mockContract.getPriceWithParameter.call(500);
})
.then(function(value) {
assert(value.equals(10));
return mockContract.getPriceWithParameter.call(5);
})
.then(function(value) {
assert(value.equals(20));
});
});
});
- Create a new proxy using
newMock
withoptions.traceFunctionCalls = true
. - Use
wasCalled
on the transaction receipt to see if the function was called in that transaction.
var Workbench = require('ethereum-sandbox-workbench');
var workbench = new Workbench({
defaults: {
from: '0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826'
}
});
workbench.startTesting('GoldPrice', function(contracts) {
it('tests-tracing', function() {
var mockContract;
return contracts.GoldPrice.newMock()
.then(function(contract) {
mockContract = contract;
return mockContract.getPriceWithParameter(5);
})
.then(function(txHash) {
return workbench.waitForReceipt(txHash);
})
.then(function(receipt) {
var result = mockContract.getPriceWithParameter.wasCalled(receipt);
assert(result.called);
assert(result.args[0].toString(), '5');
});
});
});
For an extensive example, check out mock-test.js.
We are looking for people who wants to
add testcases demonstrating best practice,
good bitcoins promised for help.
Ask ether.camp slack.
OS Name | Version |
---|---|
Ubuntu | 14.04+ |
Fedora | 24 |
CentOS | 7 |
Max | El Capitan, 10.11.5 |
Windows | (Not supported Yet) 😁 |
*. node.js ver >= 4
Ping us on Slack