In a unit test, test doubles (dummies, fakes, stubs, spys, and mocks)
are used to simulate the behavior of complex, real objects when a
real object is difficult or impossible to incorporate into a unit
test. A test double can be used anywhere in the program where the
program expects an object of the original class.
Traditionally, frameworks and libraries are used to automatically
generate test double objects based on the original objects using
reflection. This idea stems from a time when IDEs were not powerful
enough to do this generation for us.
Quoting Robert C. Martin [1]:
"Stubs and spies are very easy to write. My IDE makes it trivial.
I just point at the interface and tell the IDE to implement it.
Voila! It gives me a dummy. Then I just make a simple modification
and turn it into a stub or a spy. [...]
I don't like the strange syntax of mocking tools, and the
complications they add to my setups. I find writing my own test
doubles to be simpler in most cases."
The more I think about this (especially in the aftermath of the
unserialize()
issue), the more it appeals to me to have "real
code", generated in parts by my IDE, for my test doubles.
The question remains, though, where to put this code. Obviously
it belongs with the test suite. But how to name the test double
classes? Unique names are required to be able to have multiple
(differently configured) test doubles for the same original
class. Mocking tools (such as PHPUnit_MockObject, for instance)
use random names for their test double classes.
Anonymous classes could be a solution for this problem.
I was surprised to find [2] (and even more so because I voted "No"
on it ... and had no recollection of reading the RFC or voting
on it). Most likely I did not see a use case for anonymous classes
back then.
I would like to propose the following syntax for declaring an
anonymous class that does not implement an interface or extend
a class:
$o = new {
public function method() {
// ...
}
// ...
};
An anonymous class can extend one class or implement one interface.
Unlike non-anonymous classes, an anonymous class cannot do both.
In other words, it cannot both extend a class and implement an
interface, nor can it implement more than one interface.
The syntax to have an anonymous class implement an interface or
extend a class is the same:
interface AnInterface {
public function method();
}
$o = new AnInterface {
public function method() {
// ...
}
}
class AClass {
public function method()
{
// ...
}
}
$o = new AClass {
// ...
}
The ReflectionClass and ReflectionObject classes need to be adapted
for anonymous classes. Only new syntax elements would be added to
the PHP language. Existing elements of the PHP language syntax would
not be changed. No new reserved words would be added to the PHP
language. There should not be an impact on backwards compatibility.
In case there is enough positive feedback to the syntax proposed in
this email I would gladly write a formal RFC. I could also help with
writing tests and documentation. Unfortunately I cannot implement the
feature myself.
--
[1] http://blog.8thlight.com/uncle-bob/2014/05/14/TheLittleMocker.html
[2] https://wiki.php.net/rfc/anonymous_classes
$o = new {
public function method() {
// ...
}// ...
};
I wanted a similar syntax for object literals, but seems you had the same idea for anonymous classes.
Which makes me wonder if we need to implement the full features of a class. Could we perhaps use a more compact, but limited, syntax for properties and methods here? Would these dummies need private and protected members?
Andrea Faulds
http://ajf.me/
Hello @internals,
Le 28 juin 2014 à 15:36, Sebastian Bergmann sebastian@php.net a écrit :
The question remains, though, where to put this code. Obviously
it belongs with the test suite. But how to name the test double
classes? Unique names are required to be able to have multiple
(differently configured) test doubles for the same original
class. Mocking tools (such as PHPUnit_MockObject, for instance)
use random names for their test double classes.
Atoum[1] use the \mock namespace to name stub and mock class.
So, if you want to create a mock of \vendor\project\bar, atoum generate the class \mock\vendor\project\bar.
Moreover, to create a mock, just do $mock = new \mock\vendor\project\bar() in a test method and atoum generate automatically the corresponding mock class code, thanks to its autoloader.
And an another namespace than \mock can be defined in the configuration file or in the test class.
But sure that anonymous class may be interesting…
Best regards,
Fred
========================================================================
Frédéric Hardy : Directeur technique et fonctionnel agile
CV : http://blog.mageekbox.net/public/cv.frederic.hardy.pdf
Blog : http://blog.mageekbox.net
Twitter : http://twitter.com/mageekguy
I was surprised to find [2] (and even more so because I voted "No"
on it ... and had no recollection of reading the RFC or voting
on it). Most likely I did not see a use case for anonymous classes
back then.I would like to propose the following syntax for declaring an
anonymous class ...
Apart from a possibly new use case (although the wiki page does list "it
would also definitely be very useful for mocking in tests" among the
"Use Cases from the Community"), how is this proposal different from the
one which was rejected less than a year ago?
Not that I particularly oppose it, but is there any reason to think that
this will pass now when it didn't then? For that matter, has there ever
been discussion of a "statute on limitations" (time of other conditions
for reopening discussion) for re-proposing previously rejected features?
Regards,
--
Rowan Collins
[IMSoP]
Am 28.06.2014 20:53, schrieb Rowan Collins:
Apart from a possibly new use case (although the wiki page does list "it
would also definitely be very useful for mocking in tests" among the "Use
Cases from the Community"), how is this proposal different from the one
which was rejected less than a year ago?
I probably missed the use case back then :) I like the syntax I
proposed better (including the limitation regarding extending classes
and implementing interfaces) which is more similar to that of other
languages.
For that matter, has there ever been discussion of a
"statute on limitations" (time of other conditions for reopening
discussion) for re-proposing previously rejected features?
Interesting question (that IMHO should be the topic of a separate
thread / RFC).
Am 28.06.2014 20:53, schrieb Rowan Collins:
Apart from a possibly new use case (although the wiki page does list "it
would also definitely be very useful for mocking in tests" among the "Use
Cases from the Community"), how is this proposal different from the one
which was rejected less than a year ago?I probably missed the use case back then :) I like the syntax I
proposed better (including the limitation regarding extending classes
and implementing interfaces) which is more similar to that of other
languages.
You still haven't stated why you intend to restrict it to only one
implements/extends; I'd like to hear your reasoning.
Hi,
Am 28.06.2014 15:36, schrieb Sebastian Bergmann:
An anonymous class can extend one class or implement one interface.
Unlike non-anonymous classes, an anonymous class cannot do both.
In other words, it cannot both extend a class and implement an
interface, nor can it implement more than one interface.
Omitting name might be useful but please don't redefine the class concept.
Am 28.06.2014 20:53, schrieb Rowan Collins:
is there any reason to think that this will pass now when it didn't then?
Don't know a reason but PHP6 is a good point in time for new syntax &
features. +1
cryptocompress
Hi!
I would like to propose the following syntax for declaring an
anonymous class that does not implement an interface or extend
a class:
What would be the use case for such class? I can understand what is the
use case for a just-in-time class that implement certain interface or
extends certain class - mocks, stubs, handlers, etc. - e.g. widely used
in Java in this way. However free-standing anonymous class, not
connected to any hierarchy, does not seem to be very useful - why not
have a real class then, or if it's too much trouble, an anon function?
I know C# has them, but in that case it plays more of a role that
stdclass or just array plays in PHP.
OTOH, if you just make your anon class extend stdclass you have the same
effect but without a special case that looks a bit weird to me. But
maybe it's not a real problem since again you can do the same with
stdclass.
$o = new AnInterface {
Looks very similar to what Java does except Java does () and allows to
pass ctor arguments. Are we assuming here anonymous classes do not have
ctors, never call them or something else?
A bit of bikeshedding: if we don't want to follow Java here, we could
also do this:
$o = class {
public function method() { ... }
...
} as AnInterface;
This is (partially) stolen from Groovy syntax and follows roughly the
same model as anon functions (definition without the name). Also, this
is a bit like you define anonymous struct type in C and C-like languages.
--
Stanislav Malyshev, Software Architect
SugarCRM: http://www.sugarcrm.com/