Skip to content

Commit

Permalink
Merge 6054469 into 0e605c2
Browse files Browse the repository at this point in the history
  • Loading branch information
jails committed Sep 24, 2016
2 parents 0e605c2 + 6054469 commit cfe6049
Show file tree
Hide file tree
Showing 236 changed files with 12,476 additions and 10,026 deletions.
23 changes: 15 additions & 8 deletions .travis.yml
@@ -1,30 +1,37 @@
sudo: false
sudo: required
dist: xenial
group: edge
language: php

php:
- 5.4
- 5.5
- 5.6
- 7.0
- 7.1
- hhvm

matrix:
fast_finish: true
allow_failures:
- php: hhvm

before_script:
- composer config -g github-oauth.github.com $GITHUB_COMPOSER_AUTH
- composer self-update
- composer install --no-interaction
- if [[ $TRAVIS_PHP_VERSION =~ ^hhvm ]]; then echo 'xdebug.enable = On' >> /etc/hhvm/php.ini; fi
#- if [[ $TRAVIS_PHP_VERSION =~ ^7 ]]; then pecl install xdebug; fi
- if [[ $TRAVIS_PHP_VERSION =~ ^hhvm ]]; then echo 'hhvm.php7.all = 1' >> /etc/hhvm/php.ini; fi
- if [[ $TRAVIS_PHP_VERSION =~ ^7 ]]; then pecl install xdebug; fi

script: bin/kahlan --config=kahlan-config.travis.php --clover=clover.xml
script:
- vendor/bin/phpcs
- bin/kahlan --config=kahlan-config.travis.php --clover=clover.xml

after_success:
- "if [ $(phpenv version-name) = '5.6' ]; then curl -X POST -d @codeclimate.json -H 'Content-Type:application/json' https://codeclimate.com/test_reports --verbose; fi"
- "if [ $(phpenv version-name) = '5.6' ]; then curl -F 'json_file=@coveralls.json' https://coveralls.io/api/v1/jobs --verbose; fi"
- "if [ $(phpenv version-name) = '5.6' ]; then wget https://scrutinizer-ci.com/ocular.phar; fi"
- "if [ $(phpenv version-name) = '5.6' ]; then php ocular.phar code-coverage:upload --format=php-clover 'clover.xml'; fi"
- "if [ $(phpenv version-name) = '7.0' ]; then curl -X POST -d @codeclimate.json -H 'Content-Type:application/json' https://codeclimate.com/test_reports --verbose; fi"
- "if [ $(phpenv version-name) = '7.0' ]; then curl -F 'json_file=@coveralls.json' https://coveralls.io/api/v1/jobs --verbose; fi"
- "if [ $(phpenv version-name) = '7.0' ]; then wget https://scrutinizer-ci.com/ocular.phar; fi"
- "if [ $(phpenv version-name) = '7.0' ]; then php ocular.phar code-coverage:upload --format=php-clover 'clover.xml'; fi"

env:
global:
Expand Down
16 changes: 16 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,22 @@

## Last changes

## 3.0.0 (2016-09-01)

* **Add:** Add `allow()` DSL.
* **Add:** Add `toBeCalled()` matcher.
* **Add:** `toReceive` now support a chain of messages as definition if correctly stubbed.
* **Add:** Allow to monkey patch a class using a specific instance for all `new` on that class.
* **Change:** Refactor the reporting to provide more meaningful messages on failure.
* **Bugfix:** Fixes an issue with `toReceive()/toBeCalled` and stubs where past called methods were taken into account.
* **BC break:** Cached files are no more compatible, cached files needs to be purged.
* **BC break:** Rename `'params'` option to `'args'` in `Double::instance()`.
* **BC break:** `Stub::on()` is now deprecated use `allow()` instead.
* **BC break:** Rename `Stub::create()` to `Double::instance()`.
* **BC break:** Rename `Stub::classname()` to `Double::classname()`.
* **BC break:** Rename `before()` and `after()` to `beforeAll()` and `afterAll()`.
* **BC break:** Remove `toReceiveNext` matchers in flavor of `->ordered` attribute to be more close to rspec way.

## 2.5.7 (2016-09-23)

* **BC break:** Moving Kahlan to its own organization.
Expand Down
45 changes: 32 additions & 13 deletions README.md
Expand Up @@ -11,13 +11,12 @@

