

Parody – a PHP testing library - mattsah
https://github.com/dotink/Parody

======
freework
I'm confused, what does this have to do with Aaron Swartz?

~~~
stanleydrew
I assume you are trying to make a joke. But it's not very funny.

~~~
shanelja
Not funny at all, but in all honesty, I am also keen to get back to the normal
flow of things, I have paid my respects and I feel that perhaps 15 articles on
the front page may have been enough for now, leave his friends to mourn as
they will, if you over do anything people tire of it quickly.

------
sentiental
There are a number of mock object libraries out there (mockery, shmock,
phpunit's mock syntax, etc). This just looks like another builder syntax for
making mocks / stubs with some fanciness around class extension - what's the
benefit?

Also, how often is it useful to define a class at test time that is
initialized by production code (from the section I Still Have Serious
Dependency Issues!). This seems like an unlikely use case.

~~~
mattsah
I wrote this library for two major reasons:

1) To avoid the complexity of existing frameworks. 2) To be able to quickly
and easily test code with various kinds of dependencies.

It is predominately designed to test a single class in an extremely isolated
context, not in the context of a full blown testing framework.

It is designed to be a stupid as possible with the intention of allowing the
developer (myself) to define very strict test condition and very fine grain
control.

------
0x0
Noticing it's licensed under AGPL. I guess that means all projects that happen
to include this (or something depending on it) would then be required to post
full source code on the webpage, even if the project is for a single install?

~~~
mattsah
Nope. If the project actually uses tests for its function and it is accessible
they would be required to provide the source to anyone user who requested it.

Much like the regular GPL, AGPL is dependent on distribution.

If you create a project that uses it to perform testing on the project, unless
your testing is a function of the actual project then you wouldn't have to do
anything of the sorts.

If you created a testing platform online that used it to render services, then
you would, yes.

~~~
mattsah
Let me clarify, I don't think anyone's projects that are web facing actually
use their testing library to perform the tasks. So for all intents and
purposes their projects as they are "distributed" over a network are not
linking to it even though it may exist in their source tree.

The only exception to this would be if the project is actually for testing,
like say an online service that tests your code.

Also any private non-distributed use does not require source distribution.

~~~
RyanZAG
Why is it AGPL then and not GPL? This seems to just create a minefield in the
strange event when some test function needs to be exposed to the public for
some reason. While this probably won't occur, if it does, suddenly the whole
source needs to be released.

There does not seem to be any benefit to making this AGPL as it as the source
release on use feature of AGPL is not intended to be used during regular
usage.

Change it to GPL - it makes no practical difference, yet increases potential
usage of the library at no cost.

~~~
mattsah
GPL only applies to binary software. PHP is not compiled and linked.

~~~
RyanZAG
You're correct...

I find this completely insane though! Everything is just 0s and 1s that are
transformed and ultimately can be passed through the logic gates on a CPU to
do math. Whether code is compiled or interpreted has no effect on the end goal
- such as an instruction to the CPU to add two numbers.

Copyright is such a mess.

~~~
mattsah
I tried to clarify above.

What I am trying to get at is that traditionally web software is not
distributed at all. That is, the software itself is never provided to the user
and the user just interfaces with it externally.

The point regarding binary stuff was as it applies to distributed projects
because if PHP or any other interpreted language is "distributed" in a
traditional sense, it is _already_ in source code.

I feel as though the confusion here is because people are looking at the
initial distribution to other developers and thinking that's it. But that
distribution completely allows for a developer to "link to it", modify it, and
keep it behind closed doors _if it is never distributed_. And it isn't if it's
sitting behind a web server... someone is just interfacing with it through a
completely open protocol.

The point of the AGPL is to say that someone who is using a program,
regardless of the fact that the program itself is never being distributed to
them, has a right to get the source.

------
wvenable
Since this library requires PHP 5.4, I'm not sure why it bothers with this
syntax:

    
    
        Parody\Mime::create('Vendor\Class\Project')
        -> onCall('method') -> expect('argument one') ->  give('response one')
        -> onCall('method') -> expect('argument two') -> give('response two')
        -> resolve();
    

