Hedera
  • Welcome to Hedera — let’s build the future
  • Getting Started
    • Environment Setup
    • Web2 Developers
      • Transfer HBAR
      • Create a Token
      • Create a Topic
    • EVM Developers
      • Deploy a Contract
  • Tutorials
    • Smart Contracts
      • How to Mint & Burn an ERC-721 Token Using Hardhat and Ethers (Part 1)
      • How to Set Access Control, a Token URI, Pause, and Transfer an ERC-721 Token Using Hardhat (Part 2)
      • How to Upgrade an ERC-721 Token with OpenZeppelin UUPS Proxies and Hardhat (Part 3)
      • How to Verify a Smart Contract on HashScan
      • Deploy a Smart Contract Using Remix
      • Deploy a Smart Contract Using Hardhat and Hiero JSON-RPC Relay
      • Deploy Your First Smart Contract
      • Deploy a Contract Using the Hedera Token Service
      • Send and Receive HBAR Using Solidity Smart Contracts
      • Deploy By Leveraging Ethereum Developer Tools On Hedera
      • Deploy a Subgraph Using The Graph and Hedera JSON-RPC Relay
      • Deploy Smart Contracts on Hedera Using Truffle
      • The Power of Native Hedera Tokens as ERC-20 Tokens: A step-by-step guide
      • HTS x EVM - Part 1: How to Mint NFTs
      • HTS x EVM - Part 2: KYC & Update
      • HTS x EVM - Part 3: How to Pause, Freeze, Wipe, and Delete NFTs
      • Hedera Smart Contracts Workshop
        • Setup
        • Solidity
        • Hedera SDK JS
        • Hardhat and EthersJs
        • Outro
      • Foundry
        • How to Setup Foundry and Write a Basic Unit Test
        • How to Deploy and Verify a Hedera Smart Contract with Foundry
        • How to Test A Solidity Event
        • How to Fork Testnet on Latest Block
    • Consensus
      • Submit Your First Message
      • Submit Message to Private Topic
      • Query Messages with Mirror Node
    • Tokens
      • Create and Transfer Your First NFT
      • Create and Transfer Your First Fungible Token
      • Create and Transfer an NFT using a Solidity Contract
      • Structure Your Token Metadata Using JSON Schema V2
      • Hedera Token Service - Part 1: How to Mint NFTs
      • Hedera Token Service - Part 2: KYC, Update, and Scheduled Transactions
      • Hedera Token Service - Part 3: How to Pause, Freeze, Wipe, and Delete NFTs
      • Create Your First Frictionless Airdrop Campaign
    • Local Node
      • How to Run Hedera Local Node in a Cloud Development Environment (CDE)
        • Run a Local Node in Gitpod
        • Run a Local Node in Codespaces
      • How to Set Up a Hedera Local Node
      • Set Up a Hedera Local Node using the NPM CLI
    • More Tutorials
      • Create and Fund Your Hedera Testnet Account
      • How to Create a Personal Access Token (API Key) on the Hedera Portal
      • How to Auto-Create Hedera Accounts with HBAR and Token Transfers
      • How to Configure a Mirror Node and Query Data
      • How to Generate a Random Number on Hedera
      • Get Started with the Hedera Consensus Service Fabric Plugin
        • Virtual Environment Setup
      • Schedule Your First Transaction
      • How to Connect to Hedera Networks Over RPC
        • Configuring Hashio RPC endpoints
        • Configuring Hiero JSON-RPC Relay endpoints
        • Configuring Validation Cloud RPC endpoints
      • JavaScript Testing
      • Create a Hedera DApp Integrated with WalletConnect
      • How to Connect MetaMask to Hedera
    • Demo Applications
    • Starter Projects
    • Building on Hedera (course)
  • Networks
    • Mainnet
      • Mainnet Accounts
      • Mainnet Consensus Nodes
        • Node Requirements
          • FAQ
      • Fees
        • Transaction Records
    • Testnets
      • Testnet Accounts
      • Testnet Consensus Nodes
    • Localnet
      • Single Node Configuration
      • Multinode Configuration
    • Network Explorers and Tools
    • Release Notes
      • Consensus Node
      • Hedera Mirror Node
  • Core Concepts
    • Accounts
      • Account Creation
      • Auto Account Creation
      • Account Properties
    • Keys and Signatures
    • Schedule Transaction
    • Smart Contracts
      • Understanding Hedera's EVM Differences and Compatibility
        • For EVM Developers Migrating to Hedera
          • Accounts, Signature Verification & Keys (ECDSA vs. ED25519)
          • JSON-RPC Relay and EVM Tooling
          • Token Management with Hedera Token Service
          • Decimal Handling (8 vs. 18 Decimals)
          • Handling HBAR Transfers in Contracts
        • For Hedera-Native Developers Adding Smart Contract Functionality
          • Integrating ED25519 Accounts and Advanced Features Into Smart Contracts
          • JSON-RPC Relay and State Queries
          • Extending Token Management with Smart Contracts
      • Creating Smart Contracts
      • Compiling Smart Contracts
      • System Smart Contracts
        • Hedera Account Service
        • Hedera Schedule Service
      • Gas and Fees
      • JSON-RPC Relay
      • Deploying Smart Contracts
      • Smart Contract Addresses
      • Verifying Smart Contracts
      • Smart Contract Traceability
      • Tokens Managed by Smart Contracts
        • ERC-20 (Fungible Tokens)
        • ERC-721 (Non-Fungible Token)
        • ERC-3643 Real World Assets (RWA)
        • ERC-1363 (Payable Tokens)
        • Hedera Token Service System Contract
      • Wrapped HBAR (WHBAR)
      • Smart Contract Rent
      • Smart Contract Security
      • EVM Archive Node Queries
    • Tokens
      • Tokenization on Hedera
      • Hedera Token Service (HTS) Native Tokenization
        • Token Types and ID Formats
        • Token Properties
        • Token Creation
        • Custom Fee Schedule
        • Token Airdrops
      • ERC/EVM-Compatible Tokenization
      • Hybrid (HTS + EVM ) Tokenization
    • Staking
      • Staking Program
      • Stake HBAR
    • Hashgraph Consensus Algorithm
      • Gossip About Gossip
      • Virtual Voting
    • Transactions and Queries
      • Transaction Properties
    • State and History
    • Mirror Nodes
      • Hedera Mirror Node
      • One Click Mirror Node Deployment
      • Run Your Own Mirror Node
        • Run Your Own Mirror Node with Google Cloud Storage (GCS)
        • Run Your Mirror Node with Amazon Web Services S3 (AWS)
  • Open Source Solutions and Integrations
    • AI Studio on Hedera
      • ElizaOS Plugin for Hedera
      • Hedera AI Agent Kit
      • MCP Server
      • OpenConvAI
    • AI Tools for Developers
      • Hedera Hivemind
      • Kapa AI
    • Asset Tokenization Studio (ATS)
      • Web User Interface (UI)
      • Frequently Asked Questions (FAQs)
    • HashioDAO
      • Governance Token DAO
      • NFT DAO
      • Multisig DAO
      • DAO Proposals
      • Local Environment Setup
    • Hedera CLI
    • Hedera Contract Builder
    • Hedera Custodians Library
      • How to use it
    • Hedera Developers Code Repository
    • Hedera Developer Playground
    • Hedera Wallet Snap By MetaMask
      • Hedera Wallet Snap Documentation
      • Tutorial: MetaMask Snaps – What Are They and How to Use Them
    • Interoperability and Bridging
      • LayerZero
    • NFT Studio
      • Airdrop List Verifier
      • Metadata Validator
      • NFT Rarity Inspector
      • NFT Token Holders List Builder
      • NFT Risk Calculator
      • Token Balance Snapshot
      • Hedera NFT SDK
    • Oracle Networks
      • Chainlink Oracles
      • Pyth Oracles
      • Supra Oracles
    • Stablecoin Studio
      • Core Concepts
      • Web UI Application
      • CLI Management
      • TypeScript SDK
    • Hedera Guardian
    • Hedera WalletConnect
  • SDKs & APIs
    • SDKs
      • Build Your Hedera Client
      • Set Up Your Local Network
      • Network Address Book
      • Keys
        • Generate a new key pair
        • Import an existing key
        • Create a key list
        • Create a threshold key
        • Generate a mnemonic phrase
        • Recover keys from a mnemonic phrase
      • HBAR
      • Specialized Types
      • Pseudorandom Number Generator
      • Transactions
        • Create a Batch Transaction
        • Transaction ID
        • Modify transaction fields
        • Create an unsigned transaction
        • Manually sign a transaction
        • Submit a transaction
        • Sign a multisignature transaction
        • Get a transaction receipt
        • Get a transaction record
      • Schedule Transaction
        • Schedule ID
        • Create a schedule transaction
        • Sign a scheduled transaction
        • Delete a schedule transaction
        • Get schedule info
        • Network Response Messages
      • Queries
      • General Network Response Messages
      • Accounts and HBAR
        • Create an account
        • Update an account
        • Transfer cryptocurrency
        • Approve an allowance
        • Delete an allowance
        • Delete an account
        • Get account balance
        • Get account info
        • Network Response Messages
      • Consensus Service
        • Create a topic
        • Update a topic
        • Submit a message
        • Delete a topic
        • Get topic messages
        • Get topic info
        • Network Response
      • Token Service
        • Token ID
        • NFT ID
        • Token types
        • Create a token
        • Custom token fees
        • Update a token
        • Update token custom fees
        • Update NFT metadata
        • Transfer tokens
        • Airdrop a token
        • Claim a token
        • Cancel a token
        • Reject a token
        • Delete a token
        • Mint a token
        • Burn a token
        • Freeze an account
        • Unfreeze an account
        • Enable KYC account flag
        • Disable KYC account flag
        • Associate tokens to an account
        • Dissociate tokens from an account
        • Pause a token
        • Unpause a token
        • Wipe a token
        • Atomic swaps
        • Get account token balance
        • Get token info
        • Get NFT info
        • Network Response Messages
      • File Service
        • Create a file
        • Append to a file
        • Update a file
        • Delete a file
        • Get file contents
        • Get file info
        • Network Response Messages
      • Smart Contract Service
        • Delegate Contract ID
        • Create a smart contract
        • Update a smart contract
        • Delete a smart contract
        • Call a smart contract function
        • Ethereum transaction
        • Get a smart contract function
        • Get smart contract bytecode
        • Get smart contract info
        • Hedera Service Solidity Libraries
        • Network Response Messages
      • Signature Provider
        • Provider
        • Signer
        • Wallet
        • Local Provider
    • Mirror Node REST API
      • Accounts
      • Balances
      • Blocks
      • Schedule Transactions
      • Smart Contracts
      • Tokens
      • Topics
      • Transactions
      • Network
    • Hedera Consensus Service gRPC API
    • Hedera APIs
      • Basic Types
        • AccountAmount
        • AccountID
        • ContractID
        • CryptoAllowance
        • CurrentAndNextFeeSchedule
        • FeeComponents
        • FeeData
        • FeeSchedule
        • FileID
        • Fraction
        • HederaFunctionality
        • Key
        • KeyList
        • NftAllowance
        • NftTransfer
        • NodeAddress
        • NodeAddressBook
        • RealmID
        • ScheduleID
        • SemanticVersion
        • ServicesConfigurationList
        • ServiceEndpoint
        • Setting
        • ShardID
        • Signature
        • SignatureList
        • SignatureMap
        • SignaturePair
        • SubType
        • TransferList
        • TransactionID
        • ThresholdKey
        • ThresholdSignature
        • TokenAllowance
        • TokenBalance
        • TokenBalances
        • TokenFreezeStatus
        • TokenPauseStatus
        • TokenID
        • TokenKycStatus
        • TokenRelationship
        • TokenTransferList
        • TokenType
        • TokenSupplyType
        • TopicID
        • TransactionFeeSchedule
      • Cryptocurrency Accounts
        • CryptoService
        • CryptApproveAllowance
        • CryptoDeleteAllowance
        • CryptoCreate
        • CryptoTransfer
        • CryptoUpdate
        • CryptoDelete
        • CryptoGetAccountBalance
        • CryptoGetAccountRecords
        • CryptoGetInfo
        • CryptoGetStakers
      • Consensus Service
        • Consensus Service
        • ConsensusCreateTopic
        • ConsensusUpdateTopic
        • ConsensusSubmitMessage
        • ConsensusDeleteTopic
        • ConsensusTopicInfo
        • ConsensusGetTopicInfo
      • Schedule Service
        • ScheduleService
        • SchedulableTransactionBody
        • ScheduleCreate
        • ScheduleDelete
        • ScheduleSign
        • ScheduleGetInfo
      • Token Service
        • TokenService
        • CustomFees
          • AssessedCustomFee
          • CustomFee
          • FractionalFee
          • FixedFee
          • RoyaltyFee
        • TokenCreate
        • TokenUpdate
        • TokenFeeScheduleUpdate
        • TokenDelete
        • TokenMint
        • TokenBurn
        • TokenFreezeAccount
        • TokenUnfreezeAccount
        • TokenGrantKyc
        • TokenRevokeKyc
        • TokenAssociate
        • TokenDissociate
        • TokenWipeAccount
        • TokenPause
        • TokenUnpause
        • TokenGetInfo
        • TokenGetNftInfo
        • TokenGetNftInfos
        • TokenGetAccountNftInfo
      • File Service
        • FileService
        • FileCreate
        • FileAppend
        • FileUpdate
        • FileDelete
        • FileGetContents
        • FileGetInfo
      • Smart Contracts
        • SmartContractService
        • ContractCall
        • ContractCallLocal
        • ContractCreate
        • ContractUpdate
        • ContractDelete
        • ContractGetByteCode
        • ContractGetInfo
        • ContractGetRecords
      • Miscellaneous
        • Duration
        • ExchangeRate
        • Freeze
        • FreezeType
        • GetByKey
        • GetBySolidityID
        • NetworkGetVersionInfo
        • NetworkService
        • Query
        • QueryHeader
        • Response
        • ResponseCode
        • ResponseHeader
        • SystemDelete
        • SystemUndelete
        • TimeStamp
        • Transaction
        • TransactionBody
        • TransactionContents
        • TransactionGetFastRecord
        • TransactionGetReceipt
        • TransactionGetRecord
        • TransactionReceipt
        • TransactionRecord
        • TransactionResponse
        • UncheckedSubmit
    • Hedera Status API
  • Support & Community
    • Glossary
    • Contributing to Hedera documentation
      • Contribution Guidelines
        • Creating Issues
        • Creating Pull Requests
        • Hedera Improvement Proposal (HIP)
        • Submit Demo Applications
      • Style Guide
        • Understanding different types of documentation
        • Use of HBAR and tinybars
        • Use of web2 and web3
        • Language and grammar
        • Formatting
        • Punctuation
        • GitBook Markdown Syntax
    • Discord
    • GitHub
    • Stack Overflow
    • Hedera Blog
    • Bug Bounty
    • Hedera Help
    • Documentation Survey
    • Meetups
    • Brand Guidelines
    • Status Page
