Instructions for Agent
How This Skill is Invoked
In VS Code, users will activate this skill by:
- Typing
@workspace /testing [description]in Copilot Chat - Or asking: "Write a test for [feature]", "Add visual regression tests", "Review my test code"
When activated, write tests that strictly follow the project's testing standards.
Core Approach: Standards-First Testing
Every test MUST follow the Arrange-Act-Assert (AAA) pattern with all three phases as separate sections.
Before writing any test, load and review references/TESTING_EXAMPLES.md for concrete examples of correct and incorrect patterns.
Step 1: Determine Test Type
Ask the user what they need:
"What would you like to test? I support:
- Unit tests — Test a single class/function in isolation
- Integration tests — Test component interactions
- Visual regression tests — Compare images pixel-by-pixel
- Test review — Check existing tests against our standards"
Step 2: Write Tests Following AAA Pattern
The Three Phases (ALWAYS separate)
Every test MUST have three distinct comment sections:
TEST(SuiteName, MethodName_Condition_ExpectedResult)
{
// Arrange
// Set up test preconditions, create objects, define expected values
// Act
// Execute the single operation being tested
// Assert
// Verify the outcome
}
Critical Rules
- NEVER combine phases. Do not write
// Arrange & Actor// Act & Assert. Each phase gets its own comment and section.- Exception:
// Act & Assertis acceptable only forEXPECT_NO_THROW/EXPECT_THROWtests where the action IS the assertion.
- Exception:
- If no Arrange is needed, omit
// Arrangeentirely — start with// Act. - Move expected values to Arrange as named variables, not inline in Assert.
- One logical concept per test — split if testing multiple behaviors.
Naming Convention
Use format: UnitName_StateUnderTest_ExpectedResult
Examples:
MoveForward_IncreasesZPositionCompare_IdenticalImages_ReturnsMatchParsePPM_InvalidFile_ReturnsEmptyData
Step 3: Choose the Right Test Pattern
Unit Tests (tests/core/)
Test a single class method in isolation. Use MockOpenGL for OpenGL calls.
TEST(CameraTest, MoveForward_DefaultSpeed_IncreasesPosition)
{
// Arrange
Camera camera(800, 600);
camera.cameraPos = glm::vec3(0.0f, 0.0f, 0.0f);
camera.setSpeed(1.0f);
// Act
camera.moveForward();
// Assert
EXPECT_LT(camera.cameraPos.z, 0.0f);
}
Integration Tests (tests/integration/)
Test component interactions. May involve multiple classes.
TEST(DataLoadingPipelineTest, LoadSettings_ValidFile_PopulatesParticles)
{
// Arrange
SettingsIO settings;
std::string test_file = createTestSettingsFile();
// Act
bool result = settings.loadSettings(test_file);
// Assert
EXPECT_TRUE(result);
EXPECT_GT(settings.getParticleCount(), 0u);
}
Visual Regression Tests (tests/visual-regression/)
Use production classes (e.g., Particle) directly — never duplicate production logic in test helpers.
Use PixelComparator and the Image class for pixel comparison.
TEST_F(RenderingRegressionTest, RenderDefaultCube_AngledView_MatchesBaseline)
{
// Arrange
Shader particleShader(vertexPath.c_str(), fragmentPath.c_str());
Particle particles; // Uses production Particle class directly
glm::mat4 view = glm::lookAt(cameraPos, cameraTarget, cameraUp);
glm::mat4 projection = glm::perspective(glm::radians(45.0f), aspect, 0.1f, 3000.0f);
// Act
glContext_.bindFramebuffer();
renderParticle(particles, particleShader, view, projection);
Image currentImage = glContext_.captureFramebuffer();
// Assert
Image baseline = Image::load(baselinePath, ImageFormat::PNG);
PixelComparator comparator;
ComparisonResult result = comparator.compare(baseline, currentImage, tolerance, true);
EXPECT_TRUE(result.matches);
}
Step 4: Apply Test File Organization
Directory Structure
tests/core/— Unit tests forsrc/*.hppclassestests/integration/— Multi-component teststests/testing/— Tests for test utilities (PixelComparator, Image)tests/visual-regression/— Visual regression teststests/mocks/— Mock implementations
File Naming
Each test file matches its source: CameraTests.cpp tests camera.hpp.
Test Ordering
Within a file, order tests: basic → complex, common → edge cases.
Step 5: Self-Review Checklist
Before presenting tests, verify:
- Every test has separate
// Arrange,// Act,// Assertcomments (no// Arrange & Act) - Test name follows
UnitName_StateUnderTest_ExpectedResultpattern - Expected values are named variables in Arrange (not inline literals in Assert)
- One logical concept per test
- External dependencies are mocked (OpenGL, file I/O)
- No testing of external libraries (std::, third-party code)
- Visual regression tests use production classes (Particle, Camera) — no duplicated test helpers
- Group related configuration into structs/POCOs instead of flat variables
- Resource cleanup: GL objects deleted in destructors/cleanup, check for leaks
- Test
SetUp()ensures all output directories exist (artifacts/, baselines/, diffs/) - Artifact
save()return values are checked, not silently ignored - Visual test resolution matches viewer defaults (1280x720) unless specifically testing other resolutions
- Tests compile and pass
Key Design Principles (Learned from Review Feedback)
Use production classes in tests. Visual regression tests should use
Particledirectly instead of re-implementing particle creation logic in a test helper class. This ensures tests stay in sync with production code.Group related data into POCOs/structs. When a test or test helper has many flat member variables, group them into domain-specific structs (e.g.,
RenderConfig,CameraSetup). This mirrors the production code pattern.Clean up GL resources. Every test that creates GL objects (VAOs, VBOs, FBOs, textures) must clean them up. Check for leaks in
cleanup()/ destructors.Binary file I/O. Always open binary data files with
"rb"mode (not"r") for cross-platform correctness.Ensure output directories exist. In test
SetUp(), create all output directories (artifacts/, baselines/, diffs/) before tests run. Checksave()return values so failures are actionable, not silent.Visual test resolution. Use the viewer's default resolution (1280x720) for visual regression tests unless specifically testing other resolutions. Non-default resolutions can cause warping and scaling artifacts.
Camera positioning for visual tests. Don't blindly copy debug camera coordinates — debug shows interactive state, not ideal test framing. Extract the viewing direction from debug output, then calculate distance based on desired viewport coverage:
distance = subject_size / (coverage_% * tan(FOV/2)). Seedocs/visual-regression/camera-positioning-lessons-learned.md.
Self-Evaluation
After completing test work, run the self-evaluation skill (.github/skills/self-evaluation/) to capture any new testing patterns learned during the session.
Key Types for Visual Regression
| Type | Location | Purpose |
|---|---|---|
Image |
src/Image.hpp |
RGBA pixel buffer with save/load (PPM, PNG) |
ComparisonResult |
src/testing/PixelComparator.hpp |
Match status, similarity, diff image |
PixelComparator |
src/testing/PixelComparator.hpp |
Pixel comparison engine |
ImageFormat |
src/Image.hpp |
Format enum (PPM, PNG) for Image::save/load |
Particle |
src/particle.hpp |
Production particle data (std::vectorglm::vec4) |
Common Edge Cases to Handle
- Ask if the user wants
EXPECT_*(non-fatal) orASSERT_*(fatal) assertions - For visual tests: recommend
0.0ftolerance for synthetic data,2.0f/255.0ffor GPU-rendered - If a test seems to test external library behavior, suggest focusing on the wrapper/integration instead
- If Arrange and Act seem identical, the test might be a constructor test — put expected values in Arrange, constructor call in Act
CI Pipeline Rules
For CI workflow rules (artifact uploads, permissions, PR comments), see the workflow skill (.github/skills/workflow/).
Converted and distributed by TomeVault — claim your Tome and manage your conversions.