When one could just use anonymous functions to give the same result with less
API-as-code.

    
    
        Parody\Mime::create('Vendor\Class\Project')
        -> onCall('method', function($param) {
          if ($param == 'argument one') return 'response one';
          if ($param == 'argument two') return 'response two';
        ) -> resolve();
    

This is more flexible, less code for the framework, and easier to learn.

~~~
mattsah
Expectations and responses (provided by give()) are actually held internally.
Your syntax is cleaner for very simple responses, however will become
increasingly complex, particularly when the response itself is determined by a
callback. Adding more params would clutter your example terribly. Also, the
mime's are passed to callbacks which provide response so that additional
functionality can be added, see this recent use-case:

Parody\Mime::create('App\Text') -> onCall('create') -> expect('WTF' . DS .
'Yo') -> give(function($mime) { return $mime -> onCall('underscorize') ->
give('wtf' . DS . 'yo') -> resolve(); });

~~~
wvenable
That seems exceedingly clever -- too clever. I'd make some of that
functionality a named function and call it from my callback. It took me way
too long to figure out what your code did.

~~~
mattsah
I'm not sure I follow. Could you give a hypothetical example?

It's difficult to tell if you are only referring to the passing of the
original Mime as an argument... which if you are, I presume is more a
limitation of documentation. While it may be clever, it's a pretty standard
use of callbacks to have arguments passed to them that may be useful inside.

~~~
wvenable
Obviously, it would make sense to pass an instance of the object as the first
argument to every callback.

A mocked class stands in for the original class. All the methods are stubs.
Sometimes you need to provide functionality for the stub methods. Isn't it
most logical to use a function to provide a stub method's body?

It's my opinion that all the stuff PHPUnit unit does with mocks (and JUnit as
it's based on) is because Java didn't have anonymous functions. So you _had_
to call methods to essentially construct code for the stub method body. But if
you can provide _actual_ code than all that API is unnecessary.

~~~
mattsah
In 99% of cases I would wager you _don't_ want your stubs to perform much
logic at all. You want their returns to be a static as possible. The more
logic your stubs perform the greater chance your test may fail due to an error
in your test logic.

This is _purposeful_. The point of passing the original mime object in two-
fold. It is first that it allows you to manipulate the object further when and
only when the method is called. This is extremely useful for mocking/stubbing
very dynamic objects whose behavior may change based on a method call.
Secondly, it allows you to return the mimicked object.

Think of a method, for example that might expect two arguments, set a number
of properties, and then return itself. Now imagine it has a second method,
which may also alter one of those properties and return something else
completely. This is pretty straightforward to do with parody:

    
    
        Mime::create('Vendor\Project\Class')
            ->onCall('method1')->expect('arg1', 2)->give(function($mime) {
                $mime->onGet('property1')->give('totally');
                $mime->onGet('property2')->give('possible');
    
                return $mime;
            })
            ->onCall('method2')->give(function($mime) {
                $mime->onGet('property1')->give('new value');
    
                return TRUE;
            });
    

I am not readily familiar with the syntax of other frameworks... but this
seems a lot more flexible than passing arguments to the callback and forcing
untested logic into the callbacks.

As I mentioned in another comment -- this is designed for very strict and
context sensitive test cases. In principle, everything you should ever expect
when calling a method is to give something and get something back, and maybe
that it modifies a property. All of this is possible here, the logic of how it
transforms what you get to what it gives is irrelevant, you should know the
answer to both as a developer. And by removing that logic from the code, you
"fake" only the very explicit expectations of the code you're pretending to
be.

~~~
wvenable
I think I finally see where you're going with this -- that's very interesting
and clever (in a good way). I might suggest that you need better
documentation/examples -- it took me a while to _see_ what was going on.

~~~
mattsah
I completely agree with this, and the documentation will be forthcoming on the
GitHub wiki - so please follow and keep and eye out. There will also be an
article at some point soon on Nettuts which will give a pretty complete
example of it mimicking a new object instantiation to resolve a static
dependency on an example class being tested.

------
Gigablah
This library could use Composer support. I suppose I'll make a quick pull
request.

~~~
mattsah
I'm cool with that. Feel free to move the example.php into a separate
subdirectory to make it cleaner too, but please fix the includes to point to
whatever relative directory need be.

~~~
Gigablah
It's done, I didn't touch the file structure since Composer is smart enough to
map classes even if the layout isn't PSR compliant.

~~~
mattsah
Ok... I did have to increase version in composer.json though due to literal
array syntax (hopefully not a deal breaker).

~~~
Gigablah
Sure thing, didn't notice before.

------
parf73
great simplistic php testing framework: <https://github.com/parf/spartan-test>

