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#
| Library | Language | What it’s for | Link |
|---|---|---|---|
near-sdk `test_utils` (VMContextBuilder, testing_env!, accounts(), get_logs()) | Rust | Unit 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-rs | Rust | Integration 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-js | TypeScript / JS | Same 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-sandbox | Rust / JS wrappers around a neard build | A 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 |
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)));
}
}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