1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
|
---
name: pairwise-tests
description: Generate a minimal test matrix covering all pairwise parameter interactions using PICT (Pairwise Independent Combinatorial Testing). Given a function, feature, or configuration space with many input parameters, identifies parameters + value partitions + inter-parameter constraints, builds a PICT model, and produces a small set of test cases (typically 80-99% fewer than exhaustive) that still hits every 2-way combination of parameter values. Empirically catches 60-90% of combinatorial bugs. Use when a function has 3+ parameters with multiple values each and exhaustive testing would explode (feature flag combinations, permission/role matrices, config matrices, multi-field form validation, API parameter spaces). Do NOT use for functions with 1-2 parameters (just write the cases directly), for regulated contexts where provably exhaustive coverage is required (document as an ADR and write all cases), or for testing non-parametric behavior (happy path, single error case, performance regression — use `/add-tests` for those). Output: PICT model, markdown table of generated test cases, expected result per row. See also `/add-tests` for standard test generation and `testing.md` for the Normal/Boundary/Error category discipline.
---
# Pairwise Tests (PICT)
This skill enables systematic test case design using PICT (Pairwise Independent Combinatorial Testing). Given requirements or code, it analyzes the system to identify test parameters, generates a PICT model with appropriate constraints, executes the model to generate pairwise test cases, and formats the results with expected outputs.
## When to Use This Skill
Use this skill when:
- Designing test cases for a feature, function, or system with multiple input parameters
- Creating test suites for configurations with many combinations
- Needing comprehensive coverage with minimal test cases
- Analyzing requirements to identify test scenarios
- Working with code that has multiple conditional paths
- Building test matrices for API endpoints, web forms, or system configurations
## Workflow
Follow this process for test design:
### 1. Analyze Requirements or Code
From the user's requirements or code, identify:
- **Parameters**: Input variables, configuration options, environmental factors
- **Values**: Possible values for each parameter (using equivalence partitioning)
- **Constraints**: Business rules, technical limitations, dependencies between parameters
- **Expected Outcomes**: What should happen for different combinations
**Example Analysis:**
For a login function with requirements:
- Users can login with username/password
- Supports 2FA (on/off)
- Remembers login on trusted devices
- Rate limits after 3 failed attempts
Identified parameters:
- Credentials: Valid, Invalid
- TwoFactorAuth: Enabled, Disabled
- RememberMe: Checked, Unchecked
- PreviousFailures: 0, 1, 2, 3, 4
### 2. Generate PICT Model
Create a PICT model with:
- Clear parameter names
- Well-defined value sets (using equivalence partitioning and boundary values)
- Constraints for invalid combinations
- Comments explaining business rules
**Model Structure:**
```
# Parameter definitions
ParameterName: Value1, Value2, Value3
# Constraints (if any)
IF [Parameter1] = "Value" THEN [Parameter2] <> "OtherValue";
```
**Refer to references/pict_syntax.md for:**
- Complete syntax reference
- Constraint grammar and operators
- Advanced features (sub-models, aliasing, negative testing)
- Command-line options
- Detailed constraint patterns
**Refer to references/examples.md for:**
- Complete real-world examples by domain
- Software function testing examples
- Web application, API, and mobile testing examples
- Database and configuration testing patterns
- Common patterns for authentication, resource access, error handling
### 3. Execute PICT Model
Generate the PICT model text and format it for the user. You can use Python code directly to work with the model:
```python
# Define parameters and constraints
parameters = {
"OS": ["Windows", "Linux", "MacOS"],
"Browser": ["Chrome", "Firefox", "Safari"],
"Memory": ["4GB", "8GB", "16GB"]
}
constraints = [
'IF [OS] = "MacOS" THEN [Browser] IN {Safari, Chrome}',
'IF [Memory] = "4GB" THEN [OS] <> "MacOS"'
]
# Generate model text
model_lines = []
for param_name, values in parameters.items():
values_str = ", ".join(values)
model_lines.append(f"{param_name}: {values_str}")
if constraints:
model_lines.append("")
for constraint in constraints:
if not constraint.endswith(';'):
constraint += ';'
model_lines.append(constraint)
model_text = "\n".join(model_lines)
print(model_text)
```
**Using the helper script (optional):**
The `scripts/pict_helper.py` script provides utilities for model generation and output formatting:
```bash
# Generate model from JSON config
python scripts/pict_helper.py generate config.json
# Format PICT tool output as markdown table
python scripts/pict_helper.py format output.txt
# Parse PICT output to JSON
python scripts/pict_helper.py parse output.txt
```
**To generate actual test cases**, the user can:
1. Save the PICT model to a file (e.g., `model.txt`)
2. Use online PICT tools like:
- https://pairwise.yuuniworks.com/
- https://pairwise.teremokgames.com/
3. Or install PICT locally (see references/pict_syntax.md)
### 4. Determine Expected Outputs
For each generated test case, determine the expected outcome based on:
- Business requirements
- Code logic
- Valid/invalid combinations
Create a list of expected outputs corresponding to each test case.
### 5. Format Complete Test Suite
Provide the user with:
1. **PICT Model** - The complete model with parameters and constraints
2. **Markdown Table** - Test cases in table format with test numbers
3. **Expected Outputs** - Expected result for each test case
## Output Format
Present results in this structure:
````markdown
## PICT Model
```
# Parameters
Parameter1: Value1, Value2, Value3
Parameter2: ValueA, ValueB
# Constraints
IF [Parameter1] = "Value1" THEN [Parameter2] = "ValueA";
```
## Generated Test Cases
| Test # | Parameter1 | Parameter2 | Expected Output |
| --- | --- | --- | --- |
| 1 | Value1 | ValueA | Success |
| 2 | Value2 | ValueB | Success |
| 3 | Value1 | ValueB | Error: Invalid combination |
...
## Test Case Summary
- Total test cases: N
- Coverage: Pairwise (all 2-way combinations)
- Constraints applied: N
````
## Best Practices
### Parameter Identification
**Good:**
- Use descriptive names: `AuthMethod`, `UserRole`, `PaymentType`
- Apply equivalence partitioning: `FileSize: Small, Medium, Large` instead of `FileSize: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10`
- Include boundary values: `Age: 0, 17, 18, 65, 66`
- Add negative values for error testing: `Amount: ~-1, 0, 100, ~999999`
**Avoid:**
- Generic names: `Param1`, `Value1`, `V1`
- Too many values without partitioning
- Missing edge cases
### Constraint Writing
**Good:**
- Document rationale: `# Safari only available on MacOS`
- Start simple, add incrementally
- Test constraints work as expected
**Avoid:**
- Over-constraining (eliminates too many valid combinations)
- Under-constraining (generates invalid test cases)
- Complex nested logic without clear documentation
### Expected Output Definition
**Be specific:**
- "Login succeeds, user redirected to dashboard"
- "HTTP 400: Invalid credentials error"
- "2FA prompt displayed"
**Not vague:**
- "Works"
- "Error"
- "Success"
### Scalability
For large parameter sets:
- Use sub-models to group related parameters with different orders
- Consider separate test suites for unrelated features
- Start with order 2 (pairwise), increase for critical combinations
- Typical pairwise testing reduces test cases by 80-90% vs exhaustive
## Common Patterns
### Web Form Testing
```python
parameters = {
"Name": ["Valid", "Empty", "TooLong"],
"Email": ["Valid", "Invalid", "Empty"],
"Password": ["Strong", "Weak", "Empty"],
"Terms": ["Accepted", "NotAccepted"]
}
constraints = [
'IF [Terms] = "NotAccepted" THEN [Name] = "Valid"', # Test validation even if terms not accepted
]
```
### API Endpoint Testing
```python
parameters = {
"HTTPMethod": ["GET", "POST", "PUT", "DELETE"],
"Authentication": ["Valid", "Invalid", "Missing"],
"ContentType": ["JSON", "XML", "FormData"],
"PayloadSize": ["Empty", "Small", "Large"]
}
constraints = [
'IF [HTTPMethod] = "GET" THEN [PayloadSize] = "Empty"',
'IF [Authentication] = "Missing" THEN [HTTPMethod] IN {GET, POST}'
]
```
### Configuration Testing
```python
parameters = {
"Environment": ["Dev", "Staging", "Production"],
"CacheEnabled": ["True", "False"],
"LogLevel": ["Debug", "Info", "Error"],
"Database": ["SQLite", "PostgreSQL", "MySQL"]
}
constraints = [
'IF [Environment] = "Production" THEN [LogLevel] <> "Debug"',
'IF [Database] = "SQLite" THEN [Environment] = "Dev"'
]
```
## Troubleshooting
### No Test Cases Generated
- Check constraints aren't over-restrictive
- Verify constraint syntax (must end with `;`)
- Ensure parameter names in constraints match definitions (use `[ParameterName]`)
### Too Many Test Cases
- Verify using order 2 (pairwise) not higher order
- Consider breaking into sub-models
- Check if parameters can be separated into independent test suites
### Invalid Combinations in Output
- Add missing constraints
- Verify constraint logic is correct
- Check if you need to use `NOT` or `<>` operators
### Script Errors
- Ensure pypict is installed: `pip install pypict --break-system-packages`
- Check Python version (3.7+)
- Verify model syntax is valid
## References
- **references/pict_syntax.md** - Complete PICT syntax reference with grammar and operators
- **references/examples.md** - Comprehensive real-world examples across different domains
- **scripts/pict_helper.py** - Python utilities for model generation and output formatting
- [PICT GitHub Repository](https://github.com/microsoft/pict) - Official PICT documentation
- [pypict Documentation](https://github.com/kmaehashi/pypict) - Python binding documentation
- [Online PICT Tools](https://pairwise.yuuniworks.com/) - Web-based PICT generator
## Examples
### Example 1: Simple Function Testing
**User Request:** "Design tests for a divide function that takes two numbers and returns the result."
**Analysis:**
- Parameters: dividend (number), divisor (number)
- Values: Using equivalence partitioning and boundaries
- Numbers: negative, zero, positive, large values
- Constraints: Division by zero is invalid
- Expected outputs: Result or error
**PICT Model:**
```
Dividend: -10, 0, 10, 1000
Divisor: ~0, -5, 1, 5, 100
IF [Divisor] = "0" THEN [Dividend] = "10";
```
**Test Cases:**
| Test # | Dividend | Divisor | Expected Output |
| --- | --- | --- | --- |
| 1 | 10 | 0 | Error: Division by zero |
| 2 | -10 | 1 | -10.0 |
| 3 | 0 | -5 | 0.0 |
| 4 | 1000 | 5 | 200.0 |
| 5 | 10 | 100 | 0.1 |
### Example 2: E-commerce Checkout
**User Request:** "Design tests for checkout flow with payment methods, shipping options, and user types."
**Analysis:**
- Payment: Credit Card, PayPal, Bank Transfer (limited by user type)
- Shipping: Standard, Express, Overnight
- User: Guest, Registered, Premium
- Constraints: Guests can't use Bank Transfer, Premium users get free Express
**PICT Model:**
```
PaymentMethod: CreditCard, PayPal, BankTransfer
ShippingMethod: Standard, Express, Overnight
UserType: Guest, Registered, Premium
IF [UserType] = "Guest" THEN [PaymentMethod] <> "BankTransfer";
IF [UserType] = "Premium" AND [ShippingMethod] = "Express" THEN [PaymentMethod] IN {CreditCard, PayPal};
```
**Output:** 12-15 test cases covering all valid payment/shipping/user combinations with expected costs and outcomes.
---
## Attribution
Forked from [omkamal/pypict-claude-skill](https://github.com/omkamal/pypict-claude-skill) — MIT licensed. See `LICENSE` in this directory for the original copyright and terms.
**Local changes:**
- Skill renamed from `pict-test-designer` to `pairwise-tests`. PICT remains the implementation; the user-facing name leads with the technique (pairwise / combinatorial) rather than the tool brand.
- Description rewritten to lead with the "what" (pairwise coverage, 2-way interaction), include explicit negative triggers (≤2 parameters, regulated contexts requiring exhaustive coverage, non-parametric testing), and cross-reference `/add-tests` and `testing.md`.
- Omitted from the fork: `examples/` directory (ATM + gearbox worked examples — useful as a walkthrough, not needed at skill-runtime) and repo-level metadata (README, QUICKSTART, CONTRIBUTING, releases/, .claude-plugin/). The skill-runtime files — SKILL.md, LICENSE, `references/pict_syntax.md`, `references/examples.md`, `scripts/pict_helper.py` + `scripts/README.md` — are intact.
|