Skills DirectorySkills Directory
SkillsLearnSecurityCategoriesDocsCommunityBlog
Sign InSubmit Skill
Skills Directory

Security-tested agent skills for Claude, coding agents, and AI workflows.

Directory

  • Browse Skills
  • All Skills A–Z
  • Claude Skills
  • Claude Code Skills
  • Agent Skills
  • Categories
  • Submit a Skill

Learn

  • Learn Hub
  • Install Claude Skills
  • Write SKILL.md
  • Skills vs MCP
  • Directories Compared

Security

  • Security
  • Methodology
  • Secure Claude Skills
  • Security Badges

Company

  • About
  • Community
  • Blog
  • API Docs
  • Advertise

2026 Skills Directory. All rights reserved.

Back to skills

Testing Metamorphic

ASecurity

Design and implement metamorphic testing for systems with the oracle problem. Use when: testing ML models, scientific computing, compilers, search engines, databases, graphics pipelines, or any system where correct output is unknown but input-output relationships are predictable. Metamorphic relations, property-based testing, MR taxonomy, oracle-free verification.

22 stars
0 votes
0 copies
0 views
Added 9/20/2026
testingpythonrustgosqltestingdatabaseperformance

Security Analysis

A100/100

Scanned 9/20/2026

Install to Claude Code

$npx -y skills add lev-os/agents --skill testing-metamorphic --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Testing Metamorphic?

Add the live security badge to your README — it updates automatically with every re-scan.

