Building a Web3 Discord Clone DApp: Testing and Development Environment (Part 2) | by Manas Hatwar | The Capital | Mar, 2025

Building a Web3 Discord Clone DApp: Testing and Development Environment (Part 2) | by Manas Hatwar | The Capital | Mar, 2025


The Capital
Source : Web3 & AI Insights

In Part 1, we designed and implemented the smart contract for Dappcord, our Web3-powered Discord alternative.

Now, before deploying it to the blockchain, we need to test it rigorously in a development environment to ensure it functions correctly.

Setting up development environment
Writing and executing automated tests for our contract
Using Ethers.js to interact with smart contracts
Testing different scenarios, including contract deployment, channel creation, user interactions, and fund withdrawals

In this guide, we’ll walk through setting up your local development environment to work with Dappcord, a Web3-based decentralized application. We’ll cover cloning the repository, installing dependencies, and running basic tests to ensure everything is working properly.

Before we get started, make sure you have the following installed on your system:

  • Node.js (v16 or later) — Download from nodejs.org
  • npm or yarn — Comes with Node.js
  • Git — Install from git-scm.com
  • Hardhat — A development framework for Ethereum smart contracts
  • Metamask — A browser extension wallet

First, navigate to the directory where you want to store the project and clone the starter_code branch:

cd ~/your-workspace
rm -rf dappcord # Remove any existing directory to avoid conflicts
git clone https://github.com/dappuniversity/dappcord --branch starter_code

Navigate into the project directory and install all required dependencies:

cd dappcord
npm install

This will install Hardhat and other necessary packages.

Ensure Hardhat is installed and working properly:

npx hardhat --version

If you see a version number, Hardhat is successfully installed. If not, try installing it manually:

npm install --save-dev hardhat

Before deploying or testing, compile your smart contracts to check for errors:

npx hardhat compile

You should see a success message like:

Compiled 1 Solidity file successfully

To ensure everything is set up correctly, run the test suite:

npx hardhat test

If everything is working, you should see test results printed on your terminal.

You’re now ready to start building on Dappcord! This setup ensures that your environment is properly configured for smart contract development. Stay tuned for more tutorials on deploying and interacting with smart contracts.

First, create a new project directory and initialize it:

mkdir dappcord
cd dappcord
npm init -y

Now, install the required dependencies:

npm install --save-dev hardhat @nomiclabs/hardhat-waffle ethereum-waffle chai @nomiclabs/hardhat-ethers ethers

Next, initialize Hardhat in your project:

npx hardhat

Many of the warnings are about deprecated packages. While they’re not breaking issues, it’s good practice to replace them with newer alternatives. Some of these warnings come from dependencies that Hardhat and other libraries use internally, so you might not be able to remove them all.

To ensure you’re using the latest compatible versions of key packages, update Hardhat and its dependencies:

npm install --save-dev hardhat @nomicfoundation/hardhat-toolbox @nomicfoundation/hardhat-verify ethers

After everything is installed, verify Hardhat is working:

npx hardhat --version

If it works, you’re good to go! 🎯

To ensure everything runs smoothly, restart your terminal and try running Hardhat commands again, like:

npx hardhat compile

Select “Create a basic sample project” when prompted. This will generate a basic Hardhat setup with essential configurations.

We need to automate contract testing to ensure everything works before deploying.

Let’s create a comprehensive test suite in the test directory. Create a file named Dappcord.test.js:

// Import testing libraries
const { expect } = require("chai")
const { ethers } = require("hardhat")

// Helper function to convert to ether units
const tokens = (n) => {
return ethers.utils.parseUnits(n.toString(), 'ether')
}

We’re using:

  • chai for assertions – a popular assertion library that makes it easy to write expressive tests
  • ethers.js for interacting with the Ethereum blockchain
  • A helper function tokens() to convert regular numbers to wei (the smallest unit of Ether)
