Skip to content

Testing Semantics

Cyril Kato edited this page Nov 5, 2026 · 2 revisions

This page explores the semantic distinctions between tests and specifications, explaining the unique approach taken by Fix compared to traditional testing frameworks.

The Testing Landscape

In software development, the terms "testing" and "specifying" are often used interchangeably. This semantic confusion, while seemingly harmless, reflects a conceptual fusion in our development practices that Fix aims to address.

Traditional Approach: Merging Concepts

Traditional testing frameworks typically merge two fundamentally distinct aspects:

  1. Defining expected behaviors (specifications)
  2. Verifying code compliance with these expectations (tests)

Consider this RSpec example:

RSpec.describe Calculator do
  subject(:calculator) do
    described_class.new(first_number)
  end

  describe "when first number is 2" do
    let(:first_number) do
      2
    end

    context "#add" do
      let(:second_number) do
        3
      end

      it "adds two numbers" do
        expect(calculator.add(second_number)).to be(5)
      end
    end
  end
end

Here, the specification is tightly coupled with its implementation. The test code combines:

  • Object structure
  • Test data
  • Expected behaviors
  • Verification logic

The Fix Approach: Separation of Concerns

Fix proposes a different paradigm based on a clear separation between specification and testing:

1. Writing Specifications

Fix :Calculator do
  on(:new, 2) do
    on(:add, 3) do
      it MUST be 5
    end
  end
end

2. Running Tests

Fix[:Calculator].test { Calculator }

Key Benefits

Semantic Clarity

Fix specifications focus solely on describing expected behavior, without implementation details. They define operation sequences and expected results in a pure, declarative way.

Reusability

Specifications become reusable behavioral contracts. The same specification can validate different Calculator implementations as long as they respect the defined interface.

Isolated Testing

Test execution is completely decoupled from specification definition. A simple Fix[:Calculator].test { Calculator } is sufficient to validate any implementation against the specifications.

Impact on Development Practices

This architectural distinction profoundly influences development approaches:

  • Pure Domain Language: Specifications become a pure domain language, describing expected behaviors without technical pollution
  • Simplified Testing: Tests are reduced to their essence: confronting an implementation against its specifications
  • Better Design: The decoupling encourages thorough reflection on expected behaviors before implementation

See Also

  • Design Decisions — how this separation shaped Fix 1: a specification is data, built once; match? follows the contract of Matchi
  • Architecture — the gems that write, judge and run a specification
  • Getting Started

References

Clone this wiki locally