项目作者: eloquent

项目描述 :
Mocks, stubs, and spies for PHP.
高级语言: PHP
项目地址: git://github.com/eloquent/phony.git
创建时间: 2014-09-19T02:44:49Z
项目社区:https://github.com/eloquent/phony

开源协议:MIT License

下载


No longer maintained

This package is no longer maintained. See [this statement] for more info.

[this statement]: https://gist.github.com/ezzatron/713a548735febe3d76f8ca831bc895c0

Phony

Mocks, stubs, and spies for PHP.

Current version image

Example verification output

Installation

Available as various Composer packages, depending on the test framework in
use:

See the section on Integration with test frameworks in the documentation.

Documentation

Full documentation is available.

What is Phony?

Phony is a PHP library for creating various kinds of test doubles, including
object mocks, function stubs, and function spies.

Why use Phony?

Support for language features, old and new

Phony is committed to supporting new PHP features as they emerge. Features
that require workarounds in other mocking frameworks (such as
passed-by-reference arguments), typically “just work” with Phony.

Amongst other features, Phony supports:

Zero-configuration integrations

Integrating Phony with your favorite test framework is as simple as
choosing the correct namespace to import. Complimentary features make
integrations seamless and intuitive:

Interested in better integration with other test frameworks? So are we! Just
open a GitHub issue if there’s something we can do.

Refined verification output

Phony has received a great deal of work and refinement where the rubber meets
the road; in its verification output:

Example verification output

Phony‘s output includes succinct but detailed information that is focused on
helping you find the cause of the failure as fast as possible. Where
appropriate, the output uses difference comparison and color to further
highlight important details.

First-class support for functions and callbacks

Phony is designed to provide support for testing both object-based, and
function-based systems. In Phony, object mocks are built upon full-featured
function level stubs and spies.

This focus on support for procedural programming allows Phony to handle many
situations that cannot be handled by solely class-based mocking frameworks.
Since Phony‘s class-level support is based upon its function-level support,
the interfaces are consistent, and require very little extra knowledge to use.

Extensive feature set

Phony has an extensive and powerful feature set. For detailed information on
a particular feature, select one of the following:

Help

For help with a difficult testing scenario, questions regarding how to use
Phony, or to report issues with Phony itself, please open a GitHub issue
so that others may benefit from the outcome.

Alternatively, @ezzatron may be contacted directly via Twitter.

Usage

For detailed usage, see the documentation.

Example test suites

See the phony-examples repository.

Standalone usage

Install the eloquent/phony package, then:

  1. use function Eloquent\Phony\mock;
  2. $handle = mock('ClassA');
  3. $handle->methodA->with('argument')->returns('value');
  4. $mock = $handle->get();
  5. assert($mock->methodA('argument') === 'value');
  6. $handle->methodA->calledWith('argument');

Kahlan usage

Install the eloquent/phony-kahlan package, then:

  1. use function Eloquent\Phony\Kahlan\mock;
  2. describe('Phony', function () {
  3. it('integrates with Kahlan', function () {
  4. $handle = mock('ClassA');
  5. $handle->methodA->with('argument')->returns('value');
  6. $mock = $handle->get();
  7. expect($mock->methodA('argument'))->toBe('value');
  8. $handle->methodA->calledWith('argument');
  9. });
  10. });

The eloquent/phony-kahlan package also provides auto-wired mocks:

  1. use function Eloquent\Phony\Kahlan\on;
  2. describe('Phony for Kahlan', function () {
  3. it('supports auto-wiring', function (ClassA $mock) {
  4. $handle = on($mock);
  5. $handle->methodA->with('argument')->returns('value');
  6. expect($mock->methodA('argument'))->toBe('value');
  7. $handle->methodA->calledWith('argument');
  8. });
  9. });

Peridot usage

Install the eloquent/phony-peridot package, then:

  1. use function Eloquent\Phony\mock;
  2. describe('Phony', function () {
  3. it('integrates with Peridot', function () {
  4. $handle = mock('ClassA');
  5. $handle->methodA->with('argument')->returns('value');
  6. $mock = $handle->get();
  7. expect($mock->methodA('argument'))->to->equal('value');
  8. $handle->methodA->calledWith('argument');
  9. });
  10. });

The eloquent/phony-peridot package also provides auto-wired mocks:

  1. use function Eloquent\Phony\on;
  2. describe('Phony for Peridot', function () {
  3. it('supports auto-wiring', function (ClassA $mock) {
  4. $handle = on($mock);
  5. $handle->methodA->with('argument')->returns('value');
  6. expect($mock->methodA('argument'))->to->equal('value');
  7. $handle->methodA->calledWith('argument');
  8. });
  9. });

PHPUnit usage

Install the eloquent/phony-phpunit package, then:

  1. use Eloquent\Phony\Phpunit\Phony;
  2. class PhonyTest extends PHPUnit_Framework_TestCase
  3. {
  4. public function testIntegration()
  5. {
  6. $handle = Phony::mock('ClassA');
  7. $handle->methodA->with('argument')->returns('value');
  8. $mock = $handle->get();
  9. $this->assertSame('value', $mock->methodA('argument'));
  10. $handle->methodA->calledWith('argument');
  11. }
  12. }

Inception

Please forgive me if this section is opinionated, or if I recall some particular
detail of a framework incorrectly. But if you want a TL;DR for why I created
Phony, basically:

The first mocking framework I used was probably SimpleTest‘s. Unfortunately,
that’s a long time ago now, and I didn’t really understand mocking at the time.
And to top it off, I can’t even remember how SimpleTest‘s mocks functioned.
So let’s skip ahead to the first mocking framework I really explored in depth,
which was PHPUnit‘s.

When I first discovered PHPUnit‘s mocks, they were revolutionary to me. They
allowed me to test things really thoroughly, in ways I didn’t even know were
possible previously. Although PHPUnit‘s mocking was likely a port of some
existing Java mocking system, it was the framework that first opened my eyes to
the real potential of test doubles.

Unfortunately it wasn’t all sunshine and roses. PHPUnit‘s mocks were difficult
to use, and especially, because of the expect-run-verify style interface, they
were difficult to re-use. There was no way to “un-expect” something in a
particular test, and when something failed, the true cause of the failure was
often difficult to determine.

While searching for a better solution, I stumbled across Phake, which was
inspired by Java’s extremely popular Mockito mocking framework. Phake was,
and still is, an excellent mocking framework. Both Mockito and Phake eschew
the expect-run-verify pattern, to great benefit.

By treating stubbing and verification as separate concepts, Phake essentially
fixed all of the problems I had with PHPUnit‘s mocks. Mocks could be re-used
easily, and when a test failed, the cause was (usually) clear. I was sold on the
evils of expect-run-verify, and swore never to go back.

I believe it was around this time that I heard of Mockery. Although I was
fairly happy with Phake, it did have a few little oddities, such as the way
it deals with by-reference arguments, and mocking of traits was not possible.
So I checked out Mockery, but was immediately put off by its use of
expectations; which I felt would have been a huge step backwards.

In fairness, it’s possible that Mockery supports other mocking methods, but
since the “primary” way it works seems to be based around expect-run-verify,
I’ve never considered it a viable candidate, and have never used it.

At some point around this time I worked on a Node.js project, and explored a
handful of the most popular Node mocking frameworks, before settling on the
excellent Sinon.JS. Its focus on callback-based stubbing and spying, and its
extensive verification API would eventually influence Phony heavily.

It wasn’t until HHVM became a viable option in the PHP world that I really had
to consider using something other than Phake. I had wanted for a while to
start experimenting with HHVM in my projects. Unfortunately Phake had issues
that prevented the test suite from even running under HHVM, so it was
immediately a problem.

One of my co-workers had intended to work on HHVM support for Phake, but
unfortunately it seemed at that time as though work on Phake had stagnated,
and it took over a month just to get a PR accepted that added HHVM as a test
target. To Phake‘s credit, HHVM is now supported, and hindsight has taught
me that HHVM support is hard.

One project that showed promise was Prophecy, an “opinionated” mocking
framework that arose from the phpspec testing framework. While I disliked its
use of abstract terms like “prophesize” and “reveal” for method names, the core
idea of a separate object instance that can be used to control the actual mock
worked really well. So well, in fact, that I would eventually end up using this
concept in Phony.

Importantly, Prophecy already supported HHVM, and seemed like a great fit to
replace Phake; until I ran into the “opinionated” side of Prophecy‘s nature.
One thing that Prophecy does not support is order verification. For example;
verifying that your code opens a file before writing to it. It seemed to me to
be a feature whose benefits are self-evident, but the Prophecy developers
unfortunately did not agree.

So, fool that I am, I thought “How hard can it be?” and started work on Phony,
a mocking framework designed to combine the strengths of its predecessors, and
allow testing under HHVM without compromise.

New versions of PHP came along and introduced new language features, and Phony
adapted to meet the requirements of testing these features. I was also fortunate
enough to be part of a development team at my day job, who were willing to be
the test bed for Phony, and it received a lot of real-world usage that
contributed immensely to Phony‘s stability and eventual feature set.

Of course it turned into a much longer journey than I first expected, and
Phony continues to be a challenging project to maintain. But for me, it’s an
invaluable tool that I use almost every day, and I hope it can be the same for
you.

Thanks for listening.

Erin (@ezzatron)

Thanks

Special thanks to the following people:

License

For the full copyright and license information, please view the LICENSE file.