Powered by GitBook
LogoLogo

INTRODUCTION

  • Fees
  • Core Concepts
  • Network Information

TOOLS

  • Bridge
  • Oracles
  • Explorers
  • Developer Portal & Faucet

RESOURCES

  • Status
  • Bug Bounty
  • Build on Hedera (course)
  • Documentation Survey
On this page
  • Prerequisites
  • Step 1: Set up the project
  • Step 2: Implement the system under test
  • Step 3: Implement the tests
  • Step 4: Switch to a true negative scenario
  • Step 5: Switch to a false negative scenario
  • Step 6: Switch to a false positive scenario
  • Step 7: Switch back to a true positive scenario
  • Step 8: Bonus - Add generative testing

Was this helpful?

Edit on GitHub
  1. Tutorials
  2. More Tutorials

JavaScript Testing

How to test Javascript, using Javascript

PreviousConfiguring Validation Cloud RPC endpointsNextCreate a Hedera DApp Integrated with WalletConnect

Last updated 12 months ago

Was this helpful?

Developing typically involves using quite a lot of JavaScript. This can happen in several different areas:

  • The DApp itself, when the DApp is browser-based

  • The backend APIs, when the DApp uses some web2 server components

  • Test code for

  • Test code for DApp client

  • Test code for backend APIs

