What Will I Learn?
- Setup Jest workflow with TypeScript
- Basic testing with Jest
Requirements
- Understanding of Typescript
- Understanding basic of Object Oriented Programming and Data Structure
- Understand basic of Test Driven Development (Describe, It)
Difficulty
Advanced
Description
In this video, we will add basic testing to our existing blockchain app.
Why testing?
To make our app more scalable and source code easier to be understand by others, we will add in testing to our project. This will ensure that when the blockchain being update, it does not break the existing features.
In this video tutorial, I will be using Jest, since it is the easiest to be setup and used for JavaScript project.
Before starting the project
The project started with the branch blockchain-3
(Github link) and ended with the branch blockchain-4
(Github link)
I added some adjustment in the source code. First, I added prettier in the setting. This is my prettier setup: (.vscode/settings.json
)
{
"tslint.enable": true,
"typescript.tsdk": "./node_modules/typescript/lib",
"files.exclude": {
"**/.git": true,
"**/.svn": true,
"**/.hg": true,
"**/.DS_Store": true,
"**/node_modules": false,
"**/*.map": {
"when": "$(basename)"
},
"**/*.js": {
"when": "$(basename).ts"
},
"**/*?.js": {
"when": "$(basename).tsx"
},
"**/*.sqlite": true
},
"search.exclude": {
"**/node_modules": true,
"**/bower_components": true,
"**/*.map": true,
"**/dist": true,
"**/dist-test": true
},
"editor.tabSize": 2,
"editor.insertSpaces": true,
"files.eol": "\n",
"files.trimTrailingWhitespace": true,
"prettier.printWidth": 100,
"prettier.semi": false,
"prettier.tabWidth": 2,
"prettier.useTabs": false,
"prettier.singleQuote": true,
"prettier.trailingComma": "none",
"prettier.disableLanguages": ["markdown", "json"],
"editor.formatOnSave": true
}
In addition to that, you need to initialize the project with npm init -y
. (I didn't do that in the previous series)
Install dependencies
There are 3 dependencies we need to install in this tutorial
- jest - a testing framework created by Facebook team
- @types/jest - types file for Jest (for TypeScript)
- concurrently - this library allows you to execute 2 codes concurrently.
yarn add --dev jest @types/jest concurrently
Then, setup the command in package.json
in the script
key.
"scripts": {
"test": "concurrently \"tsc -w\" \"jest --watchAll\""
},
When you run yarn test
, it will execute typescript watch (tsc -w
) alongside with jest watch (jest --watchAll
)
Code the testing file
After all the setup, we carry on to the testing file. The way jest finds test file is with test.js
at the end. Therefore, we are going to name our test file as block.test.ts
and blockchain.test.ts
which will then being compiled into .test.js
First, setup a blockchain.test.ts
file, and import necessary files to test.
import Blockchain from './blockchain'
import Block from './block'
import Transaction from './transaction'
import { BlockData, BlockChainData, TransactionData } from './types/class'
The test going to start with 'describe', where you describe your scope of testing. In this case, we are testing BlockChain Class, so we called it as 'Blockchain'.
describe('Blockchain', () => {
// insert code here
})
Then, inside the call back of describe, it where our test cases located. A simple sample test is as follow:
it('adds one to one correctly', () => {
expect(1+1).toBe(2)
})
This 'it' will check for '1+1' to be 2, if it is, the test pass; if it isn't, the test fail.
In our blockchain class, we are testing the following 2:
- checks previous block's hash to be Equal to current block previousHash
- checks addBlock function
Since both of them are using same classes, and we need not to redeclare the same thing (apply DRY concept), I used beforeEach()
function to run the repeated function before the test.
let gb: BlockData, bc: BlockChainData, t: TransactionData
let newB: BlockData
beforeEach(() => {
// create a genesis block
gb = new Block()
// initialize blockchain with genesis block
bc = new Blockchain(gb)
// create a transaction
t = new Transaction('me', 'you', 7)
})
The first test: "checks previous block's hash to be Equal to current block previousHash"
it("checks previous block's hash to be Equal to current block previousHash", () => {
newB = bc.getNextBlock([t])
expect(newB.previousHash).toEqual(gb.hash)
})
The second test: "checks addBlock function"
it('checks addBlock function', () => {
let beforeBC = bc.blocks.length // 1
bc.addBlock(newB)
let afterBC = bc.blocks.length // 2
expect(beforeBC).toBe(afterBC - 1)
})
Then, create another test file call block.test.ts
to test Block class. The file starts with 'describe' it as 'Block', and create a block and transactions before the test.
import Block from './block'
import Transaction from './transaction'
import { TransactionData, BlockData } from './types/class'
describe('Block', () => {
let t: TransactionData, b: BlockData
beforeAll(() => {
t = new Transaction('you', 'me', 4)
b = new Block(1, '777', '666', 0, [t])
})
// Insert code here
})
There is 2 cases we are testing on Block class:
- gets the key correctly
- adds transactions successfully
it('get the key correctly', () => {
// [{"from":"you","to":"me","amount":4}]
// JSON.stringify(this.transactions) + this.index + this.previousHash + this.nonce
expect(b.key).toBe('[{"from":"you","to":"me","amount":4}]16660')
})
it('adds transactions successfully', () => {
let pT = b.transactions.length
b.addTransaction(t)
let aT = b.transactions.length
expect(aT).toBe(pT + 1)
})
You can find both files at blockchain.test.ts and block.test.ts
Next video
In the next video, I will be setting up web socket to allow the blockchain to "talk" to each other (also known as peer to peer network).
Video Tutorial
Curriculum
- Introduction to Block, Transaction and Blockchain
- Setup Mining
- Resolve Type error and Setup Express API Server
Posted on Utopian.io - Rewarding Open Source Contributors