GithubHelp home page GithubHelp logo

pdibenedetto / testing-without-mocks-example Goto Github PK

View Code? Open in Web Editor NEW

This project forked from jamesshore/testing-without-mocks-example

0.0 1.0 0.0 8.32 MB

A demonstration of James Shore's "Testing Without Mocks" pattern language.

License: Other

Shell 1.25% JavaScript 97.68% Batchfile 1.08%

testing-without-mocks-example's Introduction

Testing Without Mocks, Simple Example

This is a simple example of the ideas in James Shore's Testing Without Mocks pattern language.

About the Program

The program is a ROT-13 command-line tool. To use, run ./run.sh "text to convert" (Mac/Linux) or run "text to convert" (Windows). The ROT-13 output will be displayed on the command-line. For example:

$ ./run.sh "Hello World"
Uryyb Jbeyq

About the Source Code

The code is organized according to A-Frame Architecture, which means it has a top-level Application/UI layer which is responsible for the command-line interface. It delegates to an Infrastructure layer to handle command-line arguments and output, and to a Logic layer to handle ROT-13 encoding. The Infrastructure and Logic layers are unaware of each other.

The code is all in the src/ tree. Other directories are part of the build system and can be ignored.

Application layer code

  • run.js - Application entry point; no meaningful code
  • app.js - App class. Reads command-line arguments and writes output.
  • _app_test.js - Tests for App.

Infrastructure layer code

  • infrastructure/command_line.js - CommandLine class. Infrastructure wrapper for reading command-line arguments and writing to stdout.
  • infrastructure/output_tracker.js - OutputTracker class. Generic helper class used to track CommandLine's output.
  • infrastructure/_command_line_test.js - Tests for CommandLine.
  • infrastructure/_output_tracker_test.js - Tests for OutputTracker.
  • infrastructure/_command_line_test_args_runner.js - Runs in a separate process. Used to test CommandLine's ability to read a process's command-line arguments.
  • infrastructure/_command_line_test_nulled_output_runner.js - Runs in a separate process. Used to test CommandLine's ability to turn off writes to stdout.
  • infrastructure/_command_line_test_output_runner.js - Runs in a separate process. Used to test CommandLine's ability to write to stdout.

Logic layer code

  • logic/rot13.js - ROT-13 encoding logic.
  • logic/_rot13_test.js - Tests for ROT-13 logic.

About the Patterns

The purpose of this repository is to demonstrate the Testing Without Mocks patterns. Here are each of the patterns in the article and how they're used in this code:

Foundational Patterns

All tests are “narrow tests,” which means they’re focused on a specific class, module, or concept. Specifically:

  • _app_test.js tests the App class.
  • _command_line_test.js tests the CommandLine class.
  • _rot13_test.js tests the rot13 module.

All tests are “state-based tests,” which means they make assertions about the return values or state of the unit under test, rather than making assertions about which methods it calls. Specifically:

  • _app_test.js makes assertions about how App changes the state of the CommandLine, given various command-line arguments.
  • _command_line_test.js makes assertions about how CommandLine reads command-line arguments and writes to stdout.
  • _rot13_test.js makes assertions about what the transform() function returns, given various inputs.

All tests are “sociable tests,” which means the code under test isn’t isolated from the rest of the application. Specifically:

  • _app_test.js runs real code in CommandLine and rot13, which are App's dependencies.

There are no broad integration tests (end-to-end tests), but _app_test.js and _command_line_test.js overlap to provide the same safety net that broad tests do. The one gap is run.js, which could be covered by a smoke test. (But it's so simple it's hard to imagine it breaking.)

In the interest of clarity, this code doesn't have any smoke tests.

The program has two classes, App and CommandLine, and neither of them do any significant work in their constructor.

Every class can be instantiated without providing any parameters.

The _app_test.js and _command_line_tests.js tests both have helper methods that set up the test parameters and return multiple results.

(The _rot13_test.js tests don't use Signature Shielding because the function under test is so straightforward.)

The first _app_test.js test checks App's "happy path" execution, which involves running the ROT-13 encoding function. To prevent changes to the ROT-13 algorithm from breaking that test in the future, the test uses Collaborator-Based Isolation. Specifically, it sets up its expectation by calling rot13.transform().

Architectural Patterns

The code is arranged in a simple A-Frame architecture. App is in the Application/UI layer, CommandLine is in the Infrastructure layer, and rot13 is in the Logic layer.

App is implemented with a simple Logic Sandwich that uses the Infrastructure layer to read the input from the command-line arguments, then calls the Logic layer to encode the input, then writes the encoded value to stdout.

This program doesn’t receive any events from the outside world, so it doesn't need a Traffic Cop.

The code was built evolutionarily. You can get a sense of how it evolved by looking at the commit history.

Logic Patterns

The rot13 encoding function, transform(), is a pure function.

This program doesn’t use any third-party logic libraries.

Infrastructure Patterns

The CommandLine class is a wrapper for process.args and process.stdout.

The Infrastructure Layer tests in _command_line_test.js check that command_line.js can read real command-line arguments and write to the real stdout. They do this by spawning separate processes, which allows the test to control the processes' command-line arguments and observe their output.

This program doesn't call any third-party systems, so it doesn't need Paranoic Telemetry.

Nullability Patterns

Calling CommandLine.createNull() creates a Null version of CommandLine that operates just like the real thing, except it doesn't actually read or write to the command line. This is used by the Application Layer tests in _app_test.js.

CommandLine.createNull() is implemented with an embedded stub of process.

The code is written in JavaScript, so Thin Wrappers aren't needed.

Calling CommandLine.createNull([ "my_response" ]) will cause it to say that the program's command-line argument is "my_response". This is used by the Application Layer tests in _app_test.js.

CommandLine.trackOutput() allows the command-line output to be tracked. The tracking is implemented by OutputTracker.

This code doesn’t respond to events from external systems, so Behavior Simulation isn't needed.

This code is too simple to have long dependency chains, so Fake It Once You Make It isn't needed.

Legacy Code Patterns

The code was a green-field project, so the legacy code patterns weren't needed.

(end)

testing-without-mocks-example's People

Contributors

jamesshore avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.