Kahlan is a full-featured Unit & BDD test framework a la RSpec/JSpec which uses a `describe-it` syntax and moves testing in PHP one step forward.

Kahlan embraces the [KISS principle](http://en.wikipedia.org/wiki/KISS_principle) and makes Unit & BDD testing fun again!
**Kahlan allows to stub or monkey patch your code directly like in Ruby or JavaScript without any required PECL-extentions.**

**Killer feature:** Kahlan allows to stub or monkey patch your code directly like in Ruby or JavaScript without any required PECL-extentions.

## Video
## Videos

* <a href="http://vimeo.com/116949820" target="_blank">Warren Seymour presentation at Unified Diff (2015)</a>
* <a href="https://www.grafikart.fr/tutoriels/php/tdd-kahlan-805" target="_blank">Grafikart presentation in French (2016)</a>

## IRC

Expand All @@ -26,42 +25,62 @@ Kahlan embraces the [KISS principle](http://en.wikipedia.org/wiki/KISS_principle

## Documentation

See the whole [documentation here](http://kahlan.readthedocs.org/en/latest) (documentation for Kahlan <= 1.3.0 [can still be found here](docs/deprecated))
See the whole [documentation here](http://kahlan.readthedocs.org/en/latest)

## Requirements

* PHP 5.5+
* Composer
* [Xdebug](http://xdebug.org/) (if you want to perform code coverage analysis)
* [phpdbg](http://php.net/manual/en/debugger-about.php) or [Xdebug](http://xdebug.org/) (required for code coverage analysis only)

## Main Features

* Simple API
* RSpec/JSpec syntax
* Code Coverage metrics ([xdebug](http://xdebug.org) or [phpdbg](http://phpdbg.com/docs) required)
* Handy stubbing system ([mockery](https://github.com/padraic/mockery) or [prophecy](https://github.com/phpspec/prophecy) are no longer needed)
* Set stubs on your class methods directly (i.e allows dynamic mocking)
* Ability to Monkey Patch your code (i.e. allows replacement of core functions/classes on the fly)
* Check called methods on your class/instances
* Check called methods on your classes/instances
* Built-in Reporters (Terminal or HTML reporting through [istanbul](https://gotwarlost.github.io/istanbul/) or [lcov](http://ltp.sourceforge.net/coverage/lcov.php))
* Built-in Exporters (Coveralls, Code Climate, Scrutinizer, Clover)
* Extensible, customizable workflow
* Small code base (~10 times smaller than PHPUnit)

## Syntax

```php
<?php

describe("Example", function() {

it("passes if true === true", function() {
it("makes an expectation", function() {

expect(true)->toBe(true);

});

it("expects methods to be called", function() {

expect($user)->toReceive('save')->with(['validates' => false]);

$user = new User();
$user->validates(['validates' => false]);

});

it("stubs a function", function() {

expect(true)->toBe(true);
allow('time')->toBeCalled()->andReturn(123);
$user = new User();
expect($user->save())->toBe(true)
expect($user->created)->toBe(123);

});

it("passes if false !== true", function() {
it("stubs a class", function() {

expect(false)->not->toBe(true);
allow('PDO')->toReceive('prepare', 'fetchAll')->andReturn([['name' => 'bob']]);
$user = new User();
expect($user->all())->toBe([['name' => 'bob']]);

});

Expand Down
8 changes: 7 additions & 1 deletion composer.json
Expand Up @@ -11,11 +11,17 @@
"require": {
"php": ">=5.4"
},
"require-dev": {
"squizlabs/php_codesniffer": "^2.7"
},
"autoload": {
"psr-4": {
"Kahlan\\": "src/"
},
"files": ["src/init.php"]
"files": [
"src/init.php",
"src/functions.php"
]
},
"autoload-dev": {
"psr-4": {
Expand Down
44 changes: 0 additions & 44 deletions docs/README.md

This file was deleted.

157 changes: 157 additions & 0 deletions docs/allow.md
@@ -0,0 +1,157 @@
## Stubs & Monkey Patching DSL

A method stub or simply stub in software development is used to stand in for some other programming functionality. This section explains how to perform such replacement with Kahlan.

### Method Stubbing

Use `allow()` to stub an existing method on any class like so:

```php
it("stubs a method by setting a return value", function() {
$instance = new MyClass();
allow($instance)->toReceive('myMethod')->andReturn('Good Morning World!');

expect($instance->myMethod())->toBe('Good Morning World!');
});
```

```php
it("stubs a method by setting a return value only when some arguments matches", function() {
$instance = new MyClass();
allow($instance)->toReceive('myMethod')->with('Hello!')->andReturn('Good Morning World!');

expect($instance->myMethod('Hello!'))->toBe('Good Morning World!');
expect($instance->myMethod())->toBe(null);
});
```

You can specify multiple return values with:

```php
it("stubs a method with multiple return values", function() {
$instance = new MyClass();
allow($instance)->toReceive('sequential')->andReturn(1, 3, 2);

expect($instance->sequential())->toBe(1);
expect($instance->sequential())->toBe(3);
expect($instance->sequential())->toBe(2);
});
```

You can also stub `static` methods using `::`:

```php
it("stubs a static method", function() {
$instance = new MyClass();
allow($instance)->toReceive('::myMethod')->andReturn('Good Morning World!');

expect($instance::myMethod())->toBe('Good Morning World!');
});
```

It's also possible to use a closure to replace the whole method logic:

```php
it("stubs a method using a closure", function() {
allow($foo)->toReceive('myMethod')->andRun(function($param) { return $param; });
expect($instance->myMethod('Hello World!'))->toBe('Hello World!');
});
```

Moreover you can stub a chain of methods by using the following syntax.

```php
it('should patch PDO', function() {
allow('PDO')->toReceive('prepare', 'fetchAll')->andReturn([['name' => 'bob']]);

$user = new User();
expect($user->all())->toBe([
['name' => 'bob']
]);
});
```

Where the `User` class is:

```php
<?php
use PDO;

class User
{
protected $_db = null;

public function __construct()
{
$this->_db = new PDO('mysql:dbname=testdb;host=localhost', 'root','');
}

public function all()
{
$stmt = $this->db->prepare('SELECT * FROM users');
$stmt->execute();
return $stmt->fetchAll();
}
}
```

In practice method chaining is considered as code smells because it tends to violate the Law of Demeter. So use it wisely.

Finally, `where()` can be used to specify some arguments requirement for a chain of methods:

```php
it('returns the stubbed return value when arguments requirement match', function() {
$query = new MyQuery();
allow($query)
->toReceive('find', 'where', 'order', 'limit')
->where([
'find' => ['widgets']],
'where' => [['name' => 'Bottle Opener']],
'order' => [['id' => 'desc']],
'limit' => [10]
])
->andReturn([[id => '123','name' => 'Bottle Opener']]);

expect($query->find('widgets')
->where(['name' => 'Bottle Opener'])
->order(['id' => 'desc'])
->limit(10))->toBe([[id => '123','name' => 'Bottle Opener']]);
});
```

### <a name="function-stubbing"></a>Function Stubbing

Use `allow()` to stub almost all functions like so:

```php
it("shows some examples of function stubbing", function() {
allow('time')->toBeCalled()->andReturn(123);
allow('time')->toBeCalled()->andReturn(123, 456, 789);
allow('time')->toBeCalled()->andRun(function() { return 123; });

allow('rand')->toBeCalled()->with(0, 10)->andReturn(5);
});
```

### <a name="monkey-patching"></a>Monkey Patching

Use `allow()` to monkey patch classes like so:

```php
it("shows some examples of function stubbing", function() {
// Monkey patch `PDO` and stub chained methods under the hood.
allow('PDO')->toReceive('prepare->fetchAll')->andReturn([['name' => 'bob']]);
allow('PDO')->toReceive('prepare->fetchAll')->andRun(function() {
return [['name' => 'bob']];
});

// Monkey patch `PDO` with a specific class.
allow('PDO')->toBe('My\Alternative\PDO');

// Monkey patch `DateTime` with a specific instance.
allow('DateTime')->toBe(new DateTime('@123'));

// Monkey patch `PDO` with a generic stub instance.
allow('PDO')->toBeOK();
});
```
Binary file added docs/assets/phpspec_3.1_code_coverage.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/assets/phpunit_5.7_code_coverage.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit cfe6049

Please sign in to comment.