Security grade badge for Testing Metamorphic
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lev-os-testing-metamorphic/badge)](https://www.skillsdirectory.com/skills/lev-os-testing-metamorphic)

More formats (shields.io, HTML) on the badges page.

Download Zip
Files
SKILL.md
---
name: testing-metamorphic
description: >-
  Design and implement metamorphic testing for systems with the oracle problem.
  Use when: testing ML models, scientific computing, compilers, search engines,
  databases, graphics pipelines, or any system where correct output is unknown
  but input-output relationships are predictable. Metamorphic relations, property-based
  testing, MR taxonomy, oracle-free verification.
---

# Metamorphic Testing

> **The One Rule:** When you can't verify *what* the output is, verify *how* outputs
> relate to each other under known input transformations. Never guess at oracles.

## The Loop (Mandatory)

```
1. DIAGNOSE    → Is this an oracle problem? Can you compute expected output?
2. ENUMERATE   → List ALL domain properties as candidate MRs
3. CLASSIFY    → Score each MR: fault-sensitivity × independence × cost
4. IMPLEMENT   → One MR per test function, property-based input generation
5. COMPOSE     → Chain simple MRs into compound checks (multiplicative power)
6. VALIDATE    → Mutation testing: does each MR actually catch planted bugs?
7. ITERATE     → Failed MRs reveal both code bugs AND weak relation design
```

## MR Strength Matrix (Mandatory)

Before implementing, score every candidate metamorphic relation:

| MR Candidate | Fault Sensitivity (1-5) | Independence (1-5) | Cost (1-5) | Score |
|-------------|------------------------|--------------------:|------------|-------|
| *description* | How many bug classes? | Orthogonal to others? | ÷ Runtime | F×I/C |

**Rule:** Only implement Score ≥ 2.0. Low-scoring MRs waste test budget.

**Independence matters:** Two MRs that detect the same bug class are redundant.
An MR suite of 5 independent relations catches more than 20 correlated ones.

---

## The Oracle Problem — Decision Tree

```
Can you compute expected output for arbitrary inputs?
│
├─ YES → Use conventional testing (unit tests, assertions)
│
└─ NO → Is there a reference implementation?
    │
    ├─ YES → Use differential testing (conformance harness)
    │
    └─ NO → Do you know relationships between inputs/outputs?
        │
        ├─ YES → METAMORPHIC TESTING (this skill)
        │
        └─ NO → You need domain analysis first
```

**Examples of the oracle problem:**
- ML model: what's the "correct" sentiment of "The movie was not bad"?
- Search engine: what's the "correct" ranking of 10M documents?
- Compiler optimizer: does this optimization preserve semantics for ALL programs?
- Scientific simulation: is this fluid dynamics result correct to 6 decimal places?

---

## MR Taxonomy — The Six Fundamental Patterns

Every metamorphic relation falls into one of these categories. Master all six.

### 1. Equivalence (f(T(x)) = f(x))

The transformation shouldn't change the output at all.

```rust
/// Shuffling training data shouldn't change model accuracy
#[test]
fn mr_permutation_invariance() {
    proptest!(|(mut data: Vec<DataPoint>)| {
        let acc_original = model.train_and_evaluate(&data);
        data.shuffle(&mut thread_rng());
        let acc_shuffled = model.train_and_evaluate(&data);
        prop_assert!((acc_original - acc_shuffled).abs() < EPSILON,
            "Model accuracy changed by {:.4} after shuffling training data",
            (acc_original - acc_shuffled).abs());
    });
}
```

### 2. Additive (f(x + c) = f(x) + g(c))

Adding to input produces a predictable change in output.

```rust
/// Translating all points should translate the centroid
fn mr_centroid_translation(points: &[Point], offset: Vector) {
    let original_centroid = compute_centroid(points);
    let translated: Vec<Point> = points.iter()
        .map(|p| p + offset)
        .collect();
    let new_centroid = compute_centroid(&translated);
    assert_approx_eq!(new_centroid, original_centroid + offset);
}
```

### 3. Multiplicative (f(k·x) = h(k)·f(x))

Scaling input scales output by a related factor.

```rust
/// Doubling all prices should double total revenue
fn mr_revenue_linearity(transactions: &[Transaction], k: f64) {
    let original_revenue = compute_revenue(transactions);
    let scaled: Vec<Transaction> = transactions.iter()
        .map(|t| t.with_price(t.price * k))
        .collect();
    let scaled_revenue = compute_revenue(&scaled);
    assert_approx_eq!(scaled_revenue, original_revenue * k);
}
```

### 4. Permutative (f(permute(x)) = permute(f(x)))

Permuting input permutes output in a corresponding way.

```python
def test_mr_sort_commutes_with_map():
    """map(f, sort(xs)) == sort(map(f, xs)) for monotonic f"""
    @given(st.lists(st.integers()))
    def check(xs):
        f = lambda x: x * 2 + 1  # monotonically increasing
        assert list(map(f, sorted(xs))) == sorted(map(f, xs))
    check()
```

### 5. Inclusive/Exclusive (subset/superset/disjoint relations)

Restricting input restricts output; broadening input broadens output.

```rust
/// Adding a search term should return a SUBSET of results
fn mr_search_narrowing(engine: &SearchEngine, base_query: &str, extra_term: &str) {
    let broad_results = engine.search(base_query);
    let narrow_results = engine.search(&format!("{base_query} {extra_term}"));

    // Every result in narrow must also appear in broad
    for result in &narrow_results {
        assert!(broad_results.contains(result),
            "Narrowed search returned result not in broad search: {:?}", result);
    }
}

/// Complementary filters should be disjoint
fn mr_filter_disjoint(data: &[Record], predicate: &str) {
    let matching = filter(data, predicate);
    let non_matching = filter(data, &format!("NOT ({predicate})"));

    let intersection: Vec<_> = matching.iter()
        .filter(|r| non_matching.contains(r))
        .collect();
    assert!(intersection.is_empty(),
        "Complementary filters share {} records", intersection.len());
}
```

### 6. Invertive (f(T(T(x))) = f(x))

Applying the transformation twice returns to the original.

```rust
/// Encrypt then decrypt recovers plaintext
fn mr_crypto_roundtrip(plaintext: &[u8], key: &Key) {
    let ciphertext = encrypt(plaintext, key);
    let recovered = decrypt(&ciphertext, key);
    assert_eq!(plaintext, &recovered[..],
        "Encrypt-decrypt roundtrip failed");
}
```

---

## Composition: Multiplying MR Power

Simple MRs compose into exponentially stronger checks.

```rust
/// Composite MR: permutation invariance + scaling linearity + translation covariance
fn mr_composite_regression(model: &LinearModel, data: &Dataset) {
    let prediction_original = model.predict(data);

    // MR1: Permutation invariance (reorder samples)
    let shuffled = data.shuffled();
    let prediction_shuffled = model.predict(&shuffled);
    assert_approx_eq!(prediction_shuffled.mean(), prediction_original.mean());

    // MR2: Scaling linearity (scale features by k → scale predictions)
    let k = 2.0;
    let scaled = data.scale_features(k);
    let prediction_scaled = model.predict(&scaled);
    assert_approx_eq!(prediction_scaled.mean(), prediction_original.mean() * k);

    // MR3 = MR1 ∘ MR2: shuffle THEN scale (compound property)
    let shuffled_scaled = shuffled.scale_features(k);
    let prediction_compound = model.predict(&shuffled_scaled);
    assert_approx_eq!(prediction_compound.mean(), prediction_original.mean() * k);
}
```

**Composition rule:** If MR₁ and MR₂ are valid, then MR₁∘MR₂ is valid.
Compound MRs catch bugs that no individual MR detects.

---

## Domain-Specific MR Catalogs

### Database Engines (SQLancer-style)

```rust
/// TLP: Ternary Logic Partitioning
/// WHERE P ∪ WHERE NOT P ∪ WHERE P IS NULL = all rows
fn mr_tlp(db: &Database, table: &str, predicate: &str) {
    let all = db.query(&format!("SELECT * FROM {table}"));
    let t = db.query(&format!("SELECT * FROM {table} WHERE {predicate}"));
    let f = db.query(&format!("SELECT * FROM {table} WHERE NOT ({predicate})"));
    let n = db.query(&format!("SELECT * FROM {table} WHERE ({predicate}) IS NULL"));
    assert_eq!(all.len(), t.len() + f.len() + n.len());
}

/// NoREC: Non-optimizing Reference Engine Check
/// Unoptimized query result == optimized query result
fn mr_norec(db: &Database, query: &str) {
    let optimized = db.query(query);
    let unoptimized = db.query_no_optimize(query);
    assert_eq!(optimized, unoptimized);
}

/// PQS: Pivoted Query Synthesis
/// Insert row → query that matches row → must find it
fn mr_pqs(db: &Database, table: &str, row: &Row) {
    db.insert(table, row);
    let predicate = row.to_exact_match_predicate();
    let results = db.query(&format!("SELECT * FROM {table} WHERE {predicate}"));
    assert!(results.contains(row), "Inserted row not found by exact-match query");
}
```

### ML/AI Models

| MR | Property | Detects |
|----|----------|---------|
| Synonym substitution | f("great movie") ≈ f("excellent movie") | Fragile embeddings |
| Negation flip | sign(f("good")) ≠ sign(f("not good")) | Negation blindness |
| Irrelevant addition | f(x) ≈ f(x + " The sky is blue.") | Attention leaks |
| Paraphrase | f(x) ≈ f(paraphrase(x)) | Surface-form sensitivity |
| Label permutation | accuracy(shuffled_labels) ≈ chance | Memorization detection |

### Compilers/Interpreters

| MR | Transformation | Must Preserve |
|----|---------------|---------------|
| Dead code insertion | Add unreachable code | Output unchanged |
| Constant folding | Replace `2+3` with `5` | Semantics |
| Variable renaming | `x` → `y` everywhere | Behavior |
| Optimization toggle | `-O0` vs `-O2` | Observable output |
| Equivalent rewrites | `a*(b+c)` → `a*b+a*c` | Result |

---

## The Elicitation Prompt

When you're stuck finding MRs for an unfamiliar domain:

```text
I have a [SYSTEM TYPE] that takes [INPUT TYPE] and produces [OUTPUT TYPE].
I cannot compute expected outputs for arbitrary inputs (oracle problem).

List ALL metamorphic relations that MUST hold for a correct implementation.
For each MR:
1. Category (equivalence/additive/multiplicative/permutative/inclusive/invertive)
2. The exact transformation T(x)
3. The exact relation R between f(x) and f(T(x))
4. What bug class it catches
5. Confidence: would violation ALWAYS indicate a bug, or only sometimes?

Prioritize by fault sensitivity × independence. I want a DIVERSE set that
covers different aspects of correctness, not 10 variations of the same property.
```

---

## Validation: Does Your MR Suite Actually Work?

### Mutation Testing for MRs

Plant known bugs and verify your MRs catch them:

```rust
#[test]
fn validate_mr_suite_catches_planted_bugs() {
    let mutations = vec![
        ("off-by-one", |x: i32| x + 1),
        ("sign-flip", |x: i32| -x),
        ("zero-out", |_: i32| 0),
        ("double", |x: i32| x * 2),
    ];

    for (name, mutant) in &mutations {
        let caught = mr_suite_detects_mutation(mutant);
        assert!(caught,
            "MR suite failed to detect '{}' mutation — add a stronger MR", name);
    }
}
```

**Target:** Each planted mutation caught by ≥ 1 MR. If not, your suite has blind spots.

---

## Anti-Patterns (Hard Constraints)

| ✗ Never | Why | Fix |
|---------|-----|-----|
| Test f(x) = f(x) | Tautology, catches nothing | Need a TRANSFORMATION |
| Derive MRs from the code | Won't catch bugs in the logic you're testing | Derive from the SPEC or domain |
| Only test with edge cases | Metamorphic power comes from diverse random inputs | Use property-based generation |
| Skip floating-point epsilon | False failures kill trust in suite | `assert_approx_eq!` everywhere |
| Implement 10 correlated MRs | Same bug class detected 10x, others missed | Score for independence |
| No mutation validation | You don't know if MRs actually catch bugs | Plant bugs, verify detection |

---

## Checklist (Before Shipping MR Suite)

- [ ] Oracle problem confirmed (can't compute expected output)
- [ ] ≥ 5 independent MRs from ≥ 3 different categories
- [ ] Strength matrix scored: all implemented MRs have Score ≥ 2.0
- [ ] Property-based input generation (not handcrafted examples only)
- [ ] Mutation testing validates MR suite catches planted bugs
- [ ] Floating-point comparisons use epsilon tolerance
- [ ] Composite MRs tested (at least one chain of 2+ simple MRs)
- [ ] Each MR named after the property it verifies (not `test_mr_1`)

---

## References

| Need | Reference |
|------|-----------|
| MR catalog by domain | [MR-CATALOG.md](references/MR-CATALOG.md) |
| Composition patterns | [COMPOSITION.md](references/COMPOSITION.md) |
| Framework integration | [FRAMEWORKS.md](references/FRAMEWORKS.md) |
| Anti-patterns & mistakes | [ANTI-PATTERNS.md](references/ANTI-PATTERNS.md) |
| Copy-paste templates & decision matrix | [QUICK-REFERENCE.md](references/QUICK-REFERENCE.md) |
| Research foundations | [RESEARCH.md](references/RESEARCH.md) |

## Relationship to Other Testing Skills

| Technique | Use INSTEAD when | Use TOGETHER with MT when |
|-----------|-----------------|--------------------------|
| /testing-fuzzing | Finding crashes in parsers | Fuzzing generates source inputs, MRs validate relations |
| /testing-conformance-harnesses | Reference implementation exists | MRs supplement conformance where spec is ambiguous |
| /extreme-software-optimization | Performance, not correctness | MRs verify optimization doesn't change behavior |
| /alien-artifact-coding | Need formal proofs | MRs are the practical bridge between testing and proofs |

Attribution

lev-oslev-os
View sourceMore from lev-os →
SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments (0)

No comments yet. Be the first to comment!

SSkills DirectorySkills Directory

Your tool, in front of Claude Code builders.

3 founder slots · $299/mo · GSC-verified traffic · sponsors can never buy grades.

See placements

Related Skills

Screen Reader Testing

Practical guide to testing web applications with screen readers for comprehensive accessibility validation.

393431 votes

Python Testing

使用pytest、TDD方法、夹具、模拟、参数化和覆盖率要求的Python测试策略。

2456590 votes

Tdd Workflow

在编写新功能、修复错误或重构代码时使用此技能。强制执行测试驱动开发,包含单元测试、集成测试和端到端测试,覆盖率超过80%。

2456590 votes

Springboot Tdd

使用JUnit 5、Mockito、MockMvc、Testcontainers和JaCoCo进行Spring Boot的测试驱动开发。适用于添加功能、修复错误或重构时。

2456590 votes

Eval Harness

克劳德代码会话的正式评估框架,实施评估驱动开发(EDD)原则

2456590 votes
View all in testing →