What I learned from testing complex smart contracts
In a previous article, we provide a simple guide on how to write unit tests for smart contracts. Luckily, we can use our favorite tools to test smart contracts: the Mocha framework and Chai assertion library.
Personally, I love writing tests. There is something satisfying about seeing those little green check marks in your terminal. However, when you come across a function with 3 modifiers,
7 require
statements, 3 if
statements, and then if
statements nested in if
statements, things can get tricky. That leads to so many possible combinations that your head starts to spin.
Mapping out your unit tests before writing any code definitely helps. As I mapped out the functions I was working on, I decided to nest my describe
statements. This method helped me to keep my tests organized and minimized the amount of code I needed to write.
Let’s see a simplified example.
Rather than writing several long it
statements to account for every possible scenario :
describe('buyTokens(_amount)', function () {
it('it should return true if contract has been activated, contract is not paused, when offering period has started, when offering period has not ended', async function () {
// test code
}); it('it should transfer tokens if contract has been activated, contract is not paused, when offering period has started, when offering period has not ended', async function () {
// test code
}); it('it should revert if contract has not been activated, contract is not paused, when offering period has started, when offering period has not ended', async function () {
// test code
}); it('it should revert if contract has been activated, contract is paused, when offering period has started, when offering period has not ended', async function () {
// test code
}); it('it should revert if contract has been activated, contract is not paused, when offering period has not started, when offering period has not ended', async function () {
// test code
});
});
You can use the nesting method :
describe('buyTokens(_amount)', function () {
describe('when the contract is activated', function () {
describe('when the contract is not paused', function () {
describe('when the offering period has started', function () {
describe('when the offering period has not ended', function () {
it('it should return true', async function () {
// test code
});
it('it should transfer tokens', async function () {
// test code
});
});
describe('when the offering period has ended', function () {
it('it should revert', async function () {
// test code
});
});
});
describe('when the offering period has not started', function () {
// further describes or it tests
});
});
describe('when the contract is paused', function () {
// further it tests
});
});
it('it should revert if contract has not been activated', async function () {
// unit test
});
});
I started with addressing modifiers
, then require
statements, and lastly the if
statements. I nested my describe statements until I got the test to pass, and then worked my way out. In the example above, I knew that in order to get the test to pass (effectively returning true and transferring tokens), I needed to set my test up to have the contract activated, not paused, the offering period started and not ended. Once I got down to the level that would pass the test, I worked my way out. The describe statement outside the it should return true
and it should transfer tokens
functions was:
when the offering period has not ended
I added a describe
at the same level to address what would happen conversely:
when the offering period has ended
I continued to work my way up each level until I addressed the very first describe
function: when the contract is activated,
and created a test for when the contract is not activated
.
I encourage you to try this method next time you are testing complex smart contracts. Nesting and using the beforeEach
function helps to eliminate duplicate code, and helps you stay organized.
How do you structure your tests? Comment below if you have any tips or tricks!
☞ To stay up to date with our work with smart contract development, follow us on Medium and Twitter.
☞ Need help with writing tests? Contact us at team@upstate.agency!