// Main test suite for Dappcord contract
describe("Dappcord", function () {
// Declare variables used across test cases
let dappcord, deployer, user
const NAME = "Dappcord"
const SYMBOL = "DC"

// Set up fresh contract instance before each test
beforeEach(async () => {
// Setup accounts - deployer is admin, user is regular member
[deployer, user] = await ethers.getSigners()

// Deploy contract with constructor parameters
const Dappcord = await ethers.getContractFactory("Dappcord")
dappcord = await Dappcord.deploy(NAME, SYMBOL)

// Create initial "general" channel for testing
const transaction = await dappcord.connect(deployer).createChannel("general", tokens(1))
await transaction.wait()
})

In the beforeEach block, which runs before each test:

  1. We set up two accounts: deployer (the contract owner) and user (a regular user)
  2. We deploy a fresh instance of our Dappcord contract
  3. We create a “general” channel costing 1 ETH

This ensures each test starts with the same clean state, making our tests isolated and predictable.

 describe("Deployment", function () {
it("Sets the name correctly", async () => {
// Fetch name from contract
let result = await dappcord.name()
// Verify name matches expected value
expect(result).to.equal(NAME)
})
it("Sets the symbol correctly", async () => {
// Fetch symbol from contract
let result = await dappcord.symbol()
// Verify symbol matches expected value
expect(result).to.equal(SYMBOL)
})

it("Sets the owner to deployer address", async () => {
// Fetch owner from contract
const result = await dappcord.owner()
// Verify owner is the deployer account
expect(result).to.equal(deployer.address)
})
})

  • Ensures the contract name and symbol are correctly set
  • Confirms the deployer is set as the owner

We now test whether an admin can successfully create chat channels.

describe("Creating Channels", () => {
it('Increments total channels counter', async () => {
// Check if totalChannels is updated after channel creation
const result = await dappcord.totalChannels()
expect(result).to.be.equal(1)
})

it('Stores channel attributes correctly', async () => {
// Retrieve channel data using getChannel function
const channel = await dappcord.getChannel(1)
// Verify all channel properties match expected values
expect(channel.id).to.be.equal(1)
expect(channel.name).to.be.equal("general")
expect(channel.cost).to.be.equal(tokens(1))
})
})

  • Confirms totalChannels increases after creating a channel
  • Ensures the channel’s name, cost, and ID are stored correctly

Now, let’s test if users can join a channel by paying the required ETH.

 describe("Joining Channels", () => {
const ID = 1
const AMOUNT = ethers.utils.parseUnits("1", "ether")

// Set up channel join operation before each test in this group
beforeEach(async () => {
// User joins channel by minting NFT and paying fee
const transaction = await dappcord.connect(user).mint(ID, { value: AMOUNT })
await transaction.wait()
})

it('Marks user as joined in mapping', async () => {
// Check if hasJoined mapping is updated for user
const result = await dappcord.hasJoined(ID, user.address)
expect(result).to.be.equal(true)
})

it('Increases NFT total supply', async () => {
// Verify totalSupply counter increments after mint
const result = await dappcord.totalSupply()
expect(result).to.be.equal(ID)
})

it('Updates contract balance with payment', async () => {
// Confirm contract balance increases by payment amount
const result = await ethers.provider.getBalance(dappcord.address)
expect(result).to.be.equal(AMOUNT)
})
})

  • Confirms that users can join channels
  • Ensures NFT supply increases when a user mints
  • Checks ETH payment updates contract balance

Finally, let’s test if the admin can withdraw funds collected from users.

describe("Withdrawing Funds", () => {
const ID = 1
const AMOUNT = ethers.utils.parseUnits("10", 'ether')
let balanceBefore

// Set up withdrawal scenario before each test
beforeEach(async () => {
// Record owner balance before transaction
balanceBefore = await ethers.provider.getBalance(deployer.address)

// User joins channel by paying fee
let transaction = await dappcord.connect(user).mint(ID, { value: AMOUNT })
await transaction.wait()

// Owner withdraws collected fees
transaction = await dappcord.connect(deployer).withdraw()
await transaction.wait()
})

it('Increases owner balance after withdrawal', async () => {
// Compare owner balance after withdrawal
const balanceAfter = await ethers.provider.getBalance(deployer.address)
// Balance should increase (exact amount will be less due to gas fees)
expect(balanceAfter).to.be.greaterThan(balanceBefore)
})

it('Resets contract balance to zero', async () => {
// Verify contract balance is emptied after withdrawal
const result = await ethers.provider.getBalance(dappcord.address)
expect(result).to.equal(0)
})
})
})

  • Confirms the contract balance resets after withdrawal
  • Ensures the owner receives the withdrawn funds

Now, let’s execute our test suite:

npx hardhat test

If all tests pass, you should see:

Dappcord
Deployment
✓ Sets the name correctly
✓ Sets the symbol correctly
✓ Sets the owner to deployer
Creating Channels
✓ Increments total channels
✓ Stores channel attributes correctly
Joining Channels
✓ Marks user as joined
✓ Increases total NFT supply
✓ Updates contract balance
Withdrawing Funds
✓ Increases owner balance
✓ Resets contract balance
10 passing (1s)

🎉 Congratulations! Your smart contract is now fully tested and ready for deployment.

Now that our smart contract is built and tested, the next steps are:

1.)Deploy the smart contract on a local Hardhat network
2.)Develop the frontend using React & Ethers.js
3.)Connect users’ Web3 wallets (MetaMask integration)
4.)Create a UI for joining and browsing channels
5.)Integrate real-time chat using Socket.io

In Part 3, we’ll first deploy our smart contract locally on Hardhat, then move on to building the React frontend with Web3 wallet support!

🔗 GitHub Repository



Source link

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert