Near Learn

Testing tools

How to test NEAR contracts: near-sdk unit tests with VMContextBuilder and testing_env!, near-workspaces (Rust and JS) and the near-sandbox local node.

Intermediate6 min read3-question check

NEAR testing has two layers. Unit tests run your contract as a plain Rust struct inside cargo test, with a mocked blockchain context. Integration tests deploy the compiled Wasm to a real local node (the sandbox) and drive it with transactions, so receipts, promises, callbacks, gas and storage staking all behave as they do on chain.

A project created with cargo near new already contains both: unit tests in src/lib.rs and a near-workspaces test in tests/test_basics.rs.

The tools#

LibraryLanguageWhat it’s forLink
near-sdk `test_utils` (VMContextBuilder, testing_env!, accounts(), get_logs())RustUnit tests. Set the caller, attached deposit, block timestamp etc. before calling a method. Needs the unit-testing feature on near-sdk in [dev-dependencies].docs.rs: test_utils
near-workspaces-rsRustIntegration tests: spins up a sandbox (or targets testnet), deploys Wasm, creates accounts, sends calls and views. Also fast_forward (advance blocks) and spooning via import_contract (copy a mainnet/testnet contract into the sandbox).near/near-workspaces-rs
near-workspaces-jsTypeScript / JSSame model for JS test runners (AVA, Jest, Mocha): Worker.init(), devDeploy, createSubAccount, patchState, fastForward, importContract. Releases are slower than the Rust crate (last major 5.0.0, March 2025).near/near-workspaces-js
near-sandboxRust / JS wrappers around a neard buildA local single-node NEAR chain, downloaded and managed for you. Workspaces uses it under the hood; use it directly when you want a local RPC endpoint for app or CI tests.near/near-sandbox · near-sandbox-rs · near-sandbox-js
Unit test: fake the caller and deposit with VMContextBuilder
Rust
#[cfg(test)]
mod tests {
    use super::*;
    use near_sdk::test_utils::{accounts, VMContextBuilder};
    use near_sdk::{testing_env, NearToken};

    #[test]
    fn records_the_donor() {
        let mut ctx = VMContextBuilder::new();
        ctx.predecessor_account_id(accounts(1))
            .attached_deposit(NearToken::from_near(1));
        testing_env!(ctx.build());

        let mut contract = Contract::default();
        contract.donate(); // a #[payable] method
        assert_eq!(contract.last_donor(), Some(accounts(1)));
    }
}
Integration test with near-workspaces-rs (tests/test_basics.rs, as generated by cargo near new)
Rust
use serde_json::json;

#[tokio::test]
async fn test_contract_is_operational() -> Result<(), Box<dyn std::error::Error>> {
    let sandbox = near_workspaces::sandbox().await?;
    // compile_project needs the "unstable" feature of near-workspaces
    let wasm = near_workspaces::compile_project("./").await?;
    let contract = sandbox.dev_deploy(&wasm).await?;

    let alice = sandbox.dev_create_account().await?;
    let outcome = alice
        .call(contract.id(), "set_status")
        .args_json(json!({ "message": "hello" }))
        .transact()
        .await?;
    assert!(outcome.is_success());

    let status = contract
        .view("get_status")
        .args_json(json!({ "account_id": alice.id() }))
        .await?;
    assert_eq!(status.json::<String>()?, "hello");
    Ok(())
}

Check yourself

3 questions · progress saved in this browser

  1. 1.You need to test a cross-contract call and its callback against a real local node. Which tool?
  2. 2.In a unit test, how do you make env::predecessor_account_id() return bob and attach 1 NEAR?
  3. 3.Your test needs a copy of a live testnet DEX contract and its state in the local sandbox. What feature do you use?