├── .gitignore ├── migrations ├── 1_initial_migration.js └── 2_deploy_simple_storage.js ├── contracts ├── SimpleStorage.sol └── Migrations.sol ├── test └── simple_storage.js ├── README.md ├── truffle-config.js └── steps.md /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | notes 3 | -------------------------------------------------------------------------------- /migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | const Migrations = artifacts.require("Migrations"); 2 | 3 | module.exports = function (deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | -------------------------------------------------------------------------------- /migrations/2_deploy_simple_storage.js: -------------------------------------------------------------------------------- 1 | const SimpleStorage = artifacts.require("SimpleStorage"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(SimpleStorage); 5 | } 6 | -------------------------------------------------------------------------------- /contracts/SimpleStorage.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: MIT 2 | pragma solidity >=0.5.0 <0.8.0; 3 | 4 | contract SimpleStorage { 5 | uint256 public storedData; 6 | 7 | function getStoredData() public view returns (uint256) { 8 | return storedData; 9 | } 10 | 11 | function setStoredData(uint256 x) public { 12 | storedData = x; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >=0.4.22 <0.8.0; 3 | 4 | contract Migrations { 5 | address public owner = msg.sender; 6 | uint public last_completed_migration; 7 | 8 | modifier restricted() { 9 | require( 10 | msg.sender == owner, 11 | "This function is restricted to the contract's owner" 12 | ); 13 | _; 14 | } 15 | 16 | function setCompleted(uint completed) public restricted { 17 | last_completed_migration = completed; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/simple_storage.js: -------------------------------------------------------------------------------- 1 | const SimpleStorage = artifacts.require("SimpleStorage"); 2 | 3 | contract("SimpleStorage", function (accounts) { 4 | describe("Initial deployment", async () => { 5 | it("should assert true", async function () { 6 | await SimpleStorage.deployed(); 7 | assert.isTrue(true); 8 | }); 9 | 10 | it("was deployed and it's intial value is 0", async () => { 11 | // get subject 12 | const ssInstance = await SimpleStorage.deployed(); 13 | // verify it starts with zero 14 | const storedData = await ssInstance.getStoredData.call(); 15 | assert.equal(storedData, 0, `Initial state should be zero`); 16 | }); 17 | }); 18 | describe("Functionality", () => { 19 | it("should store the value 42", async () => { 20 | // get subject 21 | const ssInstance = await SimpleStorage.deployed(); 22 | 23 | // change the subject 24 | await ssInstance.setStoredData(42, { from: accounts[0] }); 25 | 26 | // verify we changed the subject 27 | const storedData = await ssInstance.getStoredData.call(); 28 | assert.equal(storedData, 42, `${storedData} was not stored!`); 29 | }); 30 | }); 31 | }); 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Explore the full lifecycle of developing a Dapp 2 | 3 | This document is the accompanying notes for [Episode 4 | 1](https://trfl.co/truffle-webinar-series1) of our webinar series. 5 | 6 | ![Img 1](https://trufflesuite.com/img/events/webinar-livestream2.jpg) 7 | 8 | ## Resources 9 | 10 | - [Transcript](./steps.md) of the live code session. 11 | 12 | ## Code challenge 13 | 14 | Implement the following new behavior in the contract following TDD: 15 | 16 | - The contract should have the concept of an owner. **Note** that truffle uses the first account to sign deployments. 17 | - Add the ability to keep track of each time an address calls `setStoredData`. This will need: 18 | - a 19 | [mapping](https://solidity.readthedocs.io/en/v0.7.1/types.html#mapping-types) 20 | `mapping[address => uint]` and update it every time someone calls 21 | `setStoredData` 22 | - a function `getCount(address _address)` that returns the count associated with `_address` 23 | - a [modifier](https://solidity.readthedocs.io/en/v0.7.1/structure-of-a-contract.html#function-modifiers) to guard that only the owner can call `getCount` which should [revert](https://solidity.readthedocs.io/en/v0.7.1/control-structures.html#revert) when invoked with the wrong caller. 24 | - This [Truffle Assertion plugin](https://github.com/rkalis/truffle-assertions) which helps test for revert. 25 | 26 | ## Contributors 27 | 28 | A heartfelt thanks to [Angela](https://twitter.com/astarinmymind_) and [Francis](https://github.com/fodisi) for their reviews, feedback and testing. :bow: 29 | -------------------------------------------------------------------------------- /truffle-config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Use this file to configure your truffle project. It's seeded with some 3 | * common settings for different networks and features like migrations, 4 | * compilation and testing. Uncomment the ones you need or modify 5 | * them to suit your project as necessary. 6 | * 7 | * More information about configuration can be found at: 8 | * 9 | * truffleframework.com/docs/advanced/configuration 10 | * 11 | * To deploy via Infura you'll need a wallet provider (like @truffle/hdwallet-provider) 12 | * to sign your transactions before they're sent to a remote public node. Infura accounts 13 | * are available for free at: infura.io/register. 14 | * 15 | * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate 16 | * public/private key pairs. If you're publishing your code to GitHub make sure you load this 17 | * phrase from a file you've .gitignored so it doesn't accidentally become public. 18 | * 19 | */ 20 | 21 | // const HDWalletProvider = require('@truffle/hdwallet-provider'); 22 | // const infuraKey = "fj4jll3k....."; 23 | // 24 | // const fs = require('fs'); 25 | // const mnemonic = fs.readFileSync(".secret").toString().trim(); 26 | 27 | module.exports = { 28 | /** 29 | * Networks define how you connect to your ethereum client and let you set the 30 | * defaults web3 uses to send transactions. If you don't specify one truffle 31 | * will spin up a development blockchain for you on port 9545 when you 32 | * run `develop` or `test`. You can ask a truffle command to use a specific 33 | * network from the command line, e.g 34 | * 35 | * $ truffle test --network 36 | */ 37 | 38 | networks: { 39 | // Useful for testing. The `development` name is special - truffle uses it by default 40 | // if it's defined here and no other network is specified at the command line. 41 | // You should run a client (like ganache-cli, geth or parity) in a separate terminal 42 | // tab if you use this network and you must also set the `host`, `port` and `network_id` 43 | // options below to some value. 44 | // 45 | // development: { 46 | // host: "127.0.0.1", // Localhost (default: none) 47 | // port: 8545, // Standard Ethereum port (default: none) 48 | // network_id: "*", // Any network (default: none) 49 | // }, 50 | // Another network with more advanced options... 51 | // advanced: { 52 | // port: 8777, // Custom port 53 | // network_id: 1342, // Custom network 54 | // gas: 8500000, // Gas sent with each transaction (default: ~6700000) 55 | // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) 56 | // from:
, // Account to send txs from (default: accounts[0]) 57 | // websockets: true // Enable EventEmitter interface for web3 (default: false) 58 | // }, 59 | // Useful for deploying to a public network. 60 | // NB: It's important to wrap the provider as a function. 61 | // ropsten: { 62 | // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), 63 | // network_id: 3, // Ropsten's id 64 | // gas: 5500000, // Ropsten has a lower block limit than mainnet 65 | // confirmations: 2, // # of confs to wait between deployments. (default: 0) 66 | // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) 67 | // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) 68 | // }, 69 | // Useful for private networks 70 | // private: { 71 | // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), 72 | // network_id: 2111, // This network is yours, in the cloud. 73 | // production: true // Treats this network as if it was a public net. (default: false) 74 | // } 75 | }, 76 | 77 | // Set default mocha options here, use special reporters etc. 78 | mocha: { 79 | // timeout: 100000 80 | }, 81 | 82 | // Configure your compilers 83 | compilers: { 84 | solc: { 85 | // version: "0.5.1", // Fetch exact version from solc-bin (default: truffle's version) 86 | // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) 87 | // settings: { // See the solidity docs for advice about optimization and evmVersion 88 | // optimizer: { 89 | // enabled: false, 90 | // runs: 200 91 | // }, 92 | // evmVersion: "byzantium" 93 | // } 94 | }, 95 | }, 96 | }; 97 | -------------------------------------------------------------------------------- /steps.md: -------------------------------------------------------------------------------- 1 | # Truffle BDD/TDD walkthrough 2 | 3 | - [Project initialization](#project-initialization) 4 | - [SimpleStorage Behavior](#simplestorage-behavior) 5 | * [the contract test](#the-contract-test) 6 | * [the Contract subject](#the-contract-subject) 7 | * [the migration](#the-migration) 8 | * [business logic](#business-logic) 9 | + [define initial deployment value of storedData](#define-initial-deployment-value-of-storeddata) 10 | - [test: watch it fail](#test--watch-it-fail) 11 | + [implement getStoredData](#implement-getstoreddata) 12 | - [test: getStoredData](#test--getstoreddata) 13 | + [define setStoredData behavior](#define-setstoreddata-behavior) 14 | - [implement setStoredData](#implement-setstoreddata) 15 | - [Conclusion](#conclusion) 16 | 17 | Table of contents generated with markdown-toc 18 | 19 | # Project initialization 20 | 21 | - [ ] First [install Truffle. See documentation here](https://www.trufflesuite.com/docs/truffle/getting-started/installation) 22 | - [ ] Create a workplace for this project 23 | 24 | > The following commands initializes a truffle project in the **$HOME/tut** directory. 25 | 26 | ```sh 27 | $ cd # navigate to $HOME folder 28 | $ mkdir tut && cd $_ # make a tut folder and navigate into it. 29 | $ truffle init 30 | ``` 31 | 32 |
See output 33 | 34 | ```sh 35 | $ truffle init 36 | 37 | Starting init... 38 | ================ 39 | 40 | > Copying project files to /home/amal/work/webinars/tut 41 | 42 | Init successful, sweet! 43 | ``` 44 | 45 |
46 | 47 | We now have the basic ingredients for a Truffle project. 48 | 49 | File | description 50 | --------------------------------------------------------------| ---------------------------- 51 | [Migrations.sol](./contracts/Migrations.sol) | a migration contract 52 | [1_initial_migration.js](./migrations/1_initial_migration.js) | a migration script 53 | [truffle-config.js](./truffle-config.js) | Truffle's configuration file 54 | 55 | 56 | Run `truffle test` to check the system. 57 | 58 | > `truffle test` will execute all the test we specified and report the results. 59 | 60 | ```sh 61 | $ truffle test 62 | ``` 63 | 64 |
See output 65 | 66 | ```sh 67 | Compiling your contracts... 68 | =========================== 69 | > Compiling ./contracts/Migrations 70 | ``` 71 | 72 |
73 | 74 | > :information_desk_person: There's not much to report because we have not yet written any tests. 75 | 76 | 77 | # SimpleStorage Behavior 78 | 79 | We want to write a Smart Contract with a simple API: set its value, and read its 80 | value. To do this we need to have the following. 81 | - [ ] a smart contract, `SimpleStorage` 82 | - [ ] some way to define the behavior of `SimpleStorage` 83 | - [ ] the ability to verify `SimpleStorage` behaves the way we defined 84 | 85 | We will use aspects of [Behavior driven development (BDD)](https://en.wikipedia.org/wiki/Behavior-driven_development), and [Test driven 86 | development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development) methodologies to work through this tutorial. We will define 87 | new behavior in truffle test files and implement contract logic to achieve that 88 | behavior. 89 | 90 | This approach lets us to examine the system in small testable pieces while 91 | increasing our knowledge and intuition of Truffle, Solidity and the ecosystem. 92 | 93 | 94 | ## the contract test 95 | 96 | We need to define the behavior of our contract. Let's create our SimpleStorage 97 | test-file, invoke test, and examine its output. We will use the `truffle 98 | create test` command to scaffold a truffle test. 99 | 100 | ``` sh 101 | 102 | $ truffle create test SimpleStorage 103 | $ truffle test 104 | ``` 105 |
106 | See output 107 | 108 | ``` sh 109 | 110 | Compiling your contracts... 111 | =========================== 112 | > Compiling ./contracts/Migrations.sol 113 | > Artifacts written to /tmp/test--15904-aWUmTcuhKTB5 114 | > Compiled successfully using: 115 | - solc: 0.5.16+commit.9c3226ce.Emscripten.clang 116 | 117 | Error: Could not find artifacts for SimpleStorage from any sources 118 | at Resolver.require (/home/amal/.nvm/versions/node/v10.20.1/lib/node_modules/truffle/build/webpack:/packages/resolver/dist/lib/resolver.js:35:1) 119 | at TestResolver.require (/home/amal/.nvm/versions/node/v10.20.1/lib/node_modules/truffle/build/webpack:/packages/core/lib/testing/TestResolver.js:24:1) 120 | at Object.require (/home/amal/.nvm/versions/node/v10.20.1/lib/node_modules/truffle/build/webpack:/packages/core/lib/test.js:278:1) 121 | at Object. (/home/amal/work/webinars/tut/test/simple_storage.js:1:33) 122 | at Module._compile (internal/modules/cjs/loader.js:778:30) 123 | at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10) 124 | at Module.load (internal/modules/cjs/loader.js:653:32) 125 | at tryModuleLoad (internal/modules/cjs/loader.js:593:12) 126 | at Function.Module._load (internal/modules/cjs/loader.js:585:3) 127 | at Module.require (internal/modules/cjs/loader.js:692:17) 128 | at require (internal/modules/cjs/helpers.js:25:18) 129 | at /home/amal/.nvm/versions/node/v10.20.1/lib/node_modules/truffle/node_modules/mocha/lib/mocha.js:390:36 130 | at Array.forEach () 131 | at Mocha.loadFiles (/home/amal/.nvm/versions/node/v10.20.1/lib/node_modules/truffle/node_modules/mocha/lib/mocha.js:387:14) 132 | at Mocha.run (/home/amal/.nvm/versions/node/v10.20.1/lib/node_modules/truffle/node_modules/mocha/lib/mocha.js:961:10) 133 | at resolve (/home/amal/.nvm/versions/node/v10.20.1/lib/node_modules/truffle/build/webpack:/packages/core/lib/test.js:159:1) 134 | at new Promise () 135 | at Object.run (/home/amal/.nvm/versions/node/v10.20.1/lib/node_modules/truffle/build/webpack:/packages/core/lib/test.js:158:1) 136 | at process._tickCallback (internal/process/next_tick.js:68:7) 137 | Truffle v5.1.43 (core: 5.1.43) 138 | Node v10.20.1 139 | ``` 140 |
141 | 142 | 143 | > :warning: :information_desk_person: This error means SimpleStorage is missing in action! 144 | 145 | 146 | 147 | Truffle test mocks! It compiles and deploys the contracts we write to a local 148 | test blockchain every time we run `truffle test`. Our only behavior, the 149 | expectation that `SimpleStorage` is deployed, fails because we have not yet 150 | introduced this contract to our project. 151 | 152 | > :point_right: [Be sure to study the test code before proceeding](./test/simple_storage.js) :point_left: 153 | 154 | 155 | ## the Contract subject 156 | 157 | Now that we've defined the SimpleStorage behavior, let's introduce the contract 158 | to our project and rerun the test. The command `truffle create contract` adds 159 | an empty contract to our project! This is where we will implement our desired 160 | contract behavior. 161 | 162 | ``` sh 163 | $ truffle create contract SimpleStorage 164 | $ truffle test 165 | ``` 166 | 167 |
Examine test output 168 | 169 | ``` sh 170 | Compiling your contracts... 171 | =========================== 172 | > Compiling ./contracts/Migrations.sol 173 | > Compiling ./contracts/SimpleStorage.sol 174 | > Artifacts written to /tmp/test--16564-yHf6bkBqCImA 175 | > Compiled successfully using: 176 | - solc: 0.5.16+commit.9c3226ce.Emscripten.clang 177 | 178 | 179 | Contract: SimpleStorage 180 | Error: SimpleStorage has not been deployed to detected network (network/artifact mismatch) 181 | Error: SimpleStorage has not been deployed to detected network (network/artifact mismatch) 182 | 1) should assert true 183 | > No events were emitted 184 | 185 | 186 | 0 passing (42ms) 187 | 1 failing 188 | 189 | 1) Contract: SimpleStorage 190 | should assert true: 191 | Uncaught Error: the string "abort(Error: SimpleStorage has not been deployed to detected network (network/artifact mismatch)). Build with -s ASSERTIONS=1 for more info." was thrown, throw an Error :) 192 | at process.emit (/home/amal/.nvm/versions/node/v10.20.1/lib/node_modules/truffle/build/webpack:/node_modules/source-map-support/source-map-support.js:495:1) 193 | at process._fatalException (internal/bootstrap/node.js:497:27) 194 | ``` 195 | 196 |
197 | 198 | > :warning: :information_desk_person: This error tells us `SimpleStorage` has 199 | > not been deployed to a network. 200 | 201 | Insights from test errors increases our knowledge and confidence in our Smart 202 | Contract evolution and lets us focus on implementing contract logic without 203 | wrestling with complicated testchain business. Here's what `truffle test` does 204 | at a high level to facilitate that focus: 205 | - starts a local test chain 206 | - compiles all contracts it knows about 207 | - deploys those contracts 208 | - calls all the tests in the system and displays the results 209 | 210 | > Recall that Truffle scaffolded an initial migration script. 211 | > [Review it to see how it deploys a Contract](./migrations/1_initial_migration.js) 212 | 213 | ## the migration 214 | 215 | Truffle test will execute all the deployment scripts in [lexical order](https://en.wikipedia.org/wiki/Lexicographic_order#:~:text=In%20mathematics%2C%20the%20lexicographic%20or,order%20of%20their%20component%20letters.). We will have to write one for our SimpleStorage contract. 216 | 217 | > :information_desk_person: a migration script is the place to define a Smart Contract's deployment 218 | > logic. These scripts are processed in lexical order which explains their odd 219 | > filenames. 220 | 221 | Create the SimpleStorage deployment `migrations/2_deploy_simple_storage.js` 222 | and then execute another test 223 | 224 | ``` Solidity 225 | const SimpleStorage = artifacts.require("SimpleStorage"); 226 | 227 | module.exports = function(deployer) { 228 | deployer.deploy(SimpleStorage); 229 | } 230 | ``` 231 | 232 | Execute Truffle test 233 | 234 | ``` sh 235 | $ truffle test 236 | ``` 237 | 238 |
Examine test output 239 | 240 | ``` sh 241 | Compiling your contracts... 242 | =========================== 243 | > Compiling ./contracts/Migrations.sol 244 | > Compiling ./contracts/SimpleStorage.sol 245 | > Artifacts written to /tmp/test--17726-UpvQCi3AszsJ 246 | > Compiled successfully using: 247 | - solc: 0.5.16+commit.9c3226ce.Emscripten.clang 248 | 249 | 250 | 251 | Contract: SimpleStorage 252 | ✓ should assert true 253 | 254 | 255 | 1 passing (34ms) 256 | 257 | ``` 258 |
259 | 260 | :tada: Congratulations! If you see a similar output then you have: 261 | - [x] a smart contract, `SimpleStorage` 262 | - [x] a way to define the behavior of `SimpleStorage` 263 | - [x] a way to validate its behavior is correct 264 | 265 | > :information_desk_person: In other words, we've built a system to iterate, 266 | > with focus, to the Contract behavior we want. In the test file we describe 267 | > the desired contract behavior, implement said contract and 268 | > test/observe/iterate to the desired outcome. 269 | 270 | ## business logic 271 | 272 | Lets focus on the business logic of our smart contract now that we have the 273 | test, contract and migration pieces in place 274 | 275 | Our SimpleStorage contract should have: 276 | - [ ] a state, `storedData`. This is the location to store an integer value 277 | - [ ] its `storedData` value at deployment be zero 278 | - [ ] a function `getStoredData`, to retrieve the current `storedData` value. 279 | - [ ] a function `setStoredData`, to set the `storedData` value. 280 | 281 | 282 | ### define initial deployment value of storedData 283 | 284 | Add the following code to the test file and execute truffle test. 285 | 286 | ``` JavaScript 287 | contract("SimpleStorage", (/* accounts */) => { 288 | describe("Initial deployment", async () => { 289 | it("should assert true", async function () { 290 | await SimpleStorage.deployed(); 291 | assert.isTrue(true); 292 | }); 293 | 294 | it("was deployed and it's intial value is 0", async () => { 295 | // get subject 296 | const ssInstance = await SimpleStorage.deployed(); 297 | // verify it starts with zero 298 | const storedData = await ssInstance.getStoredData.call(); 299 | assert.equal(storedData, 0, `Initial state should be zero`); 300 | }); 301 | }); 302 | }); 303 | 304 | ``` 305 | 306 | #### test: watch it fail 307 | 308 | Since we defined a new behavior we expect the test to fail. Lets see how the 309 | system reports this expected failure. 310 | 311 | > :information_desk_person: This is a great way to learn about new systems. We 312 | > expect the test to fail and examining the errors will build our intuition 313 | > about the system. 314 | > 315 | 316 | ``` sh 317 | $ truffle test 318 | 319 | ``` 320 | 321 |
See test result 322 | 323 | ``` sh 324 | Compiling your contracts... 325 | =========================== 326 | > Compiling ./contracts/Migrations.sol 327 | > Compiling ./contracts/SimpleStorage.sol 328 | > Artifacts written to /tmp/test--40553-kTukJ514YAII 329 | > Compiled successfully using: 330 | - solc: 0.5.16+commit.9c3226ce.Emscripten.clang 331 | 332 | 333 | 334 | Contract: SimpleStorage 335 | Initial deployment 336 | ✓ should assert true 337 | 1) was deployed and it's intial value is 0 338 | > No events were emitted 339 | 340 | 341 | 1 passing (50ms) 342 | 1 failing 343 | 344 | 1) Contract: SimpleStorage 345 | Initial deployment 346 | was deployed and it's intial value is 0: 347 | TypeError: Cannot read property 'call' of undefined 348 | at Context.it (test/simple_storage.js:14:57) 349 | at process._tickCallback (internal/process/next_tick.js:68:7) 350 | 351 | ``` 352 |
353 | 354 | > :information_desk_person: The error indicates something is undefined, and 355 | > includes a file, line and column location Take a look at the file and try to 356 | > deduce what's happening. 357 | > 358 | > In the test file we ask truffle for SimpleStorage's deployed instance and 359 | > then try to invoke a method on that instance. We have yet to define an API 360 | > for SimpleStorage 361 | 362 | 363 | ### implement getStoredData 364 | 365 | update SimpleStorage.sol to 366 | - declare a public uint256 named storedData 367 | - add a public view named getStoredData that returns the contract's state storedData 368 | 369 | ``` Solidity 370 | contract SimpleStorage { 371 | uint256 public storedData; 372 | 373 | function getStoredData() public view returns (uint256) { 374 | return storedData; 375 | } 376 | 377 | } 378 | ``` 379 | 380 | #### test: getStoredData 381 | 382 | > :question:Try to predict what will happen before running the test. Think 383 | > about what we've observed so far. What will the change to the solidity file 384 | > add to the system? How can it fail? 385 | 386 | 387 | ``` sh 388 | $ truffle test 389 | ``` 390 | 391 |
See output 392 | 393 | ``` sh 394 | Compiling your contracts... 395 | =========================== 396 | > Compiling ./contracts/Migrations.sol 397 | > Compiling ./contracts/SimpleStorage.sol 398 | > Artifacts written to /tmp/test--41672-tvxa6sdLx1V6 399 | > Compiled successfully using: 400 | - solc: 0.5.16+commit.9c3226ce.Emscripten.clang 401 | 402 | 403 | 404 | Contract: SimpleStorage 405 | Initial deployment 406 | ✓ should assert true 407 | ✓ was deployed and it's intial value is 0 408 | 409 | 410 | 2 passing (63ms) 411 | ``` 412 |
413 | 414 | :tada: SimpleStorage has: 415 | 416 | - [x] a state, `storedData`. This is the location to store an integer value 417 | - [x] its `storedData` value at deployment be zero 418 | - [x] a function `getStoredData`, to retrieve the current `storedData` value. 419 | - [ ] a function `setStoredData`, to set the `storedData` value. 420 | 421 | > :question:Something to ponder for later: we didn't initialize the state, yet its initial 422 | > value is zero. Why do you think that is? If you can't figure out yourself, read this [section](https://solidity.readthedocs.io/en/v0.7.0/control-structures.html#scoping-and-declarations) from the Solidity documentation. 423 | 424 | ### define setStoredData behavior 425 | 426 | There's one bit of feature missing. Lets implement! 427 | - [ ] a function `setStoredData`, to set the `storedData` value. 428 | 429 | Add the following `describe` block to our test and run truffle test. 430 | 431 | ``` javascript 432 | describe("Functionality", () => { 433 | it("should store the value 42", async () => { 434 | // get subject 435 | const ssInstance = await SimpleStorage.deployed(); 436 | 437 | // change the subject 438 | await ssInstance.setStoredData(42, { from: accounts[0] }); 439 | 440 | // verify we changed the subject 441 | const storedData = await ssInstance.getStoredData.call(); 442 | assert.equal(storedData, 42, `${storedData} was not stored!`); 443 | }); 444 | }); 445 | 446 | ``` 447 | 448 | > :question: Try to predict the test outcome. 449 | 450 | ``` sh 451 | $ truffle test 452 | ``` 453 | 454 |
see output 455 | 456 | ``` sh 457 | $ truffle test 458 | 459 | Compiling your contracts... 460 | =========================== 461 | > Compiling ./contracts/Migrations.sol 462 | > Compiling ./contracts/SimpleStorage.sol 463 | > Artifacts written to /tmp/test--42579-z0xosuySymKs 464 | > Compiled successfully using: 465 | - solc: 0.5.16+commit.9c3226ce.Emscripten.clang 466 | 467 | 468 | 469 | Contract: SimpleStorage 470 | Initial deployment 471 | ✓ should assert true 472 | ✓ was deployed and it's intial value is 0 473 | Functionality 474 | 1) should store the value 42 475 | > No events were emitted 476 | 477 | 478 | 2 passing (82ms) 479 | 1 failing 480 | 481 | 1) Contract: SimpleStorage 482 | Functionality 483 | should store the value 42: 484 | ReferenceError: accounts is not defined 485 | at Context.it (test/simple_storage.js:25:50) 486 | at process._tickCallback (internal/process/next_tick.js:68:7) 487 | ``` 488 | 489 |
490 | 491 | 492 | > :information_desk_person: `ReferenceError: accounts is not defined` 493 | > This error indicates that we haven't included accounts in our test. 494 | > When truffle runs its tests it creates a local testnet and creates 10 funded 495 | > accounts that use for testing. This is part of the identity concept mentioned 496 | > in the earlier part of the webinar. This is another piece of the eco system 497 | > we don't have to worry about as we build up our smart contract. 498 | 499 | 500 | Let's make sure we have accounts in our tests. Uncomment `accounts` so test 501 | entry point reads as: 502 | 503 | ``` javascript 504 | contract("SimpleStorage", function (accounts) { 505 | ``` 506 | 507 | Run truffle test 508 | 509 | ```sh 510 | $ truffle test 511 | ``` 512 | 513 | > :question: What do you think will happen now? Will the test pass? 514 | 515 |
see output 516 | 517 | ```sh 518 | Compiling your contracts... 519 | =========================== 520 | > Compiling ./contracts/Migrations.sol 521 | > Compiling ./contracts/SimpleStorage.sol 522 | > Artifacts written to /tmp/test--43198-xp1rxfykLBu7 523 | > Compiled successfully using: 524 | - solc: 0.5.16+commit.9c3226ce.Emscripten.clang 525 | 526 | 527 | 528 | Contract: SimpleStorage 529 | Initial deployment 530 | ✓ should assert true 531 | ✓ was deployed and it's intial value is 0 532 | Functionality 533 | 1) should store the value 42 534 | > No events were emitted 535 | 536 | 537 | 2 passing (81ms) 538 | 1 failing 539 | 540 | 1) Contract: SimpleStorage 541 | Functionality 542 | should store the value 42: 543 | TypeError: ssInstance.setStoredData is not a function 544 | at Context.it (test/simple_storage.js:25:24) 545 | at process._tickCallback (internal/process/next_tick.js:68:7) 546 | ``` 547 | 548 |
549 | 550 | > :information_desk_person: Did you predict what would happen? We haven't implemented the contract behavior. 551 | 552 | #### implement setStoredData 553 | 554 | The following line in the output indicates that `setStoredData` is not a 555 | function. We should fix that. 556 | 557 | ``` 558 | TypeError: ssInstance.setStoredData is not a function 559 | ``` 560 | 561 | Add the following function to the SimpleStorage contract 562 | 563 | ``` solidity 564 | function setStoredData(uint256 x) public { 565 | storedData = x; 566 | } 567 | ``` 568 | 569 | Lets run truffle test once again 570 | 571 | ``` sh 572 | $ truffle test 573 | ``` 574 | 575 |
see results 576 | 577 | ``` sh 578 | Compiling your contracts... 579 | =========================== 580 | > Compiling ./contracts/Migrations.sol 581 | > Compiling ./contracts/SimpleStorage.sol 582 | > Artifacts written to /tmp/test--44360-9r4fMMWnmb6y 583 | > Compiled successfully using: 584 | - solc: 0.5.16+commit.9c3226ce.Emscripten.clang 585 | 586 | 587 | 588 | Contract: SimpleStorage 589 | Initial deployment 590 | ✓ should assert true 591 | ✓ was deployed and it's intial value is 0 592 | Functionality 593 | ✓ should store the value 42 (55ms) 594 | 595 | 596 | 3 passing (125ms) 597 | ``` 598 |
599 | 600 | :tada: :sparkles: Congratulations! You did it! I hope this exercise was helpful and recommend you 601 | continue exploring. 602 | 603 | # Conclusion 604 | 605 | Truffle with a BDD and TDD workflow is a powerful way to develop and test Smart contracts. It keeps you focused on the activities of contract development and testing by mocking the blockchain, managing identities and converts your tests and assertsions into transactions for you. 606 | 607 | See you in the next episode where we'll continue our exploration with Ganache and Sandboxes. 608 | --------------------------------------------------------------------------------