• Stars
    star
    130
  • Rank 277,575 (Top 6 %)
  • Language
    Ruby
  • License
    Mozilla Public Li...
  • Created over 4 years ago
  • Updated almost 2 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Reflective testing.

Reflekt

Gem Version GitHub code size in bytes

Reflective testing.

Traditional testing is fine but it's not perfect. Tests often check for a golden path that works, when errors actually happen when the code or user does something unexpected. And with automated testing humans still have to write the tests.

Reflekt writes the tests for you, and tests in the negative for situations that you wouldn't have noticed. It works out of the box with no extra coding required. Because Reflekt tests your objects as they are used in the normal flow of the application, you get real world test results.

Usage

Add prepend Reflekt inside a class:

require 'reflekt'

class Example
  prepend Reflekt

Use the application as usual and test results will start showing up in the reflections folder:

Or enable Lit (included with Reflekt) to see reflections via the command line:

Installation

In Gemfile add:

gem "reflekt"

In terminal run:

bundle install

Or:

gem install reflekt

Configuration

Reflekt.configure do |config|
  # Reflekt is enabled by default and should be disabled on production.
  config.enabled = true

  # The amount of reflections to create per method call.
  config.reflect_amount = 5
end

See Config.rb for more configuration options.

Skipping actions

You can configure Reflekt to skip "no undo" methods like deletion and sending email:

class Example
  reflekt_skip :method_name

Use reflekt_skip on the method that saves to the database to avoid persisting test data. If you still want to save test data to the database then use dependency injection to connect to a dummy database.

Skipping render

Use reflekt_skip on methods that do the final render to the UI to avoid a visual mess of duplicated elements.

❌ Don't do:

def show(product)
  # Business logic.
  product.title = "Showing #{product.name}"

  # Rendering logic.
  puts product
end

βœ… Do:

reflekt_skip :render

# Business logic.
def show(product)
  product.title = "Showing #{product.name}"  
  render(product)
end

# Rendering logic.
def render(product)
  puts product
end

How it works

When a method is called in the usual flow of an application, Reflekt runs multiple simulations with different values on that method to see if it can break things, before handing back control to the method to perform its usual task.

Control-Experiment loop

Reflekt builds itself as you build your application.

Terminology:

  • Control - A shapshot of real data (a subclass of Reflection)
  • Experiment - A shapshot of random data (a subclass of Reflection)

The loop:

  1. You write code and run it
  2. A Control reflection is created per method call, tracking input and output
  3. A set of rules are created from each Control on how the program works
  4. Many Experiment reflections are created per method call, containing random input and output
  5. Each Experiment is tested to pass or fail the set of rules previously defined by each Control
  6. Results are saved to the /reflections directory
  7. Your application returns its usual output

This feedback loop creates better results the more you develop and use your application.

Comparison

Conceptual differences between testing methodologies:

Traditional testing Generative testing Reflective testing
Automation ❌ Defined manually ❎ Defined semi-automatically βœ… Defined automatically
Granularity βœ… Tests PASS or FAIL βœ… Tests PASS or FAIL βœ… Tests PASS or FAIL
Replication ❌ Tests run externally ❌ Tests run externally βœ… Tests run internally
Feedback ❌ Tests run periodically ❌ Tests run periodically βœ… Tests run in real time

Tests run internally
Tests run alongside the normal execution of the program using the exact same code. There is no duplicate code in different files/environments calling the same APIs.

Tests run in real time
Multiple simulations run as you develop and use your application.

Consider this logic:

  1. Tests often check that things work (in the positive)
  2. Errors happen when things break (in the negative)
  3. Tests should check more often for the negative
  4. This can be automated

Philosophy

To keep coding fun. There are too many layers these days. It doesn't just make coding less fun, it makes it harder for new programmers to learn programming.

To stop duplicate code. RIP DRY. You use to hear β€œDon’t Repeat Yourself” a lot but not anymore. Traditional tests are just duplicate code, we don't need them.

To test code better. By completly automating testing we get 100% coverage and test for situations we never thought possible.