@inspatial/test@0.1.1
A universal testing framework that works seamlessly across Deno, Node.js, and Bun runtimes. Write tests once, run them anywhere - from mobile to desktop, and 3D/spatial environments!
Reality is your canvas
InSpatial is a universal development environment (UDE)
for building cross-platform and spatial (AR/MR/VR) applications
| InSpatial | Description | Link |
|---|---|---|
| Universal Libraries & Frameworks | inspatial.dev | |
| Backend APIs and SDKs | inspatial.cloud | |
| Build and manage your InSpatial apps | inspatial.app | |
| Deploy and discover InSpatial apps | inspatial.store |
๐งช InSpatial Test (๐ก Preview)
A universal testing module that works seamlessly across Deno, Node.js, and Bun runtimes. Write tests once, run them anywhere - from mobile to desktop, and 3D/spatial environments!
๐ Features
- ๐ Cross-platform support (Deno, Node.js, Bun)
- ๐ Multiple test syntax styles (Function and Object)
- ๐ฏ Support for both
assertandexpectstyle assertions - ๐งฉ Behavior Driven Development Support with (descibe & it)
- โก Async/await support out of the box
- ๐จ Beautiful test output with syntax highlighting
- ๐ Runtime auto-detection
- ๐ซ Test Modifiers: Skip and Todo test support
- ๐งน Automatic resource cleanup
- ๐ Type-safe with full TypeScript support
- ๐งช Test Doubles (Mocks, Stubs, Spies, etc.)
- ๐ธ Snapshot Testing for detecting unintended changes
๐ฎ Coming Soon
- ๐ฎ XR (AR/VR/MR) Testing Support
- ๐ 3D Environment Testing
- ๐จ Visual Regression Testing for 3D
- ๐ Spatial Computing Metrics
- ๐ค AI-Powered CI/CD Test Agent
- ๐ Logging and Reporting
- ๐ Benchmarking
- ๐ท๏ธ Type Assertions (Assert & Expect)
- โฐ Time Simulation Testing
โจ Advanced Features โจ
๐ Multiple Test StylesWrite tests using function or object style syntax based on your preference
|
๐งฉ Cross-Runtime SupportTest once, run everywhere with automatic runtime detection
|
โก Async TestingTest asynchronous code with built-in async/await support
|
๐ Dual Assertion StylesChoose between expect() and assert style assertions
|
๐ Rich Matchers LibraryExtensive collection of assertion matchers for every testing need
|
|
๐ Keep reading to learn how to use all these amazing features! ๐
๐ฆ Install InSpatial Test:
Choose your preferred package manager:
deno install jsr:@inspatial/test
npx jsr add @inspatial/test
yarn dlx jsr add @inspatial/test
pnpm dlx jsr add @inspatial/test
bunx jsr add @inspatial/test
๐ ๏ธ Step-by-Step Usage Guide
Here are the essential usage patterns for working with InSpatial Test:
1. File Naming Convention
Create test files using either of these naming patterns:
file.test.ts(founders-choice)file_test.ts
// user.test.ts or user_test.ts import { expect, test } from "@inspatial/test"; test("user creation", () => { // Test code here });
2. Basic Test Structure
import { expect, test } from "@inspatial/test"; // Function style test("my first test", () => { expect(true).toBe(true); }); // Object style (recommended for complex tests) test({ name: "my first object-style test", fn: () => { expect(true).toBe(true); }, options: { // Optional configuration permissions: { read: true }, sanitizeResources: true, }, });
3. Assertion Styles
import { assert, assertEquals, expect, test } from "@inspatial/test"; test("using both assertion styles", () => { // Using expect style (chainable API) expect(42).toBe(42); expect("hello").toContain("ll"); expect(true).toBeTruthy(); // Using assert style (direct assertions) assert(true); assertEquals(42, 42); });
4. Using Describe & It Style (BDD)
InSpatial Test provides a natural way to organize your tests using Behavior-Driven Development (BDD) style:
import { describe, test, expect } from "@inspatial/test"; // Basic example - grouping related tests describe("Calculator", () => { test("should add two numbers correctly", () => { expect(2 + 2).toBe(4); }); test("should subtract numbers correctly", () => { expect(5 - 3).toBe(2); }); });
NOTE: you can also use it() in place of test e.g
import { describe, it, expect } from "@inspatial/test"; // Basic example - grouping related tests using `it` describe("Calculator", () => { it("should add two numbers correctly", () => { expect(2 + 2).toBe(4); }); it("should subtract numbers correctly", () => { expect(5 - 3).toBe(2); }); });
The BDD style really shines when testing complex features with multiple related behaviors:
import { describe, it, beforeEach, afterAll, expect } from "@inspatial/test"; describe("User Authentication", () => { let auth; let testUser; // Setup before each test beforeEach(() => { auth = authCloudExtension(); testUser = { username: "testuser", password: "P@ssw0rd" }; auth.boot(testUser); }); // Cleanup after all tests afterAll(() => { auth.clearAllUsers(); }); describe("Login Process", () => { it("should accept valid credentials", () => { const result = auth.login(testUser.username, testUser.password); expect(result.success).toBe(true); expect(result.token).toBeDefined(); }); it("should reject invalid passwords", () => { const result = auth.login(testUser.username, "wrong-password"); expect(result.success).toBe(false); expect(result.message).toContain("Invalid credentials"); }); it("should reject non-existent users", () => { const result = auth.login("nonexistent", "any-password"); expect(result.success).toBe(false); expect(result.message).toContain("User not found"); }); }); describe("Account Management", () => { it("should allow users to change passwords", () => { const newPassword = "NewP@ssw0rd"; auth.changePassword(testUser.username, testUser.password, newPassword); // Old password should no longer work expect(auth.login(testUser.username, testUser.password).success).toBe( false ); // New password should work expect(auth.login(testUser.username, newPassword).success).toBe(true); }); }); });
This approach offers several key benefits:
- Hierarchical organization: Nest test suites to reflect your feature structure
- Better readability: Tests read like natural language specifications
- Shared setup/teardown: Use hooks at different levels in the test hierarchy
- Focused testing: Run specific test suites with
.only()or skip them with.skip() - Living documentation: Tests serve as up-to-date documentation of how your system behaves
5. Mocking and Test Doubles
InSpatial Test provides powerful mocking capabilities to simulate behavior and verify interactions:
Function Spies
Use spy() to watch function calls without changing behavior:
import { test, spy, assertSpyCalls, assertSpyCall, assertEquals, } from "@inspatial/test"; test("spy example - verify function calls", () => { // Create a calculator object with a multiply function const calculator = { multiply(a: number, b: number): number { return a * b; }, }; // Create a spy for the multiply function using multiplySpyProp = spy(calculator, "multiply"); // Call the function const result = calculator.multiply(3, 4); // Verify the result assertEquals(result, 12); // Verify the function was called exactly once assertSpyCalls(multiplySpyProp, 1); // Verify it was called with specific arguments and returned the expected value assertSpyCall(multiplySpyProp, 0, { args: [3, 4], // Check arguments returned: 12, // Check return value }); });
Function Stubs
Use stub() to replace function implementation with controlled behavior:
import { test, stub, assertSpyCalls, assertSpyCall, assertEquals, returnsNext, } from "@inspatial/test"; test("stub example - replace unpredictable functions", () => { // Object with a method that returns random data const api = { fetchRandomData(): number { return Math.random(); // Unpredictable in tests }, }; // Create a stub that returns predetermined values using fetchStubProp = stub(api, "fetchRandomData", returnsNext([10, 20, 30])); // First call returns first value assertEquals(api.fetchRandomData(), 10); // Second call returns second value assertEquals(api.fetchRandomData(), 20); // Verify call count assertSpyCalls(fetchStubProp, 2); });
Mocking Time
Use FakeTime to control time-dependent code:
import { test, FakeTime, spy, assertSpyCalls } from "@inspatial/test"; test("FakeTime example - testing time-based functions", () => { // Create a FakeTime instance to control time using time = new FakeTime(); // Create a spy function to track calls const callback = spy(); // Set up a delayed function setTimeout(callback, 1000); // Initially, the callback hasn't been called assertSpyCalls(callback, 0); // Advance time by 500ms - still not enough time time.tick(500); assertSpyCalls(callback, 0); // Advance time by another 500ms - now callback should be called time.tick(500); assertSpyCalls(callback, 1); // Testing setInterval const intervalId = setInterval(callback, 1000); // Advance time by 3 seconds - should trigger callback 3 more times time.tick(3000); assertSpyCalls(callback, 4); // Clean up clearInterval(intervalId); });
Using Expect Syntax with Mocks
import { test, spy, stub, expect, returnsNext } from "@inspatial/test"; test("mock with expect syntax", () => { // Creating a spy const myFunction = spy(); myFunction(); expect(myFunction).toHaveBeenCalledTimes(1); // Creating a stub const api = { getData: () => Math.random() }; using dataStub = stub(api, "getData", returnsNext([1, 2, 3])); expect(api.getData()).toBe(1); expect(dataStub).toHaveBeenCalledTimes(1); });
Note: The using keyword ensures proper resource cleanup after the test completes.
๐ Running Tests
Basic Usage
# Node.js node --test # Node.js with TypeScript npx tsx --test # Requires "type": "module" in package.json # Deno deno test # Bun bun test
Advanced Options
# Test specific file deno test my_test.ts node --test my_test.ts bun test my_test.ts # Test specific folder deno test tests/ node --test tests/ bun test tests/ # Run tests in parallel (faster) deno test --parallel node --test --parallel bun test --preload ./setup.ts # Include extra settings deno test --allow-read my_test.ts # Give permission to read files node --test --experimental-test-coverage # Get coverage report bun test --coverage # Get coverage report
Watch Mode (Auto-rerun on changes)
deno test --watch node --test --watch bun test --watch
๐ Test Modifiers - Control Test Execution
Test modifiers help you control which tests run and which are skipped:
import { test } from "@inspatial/test"; // Run only this test (and others marked with .only) test.only("critical functionality", () => { // Test code }); // Skip this test during execution test.skip("unfinished test", () => { // This code won't run }); // Mark as something to implement later test.todo("implement user authentication tests");
๐งฉ Async Testing - Test Asynchronous Code
InSpatial Test has built-in support for testing asynchronous code:
import { expect, test } from "@inspatial/test"; // Test async functions test("async data fetching", async () => { const data = await fetchUserData(123); expect(data.id).toBe(123); expect(data.name).toBeDefined(); }); // Test promises directly test("promise resolution", () => { return Promise.resolve(42).then((value) => { expect(value).toBe(42); }); }); // Test for promise rejection test("promise rejection", async () => { await expect(Promise.reject("error")).rejects.toBe("error"); });
๐ธ Snapshot Testing - Taking "Photos" of Your Code's Output
Think of snapshot testing like taking a photo of your room. The first time you take the photo, that's your "reference picture" (snapshot). Later, when you want to check if anything has changed in your room, you take a new photo and compare it with the original one. If something is different (like moved furniture), you'll spot it right away!
In code terms:
- First run: The test takes a "photo" (snapshot) of your code's output
- Later runs: It takes a new "photo" and compares it with the original
- If they match โ Test passes โ
- If they differ โ Test fails โ (something changed!)
Basic Example
Here's a simple snapshot test for a user profile:
import { test, assertSnapshot } from "@inspatial/test"; test("user profile snapshot", async (t) => { const user = { name: "Ben", age: 24, email: "ben@inspatiallabs.com", }; // Take a "photo" of the user data await assertSnapshot(t, user); });
Advanced Example
Here's how to create a custom snapshot test for terminal output that ignores color codes:
import { test, createAssertSnapshot, stripAnsiCode } from "@inspatial/test"; // Create a custom snapshot function that removes color codes const assertColorlessSnapshot = createAssertSnapshot<string>({ serializer: (value) => stripAnsiCode(value), // Remove color codes before comparison name: "Terminal Output", // Custom name in snapshot file }); test("command output snapshot", async (t) => { const terminalOutput = "\x1b[32mSuccess!\x1b[0m"; // Green "Success!" // Compare without color codes await assertColorlessSnapshot(t, terminalOutput); });
Running Snapshot Tests
There are two ways to run snapshot tests:
- Check Mode (like comparing with the original photo):
# Using deno task deno task test:snapshot # Direct command deno test --allow-read --allow-write
- Update Mode (like taking a new reference photo):
# Using deno task deno task test:snapshot:update # Direct command deno test --allow-read --allow-write -- --update # or -u for short
Tip: Use
createAssertSnapshotwhen you need to:
- Filter out changing data (like timestamps)
- Transform data before comparison
- Customize snapshot storage location
- Reuse snapshot logic across multiple tests
--
Note: New snapshots will only be created when running in update mode. Use update mode when:
- Adding new snapshot assertions to your tests
- Making intentional changes that cause snapshots to fail
- Reviewing and accepting changes in snapshot output
For convenience, we provide simple commands:
# Windows ./run-tests.ps1 # Check against snapshots ./run-tests.ps1 -Update # Update snapshots # Linux/macOS ./run-tests.sh # Check against snapshots ./run-tests.sh --update # Update snapshots
โก Resource Cleanup - Automatic Resource Management
InSpatial Test can automatically verify that your tests properly clean up resources:
import { test } from "@inspatial/test"; test( "file operations", () => { // Test code that opens and closes files }, { // Verify all resources are properly closed sanitizeResources: true, // Verify all async operations complete sanitizeOps: true, } );
โ๏ธ CI Configuration
Bun
name: Bun CI on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: antongolub/action-setup-bun@v1.12.8 with: bun-version: v1.x - run: bun x jsr add @inspatial/test - run: bun test
Deno
name: Deno CI on: [push, pull_request] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: denoland/setup-deno@v1 with: deno-version: v1.x - run: deno add @inspatial/test - run: deno test
Node.js
name: Node.js CI on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest strategy: matrix: node-version: [18.x, 21.x] steps: - uses: actions/checkout@v3 - run: npx jsr add @inspatial/test - run: | echo '{"type":"module"}' > package.json npx --yes tsx --test *.test.ts
๐ฏ API Reference
Core Functions
| Function | Description |
|---|---|
test() |
Define a test |
test.skip() |
Define a test that will be skipped |
test.only() |
Define a test that will run exclusively |
test.todo() |
Define a placeholder for a future test |
expect() |
Create an assertion with chainable API |
assert |
Collection of direct assertion functions |
BDD Functions
| Function | Description |
|---|---|
describe() |
Create a test suite grouping related tests |
describe.only() |
Create a test suite that will run exclusively |
describe.skip() |
Create a test suite that will be skipped |
it() |
Define an individual test case |
it.only() |
Define a test case that will run exclusively |
it.skip() |
Define a test case that will be skipped |
beforeEach() |
Run setup code before each test in the suite |
afterEach() |
Run cleanup code after each test in the suite |
beforeAll() |
Run setup code once before all tests in the suite |
afterAll() |
Run cleanup code once after all tests in the suite |
before() |
Alias of beforeAll() |
after() |
Alias of afterAll() |
Mock Functions
| Function | Description |
|---|---|
spy() |
Create a spy to watch function calls |
stub() |
Replace function implementation for testing |
assertSpyCalls() |
Verify a function was called specific times |
assertSpyCall() |
Verify a function was called with specific args |
returnsNext() |
Create stub that returns values in sequence |
FakeTime |
Control time for testing time-dependent code |
Snapshot Functions
| Function | Description |
|---|---|
assertSnapshot() |
Compare a value against a stored snapshot |
createAssertSnapshot() |
Create a custom snapshot assertion function |
serialize() |
Default serializer for snapshot values |
Test Options
| Option | Description |
|---|---|
permissions |
Control what system features the test can access |
sanitizeResources |
Check if test properly closes resources |
sanitizeOps |
Check if test completes all async operations |
sanitizeExit |
Check if test tries to exit unexpectedly |
env |
Custom environment variables for the test |
Expect Matchers
| Matcher | Description |
|---|---|
toBe() |
Assert strict equality |
toEqual() |
Assert deep equality |
toBeTruthy() |
Assert value is truthy |
toBeFalsy() |
Assert value is falsy |
toBeGreaterThan() |
Assert value is greater than expected |
toBeLessThan() |
Assert value is less than expected |
toBeNull() |
Assert value is null |
toBeUndefined() |
Assert value is undefined |
toContain() |
Assert array/string contains an item/substring |
toHaveProperty() |
Assert object has a property |
toThrow() |
Assert function throws an error |
toBeType() |
Assert value is of a specific type |
toMatchObject() |
Assert object matches a partial shape |
toMatch() |
Assert string matches a regex pattern |
toBeOneOf() |
Assert value is one of the expected values |
toBeEmpty() |
Assert iterable/object/string is empty |
toBeSorted() |
Assert iterable is sorted |
toBeFinite() |
Assert number is finite |
toBeEmail() |
Assert string is a valid email |
toBeUrl() |
Assert string is a valid URL |
toBeDate() |
Assert value is a valid Date |
toRespondWithStatus() |
Assert Response has a specific status code |
Assert Functions
| Function | Description |
|---|---|
assert() |
Assert a condition is true |
assertEquals() |
Assert values are equal |
assertNotEquals() |
Assert values are not equal |
assertStrictEquals() |
Assert values are strictly equal |
assertNotStrictEquals() |
Assert values are not strictly equal |
assertArrayIncludes() |
Assert array includes all expected items |
assertStringIncludes() |
Assert string includes a substring |
assertMatch() |
Assert string matches a regex pattern |
assertThrows() |
Assert function throws an error |
assertObjectMatch() |
Assert object matches a partial shape |
assertIsError() |
Assert value is an Error object |
TypeScript Interfaces
This package exports the following TypeScript interfaces and types:
| Interface | Description |
|---|---|
TestProps |
Properties for defining object-style tests |
OptionProp |
Test configuration options |
Runner |
Test runner interface |
ExtendedExpected |
Enhanced expect assertion interface |
Promisable |
Type for values that might be Promises |
AssertionError |
Error thrown when assertions fail |
๐ค Contributing
We welcome contributions from the community! Please read our Contributing Guidelines to get started.
๐ License
InSpatial Test is released under the Apache 2.0 License. See the LICENSE file for details.
Start Testing with InSpatial
Add Package
deno add jsr:@inspatial/test
Import symbol
import * as test from "@inspatial/test";
Import directly with a jsr specifier
import * as test from "jsr:@inspatial/test";
Add Package
pnpm i jsr:@inspatial/test
pnpm dlx jsr add @inspatial/test
Import symbol
import * as test from "@inspatial/test";
Add Package
yarn add jsr:@inspatial/test
yarn dlx jsr add @inspatial/test
Import symbol
import * as test from "@inspatial/test";
Add Package
vlt install jsr:@inspatial/test
Import symbol
import * as test from "@inspatial/test";
Add Package
npx jsr add @inspatial/test
Import symbol
import * as test from "@inspatial/test";
Add Package
bunx jsr add @inspatial/test
Import symbol
import * as test from "@inspatial/test";