# How to Run Python Unit Tests for i-have-adhd Hooks: A Complete Guide

> Learn how to run Python unit tests for i-have-adhd hooks. Discover commands to execute the full test suite or validate specific hooks efficiently.

- Repository: [Ayoub Ghriss/i-have-adhd](https://github.com/ayghri/i-have-adhd)
- Tags: how-to-guide
- Published: 2026-08-31

---

**Use `python3 -m unittest discover -s tests -v` to run the full test suite, or `python3 -m unittest tests.test_always_on_hooks` for hook-specific validation.**

The i-have-adhd repository provides **SessionStart hooks** for Claude and Codex that automatically inject an ADHD ruleset into every session when users opt in. To ensure these cross-platform hooks work reliably, the project maintains a Python unit test suite in [`tests/test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_always_on_hooks.py). This guide covers how to run Python unit tests for i-have-adhd hooks locally, what the tests verify, and how to interpret the results.

## Prerequisites: Install Required Runtimes

The hook tests exercise three runtime environments: Node.js, Bash, and PowerShell. Install these before running tests.

On **Linux/macOS**:

```bash

# Node.js (required for the primary hook)

sudo apt-get install nodejs   # Debian/Ubuntu

brew install node             # macOS

# PowerShell (optional but tested)

sudo apt-get install powershell

```

On **Windows**:

```powershell

# Using Chocolatey

choco install nodejs

# PowerShell is built-in; Bash available via WSL or Git Bash

```

Verify installations:

```bash
node --version
bash --version
pwsh --version

```

## Clone and Navigate to the Repository

```bash
git clone https://github.com/ayghri/i-have-adhd.git
cd i-have-adhd

```

The test suite expects this directory structure:

```

i-have-adhd/
├── hooks/
│   ├── always-on.mjs
│   ├── always-on.sh
│   ├── always-on.ps1
│   └── hooks.json
├── skills/i-have-adhd/SKILL.md
└── tests/
    └── test_always_on_hooks.py

```

## Run All Python Unit Tests

Execute the complete test discovery to validate the entire codebase:

```bash
python3 -m unittest discover -s tests -v

```

The `-v` (verbose) flag shows individual test names and results. Expect output like:

```

test_flag_absent_hooks_silent (tests.test_always_on_hooks.TestAlwaysOnHooks) ... ok
test_flag_present_shows_ruleset (tests.test_always_on_hooks.TestAlwaysOnHooks) ... ok
test_frontmatter_stripping (tests.test_always_on_hooks.TestAlwaysOnHooks) ... ok
...
----------------------------------------------------------------------
Ran 5 tests in 2.34s

OK

```

## Run Only the Hook Tests

For rapid iteration during hook development, target the specific test module:

```bash
python3 -m unittest tests.test_always_on_hooks

```

Or with verbosity:

```bash
python3 -m unittest tests.test_always_on_hooks -v

```

## What the Python Unit Tests Validate

The [`test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/test_always_on_hooks.py) suite performs comprehensive validation of the i-have-adhd hook system:

### **Temporary Environment Setup**

- Creates a mock plugin directory structure
- Copies `hooks/` and `skills/` folders to isolated temp locations
- Configures `$CLAUDE_CONFIG_DIR` pointing to a temporary path

### **Flag File Behavior**

- **Absent flag**: Verifies all three hook runtimes (`node always-on.mjs`, `sh always-on.sh`, `pwsh always-on.ps1`) produce **no output**
- **Present flag**: Creates `.i-have-adhd-always` in the temp config directory and confirms the ADHD ruleset prints to stdout

### **Front-Matter Processing**

The tests verify [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) parsing handles edge cases:
- Standard YAML front-matter delimiters (`---`) are stripped correctly
- Stray whitespace around delimiters doesn't break parsing
- Unclosed delimiters are handled gracefully

### **Codex Launcher Configuration**

Cross-references [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) against the actual `hooks/always-on.mjs` implementation:
- Confirms the `command` field invokes the shared Node launcher
- Validates environment variable references like `$CLAUDE_CONFIG_DIR`

## Manual Hook Testing Outside Python

To observe hook behavior directly without the test framework:

```bash

# Without opt-in flag: silent

node hooks/always-on.mjs

# Enable opt-in

mkdir -p ~/.claude
touch ~/.claude/.i-have-adhd-always

# Now produces output

node hooks/always-on.mjs

```

Test Bash and PowerShell variants:

```bash
sh hooks/always-on.sh          # Unix fallback

pwsh hooks/always-on.ps1       # Windows fallback

```

Remove the flag to silence output again:

```bash
rm ~/.claude/.i-have-adhd-always

```

## Key Files in the Test System

| File | Purpose |
|------|---------|
| [`tests/test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_always_on_hooks.py) | Main Python unittest suite with `TestAlwaysOnHooks` class |
| `hooks/always-on.mjs` | Primary Node.js hook implementation |
| [`hooks/always-on.sh`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.sh) | Bash implementation for Unix compatibility |
| `hooks/always-on.ps1` | PowerShell implementation for Windows |
| [`hooks/hooks.json`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/hooks.json) | Claude/Codex hook configuration schema |
| [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) | Source content injected by hooks |

## Troubleshooting Common Test Failures

### **Missing Runtime Errors**

If you see `FileNotFoundError` or `subprocess` failures, verify the runtime is in your PATH:

```bash
which node
which pwsh

```

### **Permission Denied on Hooks**

Ensure hook scripts are executable:

```bash
chmod +x hooks/always-on.sh
chmod +x hooks/always-on.mjs   # if directly executed

```

### **Windows-Specific Issues**

PowerShell execution policy may block scripts. Adjust as needed:

```powershell
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser

```

## Summary

Running Python unit tests for i-have-adhd hooks ensures cross-platform reliability of the SessionStart injection system:

- **Full suite**: `python3 -m unittest discover -s tests -v`
- **Hook-only**: `python3 -m unittest tests.test_always_on_hooks`
- The tests in [`tests/test_always_on_hooks.py`](https://github.com/ayghri/i-have-adhd/blob/main/tests/test_always_on_hooks.py) validate Node, Bash, and PowerShell runtimes
- Flag file behavior, front-matter stripping, and launcher configuration are all verified
- Manual testing requires the `.i-have-adhd-always` opt-in flag in `~/.claude/`

## Frequently Asked Questions

### What Python version is required to run the i-have-adhd hook tests?

The tests use standard `unittest` from the Python standard library, compatible with **Python 3.7+**. No external test dependencies are required.

### Why do the tests need Node.js and PowerShell installed?

The test suite invokes actual hook scripts via `subprocess.run` to verify real-world behavior. Since `hooks/always-on.mjs`, [`hooks/always-on.sh`](https://github.com/ayghri/i-have-adhd/blob/main/hooks/always-on.sh), and `hooks/always-on.ps1` target different platforms, the tests exercise each runtime to catch environment-specific regressions.

### Can I run the tests without PowerShell?

Yes, but tests targeting the PowerShell hook will be skipped or fail. The Node.js hook is the primary implementation; Bash and PowerShell serve as fallbacks. For full coverage on Linux/macOS, install PowerShell via your package manager.

### What does "front-matter stripping" mean in the test context?

The [`skills/i-have-adhd/SKILL.md`](https://github.com/ayghri/i-have-adhd/blob/main/skills/i-have-adhd/SKILL.md) file contains YAML front-matter (metadata between `---` delimiters) that Claude uses for skill discovery. The hooks must output only the ruleset content, not this metadata. The tests verify this stripping works correctly even with malformed delimiters.