PHPUnit is the de facto unit testing framework for PHP and has been for a long time. It is also the most supported testing framework in IDEs and build systems. Due to this, other testing frameworks have had a hard time trying to grab a foothold.

There's nothing wrong with PHPUnit per se, but those who have been using it for a while understand that despite many major and minor release the features have all but stalled.

1. 100% Interoperable With PHPUnit

concise is an xUnit-style PHP testing framework. It extends the functionality of PHPUnit so that is can be used in place of existing configurations without any changes to the test code, XML configuration or CLI tooling.

concise provides a lot more functionality on top of PHPUnit, if you so choose to use any of those features simply change the test case class from PHPUnit_Framwork_TestCase to Concise\Core\TestCase. Any existing code will still work, here is an example:

class FooTest extends \Concise\Core\TestCase
public function testOldSchool()
// This test was written in PHPUnit
$this->assertEquals(123, 456);

public function testBetter()
// New code

You may use the concise or phpunit runner, which makes it work in existing build systems without needing to change the command. If you are using the concise running under a CI environment you may want to add the --ci option so that it does not output all the formatting and is appropriate for logging.

2. Better CLI

The concise runner has some major features over the exsiting phpunit runner:

  1. A colored progress bar instead of the dots provides precise, constant and visual representation of the tests as they run.
  2. Errors, failures and other test feedback messages are shown immediately above the progress bar so you don't have to wait for all the tests to finish before being able to inspect the failure reasons. This is also very useful in CI environments that show real time logging.
  3. Extra live statistics such as the estimated time remaining.

3. Better (Different) Assertions

concise uses a domain specific language for assertions which more closely resembles something found in a BDD-style testing framework like RSpec, Jasmine, etc.

It looks like this:

public function testSomeStuff()
$result = 100 + 23;

$a = ['foo' => 'bar'];

Assertions are made from chaining methods together that represent a syntax. This syntax makes it very easy to add new DSL:

class UrlModule extends \Concise\Module\AbstractModule
* @syntax url ?:string is valid
public function urlIsValid()
filter_var($this->data[0], FILTER_VALIDATE_URL) === false

class UrlTest extends TestCase
public function testUrlIsValid()
$url = '';

4. IDE Support

Out of the box concise will provide your IDE with code completion that is sensitive to the syntax itself (rather than just listing every possible assertion). This makes it much easier to use code completion with many assertions and acts a filter that only shows what is available for a type or topic:

If you write your own syntaxes you can update the code completion cache by running the concise-init tool.

5. Verify vs Assert

Assertions use a pass-or-bail mentality. That is fine in most cases becuase if a condition is not true it will likely cause something much worse to happen shortly after.

The downside with this style of asserting is that when you want to test many values that are non-fatal you will only see the first failure in your error output which means you have to repeatedly run the failing test until all the assertions pass.

concise support verifications which do not stop the execution of the test and are useful, for say, testing all the property values of an object and reporting all the failures.

Here is an example:

public function testVerify()

6. Better Mocking

Mocking in concise is much less verbose and has a richer feature set than that provided by PHPUnit. Here is an example:

->stub('myOtherMethod')->andThrow(new \Exception('Uh-oh!'));

PHPUnit only supports one type of mock; what is most known as a nice mock. concise supports 3 types of mocks:

  • Normal mocks (or simply mocks) define the behavior for every method you expect to interact with. If your test interacts with any method other than what you have explicitly stated an exception will be thrown.
  • Nice mocks work like the original object where if you don’t specify a given action for a method it will perform as if the mock didn’t exist (pass through to the original method).
  • Partial mocks create a mock from an already existing object. This means you can add custom rules to an object that already contains some arbitrary state.

$calculator = new Calculator();

$mock = $this->partialMock($calculator)

$mock->getMemory(); // 30

7. Compatible With Other Libraries

concise aims to have a rich feature set out of the box to make writing tests easier and more enjoyable. That being said, there are a lot of other great libraries out there such as other testing frameworks and mocking libraries that need not be rejected by your choice of base framework.

concise can use used as a mixed-runner with Pho. The beneifit of this is not only that you are not limited to using one framework but also that you can output JUnit XML and code coverage for your generic build systems.

How Do I Get Started?

It's as easy as installing concise with composer:

composer require --dev elliotchance/concise