This article discusses the importance of software testing, particularly for Ethereum smart contracts, and provides examples of how to test smart contracts using Solidity and JavaScript.
Abstract
The article begins by emphasizing the importance of software testing to ensure code works as intended. It introduces two types of software tests: unit tests and integration tests. The article then focuses on blockchain software testing, specifically Ethereum smart contracts, using the Truffle suite. The author suggests using both Solidity and JavaScript tests for comprehensive testing. The article provides an example project with two smart contracts, Background and EntryPoint, and demonstrates how to write unit tests, integration tests, and JavaScript tests for these contracts. The conclusion emphasizes the importance of thorough testing for smart contracts, and the article ends with further reading suggestions.
Bullet points
Software testing is crucial for ensuring code works as intended.
There are two types of software tests: unit tests and integration tests.
Blockchain software, like Ethereum smart contracts, requires thorough testing due to immutability.
The Truffle suite allows for testing smart contracts using Solidity and JavaScript.
Solidity tests can be used for both unit tests and integration tests at the blockchain level.
JavaScript tests are used for integration tests at the DApp level.
The article provides an example project with two smart contracts, Background and EntryPoint, and demonstrates how to write tests for these contracts.
The conclusion emphasizes the importance of thorough testing for smart contracts.
Further reading suggestions are provided at the end of the article.
How to Test Ethereum Smart Contracts
Test your Smart Contracts with Solidity and JavaScript
Prerequisites: A basic understanding of Blockchain, Ethereum and Javascript.
The full working project code can be found on Github.
The Importance of Software Testing
If you want code to work the way it’s intended to, software testing is paramount.
There are two general types of software test: unit tests and integration tests.
Unit tests focus on each function in isolation.
Integration tests focus on ensuring multiple parts of the code work together as expected.
Blockchain software is no different. It might be argued that Blockchain applications require more emphasis on testing, due to immutability.
Blockchain Testing
Truffle suite gives us two avenues for testing solidity smart contracts: solidity tests and JavaScript tests. The question is, which should we use?
The answer is both.
Figure 1: Test structure diagram
Solidity tests
Writing tests in Solidity gives us the ability to run Blockchain layer tests. They allow tests to call contracts and functions as if they were on the Blockchain themselves. To test the internal behaviour of smart contracts we can:
Write unit tests tocheck function return values and state variable values.
Write integration tests that test the interactions between contracts. These ensure that mechanisms such as inheritance and dependency injection are functioning as expected.
JavaScript tests
We also need to make sure that smart contracts exhibit the right external behavior. To test smart contracts from outside the Blockchain, we use Web3js, just as our DApp would. We need to have confidence that our DApp front end will work properly when calling the smart contracts. These fall under integration tests.
We have two smart contracts: Background and EntryPoint.
Background is an internal contract that our DApp front-end doesn’t interact with. EntryPoint is the contract which is designed for our DApp to interact with. It references Background in its code.
The smart contracts
Above, we see our Background contract. It exposes three functions: storeValue(uint), getValue(uint), and getNumberOfValues(). All these functions have simple instructions, so they’re easy to unit test.
This is our EntryPoint contract. An address for our Background contract is injected into the constructor and used as a state variable, named backgroundAddress. EntryPoint exposes three functions: getBackgroundAddress(), storeTwoValues(uint, uint), and getNumberOfValues().
storeTwoValues(uint, uint) calls a function in the Background contract twice, so unit testing this function in isolation will prove difficult. The same goes for getNumberOfValues(). These are good cases for integration tests.
Solidity
In Solidity, we’re going to write unit testsandintegration testsfor our smart contracts. Let’s start with unit tests since they’re the simpler of the two.
Here’s our first unit test: TestBackground:
It tests our Background contract to make sure it:
Stores a new value in its values array.
Returns values by their index.
Stores multiple values in its values array.
Returns the size of its values array.
This is TestEntryPoint, with a unit test called testItHasCorrectBackground() for our EntryPoint contract:
This function tests the dependency injection. As mentioned earlier, the other functions in our EntryPoint contract require interaction with Background so we cannot test them in isolation.
These functions are tested in our integration tests:
We can see that TestIntegrationEntryPoint uses an extension of Background called BackgroundTest , defined on line 43, to act as our mock contract. This enables our tests to check if EntryPoint calls the correct functions in the backgroundAddress contract it references.
Javascript test files
In JavaScript, we write an integration test to make sure the contracts act as we expect, so that we can build a DApp which uses them.
Here’s our JavaScript test, entryPoint.test.js:
Using the functions available in our EntryPoint contract, the JavaScript tests ensure that values from outside the Blockchain can be sent to the smart contract by creating transactions targeting the storeTwoValues(uint, uint) function (line 15). Retrieving the number of values stored on the Blockchain by calling getNumberOfValues() on lines 12 and 16 of the tests ensure that they get stored.
Conclusion
When it comes to testing smart contracts, the more the merrier. There should be no stones left unturned when ensuring all possible paths of execution return expected results. Use Blockchain level Solidity tests for unit tests and integration tests, and use Javascript tests for integration tests at the DApp level.
There are points in this project where more unit or integration tests could have been written, so if you think you can add to this project, by all means, submit a pull request to the repo on Github!
Further Reading
If you found this tutorial useful, you may find these tutorials useful for your development as well: