UnitTestPlan.md

February 21, 2026 ยท View on GitHub

Hack23 Logo

๐Ÿงช Unit Test Plan for CIA Compliance Manager

๐Ÿ“‹ Comprehensive Unit Testing Strategy
๐Ÿ”— Secure Development Policy ยท Vulnerability Management

๐Ÿ” ISMS Alignment: This unit test plan implements Secure Development Policy Section 4.3.1 - Unit Testing Requirements.

1. Overview

This document outlines the unit testing strategy for the CIA Compliance Manager application. The application is built using React with TypeScript, and unit tests are implemented using Vitest.

ISMS Compliance Requirements

Per Hack23 AB's Secure Development Policy, this project maintains:

๐ŸŽฏ Requirement๐Ÿ“Š Targetโœ… Current๐Ÿ“‹ ISMS Reference
Line Coverageโ‰ฅ80%83%Section 4.3.1.1
Branch Coverageโ‰ฅ75% (v1.0)75.39%Section 4.3.1.2
Test ExecutionEvery commitโœ… AutomatedSection 4.3.1.3
Public ReportingRequiredโœ… PublishedSection 4.3.1.4

Evidence Links:

See Also: ISMS Implementation Guide - Testing Strategy


2. Testing Framework

  • Primary framework: Vitest
  • Test environment: JSDOM
  • Coverage tool: V8 (via Vitest)
  • Target coverage: 80% line coverage minimum

3. Test Organization

3.1 File Structure

Unit tests should be placed alongside their implementation files with the .test.tsx or .test.ts extension:

src/
โ”œโ”€โ”€ components/
โ”‚   โ”œโ”€โ”€ Component.tsx
โ”‚   โ””โ”€โ”€ Component.test.tsx
โ”œโ”€โ”€ utils/
โ”‚   โ”œโ”€โ”€ helper.ts
โ”‚   โ””โ”€โ”€ helper.test.ts

3.2 Test Categories

  • Component tests: Test React components in isolation with mocked dependencies
  • Utility tests: Test utility functions, hooks, and services
  • Integration tests: Test small groups of components working together

4. Testing Standards

4.1 Component Testing

  • Test rendering without crashing
  • Test all component props and variations
  • Test state changes and user interactions
  • Test conditional rendering logic
  • Mock external dependencies and services

4.2 Test Doubles

  • Use mocks for external services and dependencies
  • Use test fixtures for complex data structures
  • Use fake timers for time-dependent functions

5. Code Coverage Requirements

  • Statements: 80% minimum
  • Branches: 75% minimum (v1.0 release target)
  • Functions: 80% minimum
  • Lines: 80% minimum

5.1 Branch Coverage Strategies

Branch coverage ensures that all conditional paths in the code are tested. To achieve and maintain 75%+ branch coverage:

5.1.1 Conditional Logic Testing

Test all branches of conditional statements:

// Function with conditional logic
export function getStatusVariant(level: string): StatusType {
  const normalizedLevel = level.toLowerCase();
  if (normalizedLevel === "none") return "error";
  if (normalizedLevel === "low") return "warning";
  if (normalizedLevel === "moderate") return "info";
  if (normalizedLevel === "high") return "success";
  if (normalizedLevel === "very high") return "purple";
  return "neutral";
}

// Tests covering all branches
describe("getStatusVariant", () => {
  it("returns error for none level", () => {
    expect(getStatusVariant("none")).toBe("error");
  });

  it("returns warning for low level", () => {
    expect(getStatusVariant("low")).toBe("warning");
  });

  // ... test each branch

  it("returns neutral for unknown levels", () => {
    expect(getStatusVariant("unknown")).toBe("neutral");
  });
});

5.1.2 Error Handling Paths

Test both success and error paths:

// Test error handling branches
it("handles Error objects", () => {
  const error = new Error("Test error");
  expect(toErrorObject(error)).toBe(error);
});

it("handles null/undefined", () => {
  expect(toErrorObject(null)).toBeInstanceOf(Error);
  expect(toErrorObject(undefined)).toBeInstanceOf(Error);
});

it("handles string errors", () => {
  const result = toErrorObject("String error");
  expect(result.message).toBe("String error");
});

5.1.3 Null/Undefined Checks

Test optional chaining and nullish coalescing:

// Function with fallback
export function getValue(level: SecurityLevel): number {
  return levelValues[level] || defaultValue;
}

// Test both branches
it("returns value for valid level", () => {
  expect(getValue("High")).toBe(1);
});

it("returns default for invalid level", () => {
  expect(getValue("Unknown" as any)).toBe(defaultValue);
});

5.1.4 Boolean Logic

Test all combinations of && and || operators:

// Test complex conditions
it("validates all conditions", () => {
  expect(isValid(true, true)).toBe(true);   // Both true
  expect(isValid(true, false)).toBe(false); // First true
  expect(isValid(false, true)).toBe(false); // Second true
  expect(isValid(false, false)).toBe(false); // Both false
});

5.1.5 Switch Statements

Test all cases including default:

describe("switch statement coverage", () => {
  it("handles case A", () => { /* test */ });
  it("handles case B", () => { /* test */ });
  it("handles default case", () => { /* test */ });
});

5.1.6 Ternary Operators

Test both branches of conditional expressions:

// Function with ternary
const color = isActive ? "green" : "gray";

// Tests
it("returns green when active", () => {
  expect(getColor(true)).toBe("green");
});

it("returns gray when inactive", () => {
  expect(getColor(false)).toBe("gray");
});

5.2 Coverage Enforcement

The project enforces coverage thresholds in vite.config.ts:

coverage: {
  thresholds: {
    statements: 80,
    branches: 75,  // v1.0 release requirement
    functions: 80,
    lines: 80,
  },
}

CI builds will fail if coverage falls below these thresholds.

6. Running Tests

  • Development: npm run test
  • CI/CD: npm run test:ci
  • Coverage report: npm run test:coverage

7. Pull Request Requirements

All pull requests must:

  • Include tests for new features and bug fixes
  • Not decrease the overall code coverage
  • Pass all existing tests

8. Common Test Patterns

8.1 Component Test Example

import { render, screen, fireEvent } from "@testing-library/react";
import { describe, it, expect, vi } from "vitest";
import Component from "./Component";

describe("Component", () => {
  it("renders correctly", () => {
    render(<Component />);
    expect(screen.getByText("Expected Text")).toBeInTheDocument();
  });

  it("handles click events", () => {
    const handleClick = vi.fn();
    render(<Component onClick={handleClick} />);
    fireEvent.click(screen.getByRole("button"));
    expect(handleClick).toHaveBeenCalledTimes(1);
  });
});

8.2 Utility Function Test Example

import { describe, it, expect } from "vitest";
import { formatData } from "./utils";

describe("formatData", () => {
  it("formats data correctly", () => {
    const input = { key: "value" };
    const expected = { formattedKey: "VALUE" };
    expect(formatData(input)).toEqual(expected);
  });

  it("handles empty input", () => {
    expect(formatData({})).toEqual({});
  });
});