PHPackages                             jimdelois/context-specification - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. jimdelois/context-specification

ActiveLibrary[Testing &amp; Quality](/categories/testing)

jimdelois/context-specification
===============================

A library for PHPUnit to provide support for the BDD ContextSpecification-style testing pattern

0.0.2(11y ago)036BSD-3-ClausePHPPHP &gt;=5.3.0

Since Mar 15Pushed 11y agoCompare

[ Source](https://github.com/jimdelois/context-specification)[ Packagist](https://packagist.org/packages/jimdelois/context-specification)[ Docs](https://github.com/jimdelois/context-specification/)[ RSS](/packages/jimdelois-context-specification/feed)WikiDiscussions master Synced yesterday

READMEChangelog (2)Dependencies (3)Versions (3)Used By (0)

[![Build Status](https://camo.githubusercontent.com/6a5dac2e406385d37219837f140dd76e69015f3bed57d2d2208fa1f5b65d2734/687474703a2f2f6275696c642e6a6f7669616e736f6674776172652e636f6d2f6a6f622f436f6e7465787453706563696669636174696f6e2f62616467652f69636f6e)](http://build.joviansoftware.com/job/ContextSpecification/)

Context Specification Testing Framework
=======================================

[](#context-specification-testing-framework)

The Context Specification (CS) library is a PHPUnit wrapper intended to promote the use of a CS-style testing pattern. This approach offers the Test-Driven Developer a working syntax which more-closely aligns with how behavior is described in business terms, giving rise to the increasingly popular "Behavior-Driven Design (BDD)" paradigm.

When compared to "traditional" unit testing, Context Specification -style BDD seems negligibly different, if at all. While at first the only deviation appears to be in syntactic semantics, the subtle arrangement of the "different" methods plays a huge part in how functionality is logically organized... and tested.

Namely, this distinction is realized when describing and considering functionality as broken into three parts... (1) The starting point; an initial state; a "Context" in which a particular action is to be taken. (2) The action itself; the "state transition" from the initial state into a new one. (3) The new, final, or resultant state after the state transition is complete.

By testing only one single transitional event or method at a time, and then verifying that everything about the new state is sufficiently expected, in conjunction with having correctly provided an initial state, it can then be assumed that the state transition, itself, is operating correctly. Since a computer program operates by endlessly transitioning from one state to another, we can ensure that a library is operating correctly by testing each transition individually, and from all known initial states. Such a consideration of behavior/functionality will likely lead to greater code coverage, fewer bugs and, when requirements can be expressed in a similar fashion, a more accurately-modeled system.

It's true that the same type of BDD can be practiced with the similar granularity and behavioral coverage without any such wrapper. The hope, however, is that providing a lightweight library and suggesting that a few rules be followed, a developer's tests will read more clearly while also providing a more natural report of test failures (and successes!).

Lastly, there are several approaches that could tighten up the BDD paradigm substantially, similar to some other frameworks in other languages. However, this library is developed specifically for PHPUnit, with tooling in mind. That is to say, this library should work correctly in all extensions of PHPUnit (so long as they aren't explicitly removing default behavior), regardless of any customized TestRunners, Commands, etc, put in place by either a developer or an IDE.

Installation
------------

[](#installation)

It is recommended that the Context Specification library be installed via the Composer dependency management tool. Otherwise, the latest compressed file may be downloaded from Github at any time, and any PSR-4 autoloaders will work to include the files therein.

### Composer Installation

[](#composer-installation)

To your `composer.json` file, add the following:

```
"require" : {
	"jimdelois/context-specification": "0.1.*@dev" ,
}

```

Once you've run `composer install` or `composer update`, you should have the right libraries available to you within the `{vendordir}/jimdelois/context-specification/src/ContextSpecification/Framework/` directory.

Basic Usage
-----------

[](#basic-usage)

Depending on the need, simply extend the `Concern` or `StaticConcern` classes and begin testing as normal!

If global or isolated functionality is being tested where no state can be injected, inserted, or tracked, then the `StaticConcern` will be just enough of a harness for this. In either case, it is up to the developer to provide an implementation for the following two abstract methods:

```
abstract protected function context( );
abstract protected function because( );

```

The `context` method is where the starting point, or initial state is established. The `because`method will be called next, where the SUT or isolated functionality will have its one and only tested state change executed.

If there is a System Under Test (SUT) which requires some initial state, has dependencies which require state prior to injection, or has a resultant internal state that must be checked, then the developer should extend the `Concern` class and instantiate this system from within:

```
protected function createSUT( );

```

By returning the object or system under test from this method, it will automatically be available within the Concern class as `$this->sut`. It ought be on this object that a Context is set specific to the test, state changes are invoked (in the `because` method), and assertions are made in observations.

Such observations, whether or not there is a SUT, can be made in standard PHPUnit "test" methods. Any method prefixed with "test" will work, although it is a general recommendation to use the `@test` annotation built into PHPUnit and then name the observation methods in accordance with traditional CS language, such as `should`, etc. This will have benefits of a better report, e.g., a `--testdox` report will read like a regular CS report.

E.g., consider `MyTest.php` as:

```

```

Then, most reports will read like a traditional CS one (note the verbosity!):

```
$ ../vendor/bin/phpunit --testdox MyTest.php
PHPUnit 3.7.33 by Sebastian Bergmann.

When_attempting_to_do_something_awesome_given_a_specific_context
[x] should_really_be_awesome
[x] then_it_should_not_be_boring

```

Additionally, the method `decontext` is available for optionally "undoing" any of the context establishment. This method is called after each observation, balancing the calls to `context`. Note that reliance on this method to pass tests for true "units" of functionality is likely the result of a poor test approach or, worse, a bad design (globals?!). Depending on the code we're testing, we may not be able to get away from this, so this function provides an opportunity to "reset" anything between observations. In "integration" testing (not "unit"), the need to do this is more commonplace and arguably much more "acceptable".

Finally, the Context Specification library offers a small amount of extra functionality that is useful in dealing with Exceptions raised from state changes. Just as one should test for all possible positive-scenario contexts, a developer should also test for contexts in which state might be invalid... Typically the target functionality (in the SUT) will be coded to raise an exception in this case. If we provided an invalid input directly into our `because` method's state change:

```
protected function because( ) {
	$this->sut->setInteger( 'This is a string but the method expects an INT!' );
}

```

... The actual invocation of `because` would cause the Exception to be raised before we're prepared, and PHPUnit would fail immediately. The correct solution is NOT to leave the `because` method blank and move the state change to the observation! Doing so brings us further back to "standard" TDD practice, wherein the state change is invoked within the same method that asserts - this is precisely what we're trying to avoid. Rather, consider capturing the Exception from within the `because`method and then evaluating it later. Alternatively, try wrapping the state change in a lambda function and then invoke it later. While the latter approach is only a semantic difference from the "discouraged" one, it's an important distinction which helps to keep our tests consistent and well-organized.

As testing invalid states and ensuring correct Exceptions are raised is a very common occurrence (or should be), the Context Specification framework does the favor of minimizing the amount of work the testing developer has to do to deal with these nuances. By simply calling `$this->becauseWillThrowException( );`, the Concern will be configured to automatically wrap the `because` method contents in a lambda, meaning one can define the state change as per usual. Then, two additional methods become available which can be used from within observations to validate and assert the resultant Exceptions: `releaseException` and `captureException`.

Examples
--------

[](#examples)

Below are a couple of (silly) examples of how we might test a single method on a service using the Context Specification library.

```

```

Note that the framework allowed us to keep a nice, clean `because` method regardless of the fact that invocation would cause a test-failing error.

Alternatively, one could call `captureException` which would avoid throwing it and then make it available from within `$this->exception` so that it may be inspected and used in assertions. It's also possible that one might want to make assertions on the state of `$this->sut` after having thrown the exception from within.

Ideally, correct implementation will then yield the following when the entire suite is run:

```
$ ../vendor/bin/phpunit --testdox
PHPUnit 3.7.33 by Sebastian Bergmann.

When_loading_first_awesomeness_from_service_for_date
[x] should_call_appropriate_method_on_awesomeness_dao
[x] should_return_correct_awesomeness_object

When_loading_first_awesomeness_from_service_for_non_date_input
[x] should_raise_invalid_argument_exception

```

There are some obvious flaws to these two trivial examples, and they are already screaming for a shared parent base Concern. However, the point of intended usage here is arguably more important than proper test design or architecture.

Issues
------

[](#issues)

- It would be ideal to extend the base PHPUnit Framework with configurable "Test Method Prefixes", just as they currently allow for the adjustment of "Test Suffixes" at the file level. By opening up configurability for method prefixes, we could get away from having to annotate any "specially named CS test methods" using the `@test`. Extending the PHPUnit\_Framework\_TestSuite to flag such methods is trivial, and a full swap of this suite for the base is possible with a set of custom Runners and/or Commands - but for the same reason this is a approach that works for introducing additional functionality into PHPUnit, it's a common approach taken by other developers and IDEs... Meaning that if this particular library insisted on *also* using custom Runners and Commands, it will almost certainly not work in other IDEs or with other extensions of PHPUnit.

    Until such a configuration is accepted into the core PHPUnit framework and eventually propagates to other tooling, we are likely stuck with having to use `test*` for the test methods or, as recommended, annotate them using `@test`.
- PHPUnit's `setUp()` method is an instance method run before each and every test method or, in this case, "observation." However, `setUpBeforeClass()` is a *static* method that runs once per class. In theory, a Context within a Context Specification test case should only be set up once per class (this is arguably one of the largest differences between traditional testing and CS testing - state changes are so clearly separated from assertions and context establishment that there's really no need to ever tearDown and setUp for each observation). This approach is common in other CS frameworks, but the difference is that their base frameworks' "once-per-class" equivalents aren't static methods.

    This is not to suggest that it cannot be done within this library, but it's non necessarily trivial to properly set our Concern up from within the statics. The same goes for `tearDown` vs. `tearDownAfterClass`. With that, it's important to realized that **a context will be established and a context will be de-contexted before and after every single observation method.** What this actually means from a practical standpoint is that there may be performance implications for any deep integration tests that contain multiple observations and complex or latent set ups. For this reason (among several others), it's often wise to logically separate all "unit" suites from "integration" suites.

Next Steps
----------

[](#next-steps)

- It would be ideal to add additional, class-level annotations such as `@concern {Concern name here}`, which would allow the tester to group multiple concerns, for the sake of reporting, *across several distinct files*. Of course, a custom reporter would have to be used to accommodate this, although it's distinctly possible that a listener could sit in the middle and perform some trickery...

    Similarly, grouping at the next-level down could be achieved with some type of `@when` annotation, as well.
- Look into addressing the above "issue" concerning once-per-observation establishment of context versus once-per-concern.

Questions/Feedback
------------------

[](#questionsfeedback)

Jim DeLois - [%%PHPDOC\_AUTHOR\_EMAIL%%](mailto:%%PHPDOC_AUTHOR_EMAIL%%)

###  Health Score

23

—

LowBetter than 27% of packages

Maintenance20

Infrequent updates — may be unmaintained

Popularity7

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity49

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

2

Last Release

4076d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7c588349ae21df6915ac10b1931eafc8fd8f5e65e56e18658de82085ff2c876e?d=identicon)[jimdelois](/maintainers/jimdelois)

---

Top Contributors

[![jimdelois](https://avatars.githubusercontent.com/u/1907498?v=4)](https://github.com/jimdelois "jimdelois (22 commits)")

---

Tags

testingphpunitspecificationxunitContextcontextspec

### Embed Badge

![Health badge](/badges/jimdelois-context-specification/health.svg)

```
[![Health](https://phpackages.com/badges/jimdelois-context-specification/health.svg)](https://phpackages.com/packages/jimdelois-context-specification)
```

###  Alternatives

[phpunit/phpunit

The PHP Unit Testing framework.

20.0k910.7M134.8k](/packages/phpunit-phpunit)[brianium/paratest

Parallel testing for PHP

2.5k118.8M754](/packages/brianium-paratest)[phpunit/phpunit-selenium

Selenium Server integration for PHPUnit

59610.9M150](/packages/phpunit-phpunit-selenium)[robiningelbrecht/phpunit-pretty-print

Prettify PHPUnit output

76460.0k15](/packages/robiningelbrecht-phpunit-pretty-print)[juampi92/test-seo

Easy way to test your SEO

26341.0k](/packages/juampi92-test-seo)[robiningelbrecht/phpunit-coverage-tools

PHPUnit coverage tools

1783.0k34](/packages/robiningelbrecht-phpunit-coverage-tools)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
