Write tests for Syncpack using the TestBuilder pattern. Use when adding tests for commands, validation logic, or any new functionality. Covers TestBuilder API, assertion patterns, and common test scenarios.
Resources
1Install
npx skillscat add jamiemason/syncpack/write-tests Install via the SkillsCat registry.
SKILL.md
Write Tests
Guide for writing tests in Syncpack using the TestBuilder pattern.
TDD Workflow (Mandatory)
- Study existing tests — Read 2-3 tests in same file, identify the pattern, match it exactly
- Write failing test — Never invent APIs; read source to see what exists
- Verify it fails — Run
just testto confirm - Ask user to confirm — Get approval before implementing
- Implement minimal code — Only what's needed to pass
- Clean up — Run
just format, fix warnings, refactor if needed
Golden Rule
Always use TestBuilder — Never manually construct Context in tests.
Quick Start
use {
crate::{
instance_state::{FixableInstance::*, InstanceState, ValidInstance::*},
test::{
builder::TestBuilder,
expect::{expect, ExpectedInstance},
},
},
serde_json::json,
};
#[test]
fn test_descriptive_name() {
let ctx = TestBuilder::new()
.with_packages(vec![
json!({"name": "pkg-a", "dependencies": {"react": "17.0.0"}}),
])
.with_version_group(json!({
"dependencies": ["react"],
"pinned": "18.0.0"
}))
.build_and_visit_packages();
expect(&ctx).to_have_instances(vec![
ExpectedInstance {
state: InstanceState::fixable(DiffersToPinnedVersion),
dependency_name: "react",
id: "react in /dependencies of pkg-a",
actual: "17.0.0",
expected: Some("18.0.0"),
overridden: None,
},
]);
}TestBuilder Methods
Packages
.with_package(json!({...})) // Single package
.with_packages(vec![json!({...})]) // Multiple packagesVersion Groups
.with_version_group(json!({...})) // Single group
.with_version_groups(vec![...]) // Multiple groupsConfiguration
.with_config(json!({...})) // Custom config
.with_semver_group(json!({...})) // Semver rules
.with_strict(true) // Strict modeRegistry (for update command)
.with_registry_updates(json!({"react": ["17.0.0", "18.0.0"]}))
.with_update_target(UpdateTarget::Latest)Build
.build() // Without visiting (rare)
.build_and_visit_packages() // With visiting (most common)Location String Format
{dependency} in {location} of {package}Examples:
"react in /dependencies of pkg-a""lodash in /devDependencies of pkg-b""pnpm in /packageManager of pkg-c"
Running Tests
just test # All tests
cargo test test_name -- --nocapture # Specific test with output
cargo test banned_test # Tests matching patternTest Organisation
- Integration tests:
src/visit_packages/*_test.rs(preferred) - Unit tests: Co-located as
*_test.rs(e.g.,src/foo.rs→src/foo_test.rs) - Test utilities:
src/test/builder.rs,src/test/expect.rs
Common Patterns
→ Full patterns and examples: patterns.md
Good Test Examples
Study these files:
src/visit_packages/banned_test.rs— Comprehensive examplessrc/visit_packages/pinned_test.rs— Version group testingsrc/visit_packages/local_test.rs— Local package handling
Common Mistakes
| Wrong | Right |
|---|---|
Context { ... } |
TestBuilder::new()... |
.build() then check states |
.build_and_visit_packages() |
ctx.instances[0] |
.find(|i| i.dependency.name == "react") |