Looking at the latter three, suffice it to say that learning testing with JavaScript is a fundamental skill for DApp developers. Unfortunately, it is one that is often overlooked. This tutorial aims to fill that gap. If you're planning to create a DApp on Hedera, but wish to brush up on the basics of testing first, start here!

What we will cover

A test runner is a developer tool that helps you to:

  • Structure your tests.

  • Execute your tests against a target application (known as the system under test).

  • Report the results of the tests.

One of the most popular JavaScript test runners is ; and this is what you will be using in this tutorial. We picked this because both and , two of the most popular smart contract development frameworks, use Mocha under the hood in their built-in smart contract testing features. This means that the syntax you learn here will be familiar and useful when building smart contracts for the Hedera Smart Contract Service (HSCS).

In this tutorial, let's start with a very simple application and modify its implementation to cover four different scenarios you'll encounter during development.

  • True positive

  • True negative

  • False negative

  • False positive

Definitions of these test outcome scenarios

We'll define these as we go through the tutorial steps, but in case you would like a preview:

  • True positive -> both the implementation and specification are correct.

  • True negative -> the implementation is wrong, but the specification is correct.

  • False negative -> the implementation is correct, but the specification is wrong.

  • False positive -> both the implementation and specification are wrong.

For each scenario, you'll cover what to look out for and how to handle it properly.

