Skip to main content

Type-safe YAML-based example specification driven development framework for python.

Project description

HitchStory

Main branch status

Type-safe StrictYAML integration tests run from pytest. They can:

Rewrite themselves from program output (command line app)

Test rewriting itself

Autogenerate documentation about the app (website)

Test writing docs

Demo projects with integration tests

Project Storytests Engine / runner code Doc template Autogenerated docs
Website add todo, correct spelling test_integration.py docstory.yml Add todo, Correct my spelling
REST API add todo, correct spelling test_integration.py docstory.yml Add todo, Correct my spelling
Interactive command line app add todo, correct spelling test_integration.py docstory.yml Add todo, Correct my spelling
A Python API add todo, correct spelling engine.py docstory.yml Add todo, Correct my spelling

Code Example

example.story:

Log in as James:
  given:
    browser: firefox  # preconditions
  steps:
  - Enter text:
      username: james
      password: password
  - Click: log in
  
See James analytics:
  based on: log in as james  # inheritance
  following steps:
  - Click: analytics

test_hitchstory.py:

from hitchstory import BaseEngine, GivenDefinition, GivenProperty
from hitchstory import Failure, strings_match
from hitchstory import StoryCollection
from strictyaml import Str
from pathlib import Path
from os import getenv

class Engine(BaseEngine):
    """Interprets and validates the hitchstory stories."""

    given_definition = GivenDefinition(
        browser=GivenProperty(
            # Available validators: https://hitchdev.com/strictyaml/using/
            Str()
        ),
    )
    
    def __init__(self, rewrite=False):
        self._rewrite = rewrite

    def set_up(self):
        print(f"Using browser {self.given['browser']}")

    def click(self, name):
        print(f"Click on {name}")
        
        if name == "analytics":
            raise Failure(f"button {name} not found")
    
    def enter_text(self, **textboxes):
        for name, text in textboxes.items():
            print(f"Enter {text} in {name}")
    
    def tear_down(self):
        pass


collection = StoryCollection(
    # All .story files in this file's directory.
    Path(__file__).parent.glob("*.story"),

    Engine(
        # If REWRITE environment variable is set to yes -> rewrite mode.
        rewrite=getenv("REWRITE", "no") == "yes"
    )
)

#Manually run stories within pytest tests
#def test_log_in_as_james():
#    collection.named("Log in as james").play()

#def test_see_james_analytics():
#    collection.named("See James analytics").play()

# Automagically add all stories as tests.
# E.g. "Log in as James" -> "def test_login_in_as_james"
collection.with_external_test_runner().ordered_by_name().add_pytests_to(
    module=__import__(__name__) # This module
)

Run log in as James test

test_log_in_as_james is created from the "Log in as James" story.

pytest -s test_hitchstory.py -k test_log_in_as_james

Outputs:

============================= test session starts ==============================
platform linux -- Python n.n.n, pytest-n.n.n, pluggy-n.n.n
rootdir: /path/to
collected 2 items / 1 deselected / 1 selected

test_hitchstory.py Using browser firefox
Enter james in username
Enter password in password
Click on log in
.

======================= 1 passed, 1 deselected in 0.1s ========================

Run failing test

Failing tests also have colors and highlighting when run for real.

pytest -k test_see_james_analytics test_hitchstory.py

Outputs:

============================= test session starts ==============================
platform linux -- Python n.n.n, pytest-n.n.n, pluggy-n.n.n
rootdir: /path/to
collected 2 items / 1 deselected / 1 selected

test_hitchstory.py F                                                     [100%]

=================================== FAILURES ===================================
___________________________ test_see_james_analytics ___________________________

story = Story('see-james-analytics')

    def hitchstory(story=story):
>       story.play()
E       hitchstory.exceptions.StoryFailure: RUNNING See James analytics in /path/to/example.story ... FAILED in 0.1 seconds.
E
E             based on: log in as james  # inheritance
E             following steps:
E             - Click: analytics
E
E
E       hitchstory.exceptions.Failure
E
E           Test failed.
E
E       button analytics not found

/src/hitchstory/story_list.py:50: StoryFailure
----------------------------- Captured stdout call -----------------------------
Using browser firefox
Enter james in username
Enter password in password
Click on log in
Click on analytics
=========================== short test summary info ============================
FAILED test_hitchstory.py::test_see_james_analytics - hitchstory.exceptions.StoryFailure: RUNNING See James analytics in /path/to/example.story ... FAILED in 0.1 seconds.

      based on: log in as james  # inheritance
      following steps:
      - Click: analytics


hitchstory.exceptions.Failure

    Test failed.

button analytics not found
======================= 1 failed, 1 deselected in 0.1s ========================

Install

$ pip install hitchstory

Using HitchStory

Every feature of this library is documented and listed below. It is tested and documented with itself.

Using HitchStory: With Pytest

If you already have pytest set up and running integration tests, you can use it with hitchstory:

Using HitchStory: Engine

How to use the different features of the story engine:

Using HitchStory: Documentation Generation

How to autogenerate documentation from your tests:

Using HitchStory: Inheritance

Inheriting stories from each other:

Using HitchStory: Runner

Running the stories in different ways:

Approach to using HitchStory

Best practices, how the tool was meant to be used, etc.

Design decisions and principles

Design decisions are justified here:

Why not X instead?

HitchStory is not the only integration testing framework. This is how it compares with the others:

Using HitchStory: Setup on its own

If you want to use HitchStory without pytest:

Using HitchStory: Behavior

Miscellaneous docs about behavior of the framework:

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

hitchstory-0.22.0.tar.gz (32.8 kB view hashes)

Uploaded Source

Built Distribution

hitchstory-0.22.0-py3-none-any.whl (30.2 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page