Skip to main content

Command Palette

Search for a command to run...

Builder Rootcamp – Week 3: Testing Smart Contracts on Rootstock

Updated
6 min read
Builder Rootcamp – Week 3: Testing Smart Contracts on Rootstock

Week 3 introduced a shift that felt inevitable after Week 2.

If Week 2 was about understanding the machinery that runs your contracts, Week 3 was about ensuring the logic running on that machinery behaves correctly.

Writing Solidity is productive.
Testing Solidity is disciplined.

On Rootstock, where contracts execute in a Bitcoin-merged-mined environment, correctness isn’t aspirational it’s structural.


Why Testing Suddenly Matters

One thing became clear early in the session:

Passing tests is not the goal.
Defining guarantees is.

The distinction between implementation and testing framed the week:

  • Implementation → The Solidity code

  • Specification → The expected behavior

  • Test Runner → The tool executing and reporting results

The loop is simple:

Run tests → Analyze failures → Fix implementation or refine specification → Repeat

What stood out to me here was realizing that tests aren’t verifying logic after the fact they are formalizing what the system promises. That changes how you write both functions and assertions.

Testing isn’t about passing — it’s about breaking your assumptions before mainnet does.

Smart Contracts as State Machines

Concept

What It Means in a Smart Contract

Why It Matters for Testing

State

Current contract data (balances, roles, mappings, flags)

Defines the system’s current truth

Transition

A function call that changes state

Must behave deterministically

Valid Transition

Expected state change

Should succeed

Invalid Transition

Unauthorized or incorrect state change

Must revert

Invariant

Rule that must always hold (e.g., total supply constant)

Must never break

This was one of the moments that genuinely clicked for me.
Instead of thinking “Does this function work?”, I started thinking, “What transitions should never be allowed?”

That question alone improves how you design contracts.

Smart contracts aren’t just functions they are state machines with defined transitions.

Types of Testing

The module organized testing into layers:

Testing Type

What It Tests

Example

Why It Matters

Unit Testing

Individual functions in isolation

Does transfer() update balances? Does require() revert properly?

Ensures basic logic correctness

Integration Testing

Interaction between contracts or components

ERC20 approvals + transfers, cross-contract calls

Reveals edge cases when systems connect

Acceptance Testing

End-to-end user-level behavior

Full user flow: deploy → approve → transfer

Validates real-world usage

Most real-world bugs don’t appear in isolation they appear when systems connect.

Code Coverage: Useful but Not Absolute

We explored coverage metrics:

  • % statements executed

  • % branches tested

  • % functions covered

Coverage tools help identify blind spots.

However, something that stood out is that high coverage does not automatically mean correctness. You can execute every line of code and still misunderstand its economic or logical implications.

Coverage tells you where you’ve looked.
It does not tell you whether you understood what you saw.

100% coverage does not mean 100% correctness.

Black Box vs White Box Testing

Another distinction was:

  • Black Box Testing → Testing external behavior

  • White Box Testing → Testing internal logic paths

🤔
Black box ensures observable behavior aligns with expectations. White box ensures internal assumptions are validated.

What I found particularly interesting here is how often we default to black box testing in dApp development. White box testing forces you to confront your internal logic more rigorously.


Hardhat vs Foundry

Tooling also shapes thinking.

Dimension

Hardhat

Foundry

Testing Language

JavaScript / TypeScript

Pure Solidity

Testing Style

Application-level

Protocol-level

Assertion System

Mocha + Chai

Solidity-based assertions

Fuzz Testing

Plugin-based

Built-in native fuzzing

Gas Insights

External plugins

Built-in gas reporting

Abstraction Level

Higher abstraction

Closer to EVM execution


Fuzz Testing & Invariants

Fuzz testing introduced property-based testing.

Instead of writing:
“Test transfer of 100 tokens”

You define invariants:

  • Total supply remains constant

  • Balances never become negative

  • Invalid operations revert

Then randomized inputs are generated automatically.

This was one of the most impactful parts of the week for me. Fuzzing challenges assumptions you didn’t even realize you were making. It introduces a layer of humility into development.


The Correctness Quadrants

A key framework discussed was the test correctness matrix:

  • Correct implementation + correct tests → Ideal

  • Incorrect implementation + correct tests → Caught early

  • Correct implementation + incorrect tests → False failures

  • Incorrect implementation + incorrect tests → False confidence

The last case is dangerous. Because passing tests can create a false sense of security.

That idea reinforced something important:
Testing is only as strong as the reasoning behind it.

Passing tests don’t guarantee correctness. They guarantee alignment between code and tests which may still be wrong.

Why This Matters on Rootstock

Rootstock combines:

  • Bitcoin’s merged mining security

  • Deterministic EVM execution

  • Irreversible state transitions

Once deployed, contract behavior is not easily changed. Testing becomes part of respecting that permanence.

From a learner’s perspective, Week 3 elevated testing from quality control to architectural responsibility.


What I’m Taking Forward

Three shifts stood out this week:

  1. Tests define guarantees, not just outcomes.

  2. Integration assumptions must be explicit.

  3. Invariants should be designed before complexity is introduced.

Testing on Rootstock is not about paranoia. It is about discipline.


What’s Next

After understanding infrastructure (Week 2) and testing discipline (Week 3), the focus now moves deeper into smart contract patterns and security considerations.

The progression is becoming clear:

  • Understand the system.

  • Write the logic.

  • Prove the guarantees.

Connect with me on this journey:


Question for Fellow Rootcampers

Did writing tests expose any assumption in your contract logic that you hadn’t noticed before?


Ready to start building on Rootstock? Here’s how you can get involved today:

📖 Explore the Docs – Get started with Rootstock development.

🏆 Check out the Hacker Hub – Learn about past hackathons and upcoming opportunities.

💰 Contribute and Earn with Hacktivator – Build or create content and earn up to $1,000 per contribution.

💬 Join the Community – Connect with other builders on Discord.