Let's begin!


Prerequisites

Prior knowledge

  • JavaScript syntax

System

  • git installed

    • Minimum version: 2.37

  • NodeJS + npm installed

    • Minimum version (NodeJS): 18


Step 1: Set up the project

git clone git@github.com:hedera-dev/js-testing.git
cd js-testing
npm install

If you do not have SSH available on your system, or are unable to configure it for GitHub, you may wish to try this git command instead: git clone https://github.com/hedera-dev/js-testing.git

Open this repository in your code editor, and you'll find the following files:

package.json

  • mocha is installed as a devDependency.

  • The test command is configured to run mocha on all files with a .spec.js file extension.

  • The test:generative command is configured to run mocha on all files with a .genspec.js file extension - note that this is only needed for the bonus step.

my-app.js

  • The system under test, also referred to as the implementation.

  • This will contain a single add function - very simple, but enough to demonstrate a point.

my-app.spec.js

  • The tests, also referred to as the specification.

  • These specify what the behavior of the implementation should be.


Step 2: Implement the system under test

In the add function within my-app.js, you should see a comment marking Step 2. It looks like this:

    // TODO: Implement the system under test
    // Follow step (2) in tutorial to complete the following section.

This is where you'll add the code for this step. The implementation for addition is self-explanatory.

return x + y;

It should now look like this:

    // TODO: Implement the system under test
    // Follow step (2) in tutorial to complete the following section.
    return x + y;

Note: In the subsequent steps of this tutorial, you will follow the same pattern as above. However, you will not copy the comments marking the steps for the remainder of the tutorial and instead only include the new/changed lines of code.

Now you have completed your system under test!


Step 3: Implement the tests

In my-app.spec.js, find the test block with the title works with known values, and add the following implementation where indicated with the comment.

const actual = add(1, 2);
const expected = 3;
assert.equal(actual, expected);
  • The actual value is obtained from invoking the system under test.

  • The expected value is simply written by you as the test writer.

  • Finally, you have an assertion that checks that the actual and expected values match.

Now you have completed the specification!

Since both the system under test and the tests for it are ready, you're ready to invoke the test runner.

npm run test

You should now see some output that looks similar to the following:

> tutorial-js-testing@0.0.0 test
> mocha './**/*.spec.js'

  my-app
    add
      ✔ works with known values
      ✔ is associative with known values
      ✔ is commutative with known values

  3 passing (3ms)

So all the tests have passed: One that you have just added ('known values'), and two more that were included in the initial state of the tutorial repo ('associative', and 'commutative').

You could wrap up here... but you're not quite done yet. This is a true positive scenario, where both the implementation and the specification are correct. But there are three other possible scenarios that you're likely to encounter when writing tests, so let's go through them in the next few steps!


Step 4: Switch to a true negative scenario

In the true negative scenario, the implementation is wrong, and the specification is correct. This is probably the most common test failure scenario you'll encounter during development.

In my-app.js, comment out the existing code from Step 2, and add this new implementation for Step 4.

return x + y + 1;

In my-app.spec.js, comment out the assertion from Step 3 (keep the other 2 lines of code), and add this new line of code for Step 4. (Note that this happens to be the same as before.)

assert.equal(actual, expected);

Now let's re-run the tests.

npm run test

You should now see some output that looks similar to the following:

> tutorial-js-testing@0.0.0 test
> mocha './**/*.spec.js'

  my-app
    add
      1) works with known values
      ✔ is associative with known values
      ✔ is commutative with known values

  2 passing (4ms)
  1 failing

  1) my-app
       add
         works with known values:

      AssertionError [ERR_ASSERTION]: 4 == 3
      + expected - actual

      -4
      +3

This shows that the actual value was 4, while the expected value was 3. While this is an error, it is actually a good thing - the main point of writing tests is to catch errors in your application, and that is precisely what has happened here. Even though this is a contrived example, as the error in the implementation is so obvious, it nevertheless illustrates the developer workflow involved: Finding bugs in the implementation of an application by specifying how it should behave, and using a test runner to detect where there are problems.

Interestingly, the tests for associativity and commutativity do not fail, even with this bug in the implementation. This illustrates that not all tests are able to catch bugs, and why several different tests are necessary. This can be the case even for simple/ "obvious" bugs.

As a developer, at this point, you would typically fix the error in the implementation, and then re-run the tests to ensure that they pass once again. You will do so eventually, towards the end of this tutorial. However, for now, you'll move on to another scenario.


Step 5: Switch to a false negative scenario

In the false negative scenario, the implementation is correct, and the specification is wrong. This is not as common of a scenario that you'll encounter during development as the false positive, but it is nonetheless important to be able to recognize it when it does occur and rectify it accordingly.

In my-app.js, comment out the existing code from Step 4, and add this new implementation for Step 5.

return x + y;

Likewise, in my-app.spec.js, comment out the existing code from Step 4, and add this new implementation for Step 5.

assert.equal(actual, expected + 1);

Now let's re-run the tests.

npm run test

You should now see some output that looks similar to the following:

> tutorial-js-testing@0.0.0 test
> mocha './**/*.spec.js'

  my-app
    add
      1) works with known values
      ✔ is associative with known values
      ✔ is commutative with known values

  2 passing (4ms)
  1 failing

  1) my-app
       add
         works with known values:

      AssertionError [ERR_ASSERTION]: 3 == 4
      + expected - actual

      -3
      +4

This time, the error shows that the actual value was 3, while the expected value was 4. This is the opposite of previous test error - the actual and expected values have swapped positions. The somewhat tricky thing here is to not "default" to assuming that the error must be in the implementation, and therefore only look into finding a bug in the implementation. The correct thing to do, whenever the test runner reports a test failure is to analyse both the implementation and specification, and identify which one of them contains an error.

As a developer, at this point, you would typically fix the error in the specification, and then re-run the tests to see if they pass once again. Instead, let's move on to another scenario.


Step 6: Switch to a false positive scenario

In the false positive scenario, the implementation is wrong, and the specification is also wrong. This is typically the least common scenario that you'll encounter during development. And it can be extremely tricky to even identify, as you'll see shortly.

In my-app.js, comment out the existing code from Step 5, and add this new implementation for Step 6.

return x + y + 1;

Likewise, in my-app.spec.js, comment out the existing code from Step 5, and add this new implementation for Step 6.

assert.equal(actual, expected + 1);

Now let's re-run the tests.

npm run test

You should now see some output that looks similar to the following:

> tutorial-js-testing@0.0.0 test
> mocha './**/*.spec.js'

  my-app
    add
      ✔ works with known values
      ✔ is associative with known values
      ✔ is commutative with known values

  3 passing (2ms)

Somewhat surprising is it not? Even though both the implementation and specification were wrong, all the tests pass. In fact, the output from the test runner looks identical to when both the implementation and specification were correct. This occurs because the implementation code and the specification code coincidentally happen to make the same error, and they "cancel" each other out.

While in this case it may be obvious, that is only because of the simplicity of this tutorial. Spotting false positive scenarios in a complex application is much harder. More than just tricky, this scenario is insidious due to its ability to have bugs and yet pass the tests meant to catch them. In fact, there is no way for a developer to identify a false positive scenario from the test results alone.

False positive scenarios are typically spotted through manual reviews of the both the implementation and specification code, or other code quality processes such as static/ dynamic analyses and code code coverage. These are beyond the scope of this tutorial.

Now let's finally fix the code, and go back to the true positive scenario, where you started off.


Step 7: Switch back to a true positive scenario

In the true positive scenario, the implementation is correct, and the specification is correct as well. This is the ideal scenario among the 4 possible ones that you've covered thus far. Whenever there are errors identified in either the implementation or specification, the goal is to fix them such that you return to this true positive scenario.

In my-app.js, comment out the existing code from Step 6, and add this new implementation for Step 7.

return x + y;

Likewise, in my-app.spec.js, comment out the existing code from Step 6, and add this new implementation for Step 7.

assert.equal(actual, expected);

Now let's re-run the tests.

npm run test

You should now see some output that looks similar to the following:

> tutorial-js-testing@0.0.0 test
> mocha './**/*.spec.js'

  my-app
    add
      ✔ works with known values
      ✔ is associative with known values
      ✔ is commutative with known values

  3 passing (2ms)

Back to all tests passing (for the right reasons)!


Step 8: Bonus - Add generative testing

Thus far, all the tests that you have written (in my-app.spec.js) are example based tests. Essentially your tests consist of one or more interactions with the system under test actual value, an expected value, and an assertion that the actual value matches the expected value. In this step, we'll add a different type of tests to this project: Generative testing.

Generative testing may be thought of as an abstract form of example based testing, where you write the tests in such a way that they do not require you to specify any input values for the examples that you write tests for. Instead you let the test runner generate value at random as the input value used in your tests. In other words, the tested examples are generated by the test runner, instead of being manually specified.

Checkout the my-app.genspec.js file, where this has already been set up for you. The 'commutative' and 'associative' tests have been copied over from my-app.spec.js, and modified to use check.it() plus gen.int. Compare the method signatures of the callback functions, and the difference between example based testing and generative testing should be clear.

const lhsOfEquation = add(a, add(b, c));
const rhsOfEquation = add(add(a, b), c);

Also, in the 'is commutative' test, replace these two lines of code.

const lhsOfEquation = add(a, b);
const rhsOfEquation = add(b, a);

Note that in both tests, the assertions do not need to be modified, you can keep them as is.

Compare this to the similar example based tests for associativity and commutativity within my-app.spec.js. These will use the same literal/ hard-coded values every time you run the tests, unlike the generative tests within my-app.genspec.js.

Note that the values of a, b, and c above are the randomly generated integers. This means that every time you run the generative tests, you will be testing the add() function using a new set of input values.

Now let's run the new generative tests.

npm run test:generative

You should now see some output that looks similar to the following:

> tutorial-js-testing@0.0.0 test:generative
> mocha './**/*.genspec.js'

  my-app
    add
      ✔ is associative
      ✔ is commutative
bas
  2 passing (17ms)

These generative tests are passing as well and form an additional layer of verification of the system under test. You can now be extra sure that the implementation is indeed correct.

Congrats!

Now that you have covered the basics of testing, you're ready to test more complex applications. If you have smart contracts that you intend to deploy to Hedera Smart Contract Service (HSCS), it is best practice to test them before deployment. If you use Hardhat or Truffle, you will already be familiar with much of the syntax for the specification code.

Recommended setup method:

SSH keys configured for Github:

Recommended setup method for Linux & Mac:

Recommended setup method for Windows:

This has already been (mostly) done. All that's left for you to do is clone the and install the dependencies:

The demo repo for this tutorial has already been wired up with a dependency named . This provides a plugin that augments Mocha with a new check.it() function that behaves in a very similar manner to it() in Mocha. The key difference being that it contains some generator functions that produce random values of a specified type. You will be using gen.int to produce random Integer values to use as inputs for the parameters of the add() function the application.

In my-app.genspec.js, replace these two lines of code for , in the 'is associative' test.

You've completed this tutorial!

🎉
🎉
🎉
Install Git (Github docs)
Add SSH key (Github docs)
nvm
nvm-windows
accompanying tutorial GitHub repository
testcheck.js
Step 8

Writer: Brendan, DevRel Engineer

Editor: Abi Castro, DevRel Engineer

Mocha
Hardhat
Truffle

|

|

GitHub
Blog
GitHub
Twitter
DApps
client
smart contracts