() translation by (you can also view the original English article)
Wenn Sie PhpSpec mit anderen Test-Frameworks vergleichen, werden Sie feststellen, dass es sich um ein sehr ausgeklügeltes und meinungsstarkes Tool handelt. Einer der Gründe dafür ist, dass PhpSpec kein Test-Framework ist, wie Sie es bereits kennen.
Stattdessen ist das ein Entwurfswerkzeug, das hilft, das Verhalten von Software zu beschreiben. Ein Nebeneffekt der Beschreibung des Verhaltens von Software mit PhpSpec ist, dass Sie Spezifikationen erhalten, die später auch als Tests dienen.
In diesem Artikel werden wir einen Blick unter die Haube von PhpSpec werfen und versuchen, ein tieferes Verständnis der Funktionsweise und Verwendung zu erlangen.
Wenn Sie phpspec auffrischen möchten, werfen Sie einen Blick auf mein Tutorial für die ersten Schritte.
In diesem Artikel...
- Eine kurze Tour durch die Interna von PHPSpec
- Der Unterschied zwischen TDD und BDD
- Wie unterscheidet sich PHPSpec (von PHPUnit)
- PhpSpec: Ein Design-Tool
Eine kurze Tour durch die Interna von PHPSpec
Beginnen wir mit einem Blick auf einige der wichtigsten Konzepte und Klassen, die PhpSpec bilden.
$this
verstehen
Zu verstehen, worauf sich $this
bezieht, ist der Schlüssel, um zu verstehen, wie sich PhpSpec von anderen Tools unterscheidet. Grundsätzlich bezieht sich $this
auf eine Instanz der tatsächlichen getesteten Klasse. Lassen Sie uns versuchen, dies etwas genauer zu untersuchen, um besser zu verstehen, was wir meinen.
Zuallererst brauchen wir eine Spezifikation und eine Klasse, mit der wir herumspielen können. Wie Sie wissen, machen uns die Generatoren von PhpSpec dies ganz einfach:
1 |
$ phpspec desc "Suhm\HelloWorld" |
2 |
$ phpspec run
|
3 |
Do you want me to create `Suhm\HelloWorld` for you? y |
Öffnen Sie als Nächstes die generierte Spezifikationsdatei und versuchen Sie, ein wenig mehr Informationen zu $this
zu erhalten:
1 |
<?php
|
2 |
|
3 |
namespace spec\Suhm; |
4 |
|
5 |
use PhpSpec\ObjectBehavior; |
6 |
use Prophecy\Argument; |
7 |
|
8 |
class HelloWorldSpec extends ObjectBehavior |
9 |
{
|
10 |
function it_is_initializable() |
11 |
{
|
12 |
$this->shouldHaveType('Suhm\HelloWorld'); |
13 |
|
14 |
var_dump(get_class($this)); |
15 |
}
|
16 |
}
|
get_class()
gibt den Klassennamen eines gegebenen Objekts zurück. In diesem Fall werfen wir einfach $this
hinein, um zu sehen, was es zurückgibt:
1 |
$ string(24) "spec\Suhm\HelloWorldSpec" |
Okay, also nicht allzu überraschend, sagt uns get_class()
, dass $this
eine Instanz von spec\Suhm\HelloWorldSpec
ist. Es ist sinnvoll, da es sich schließlich nur um alten PHP-Code handelt. Wenn wir stattdessen get_parent_class()
verwenden, würden wir PhpSpec\ObjectBehavior
verwenden, da unsere Spezifikation diese Klasse erweitert.
Denken Sie daran, ich habe Ihnen gerade gesagt, dass sich $this
tatsächlich auf die getestete Klasse bezieht, die in unserem Fall Suhm\HelloWorld
wäre? Wie Sie sehen, steht der Rückgabewert von get_class($this)
im Widerspruch zu $this->shouldHaveType('Suhm\HelloWorld');
.
Probieren wir etwas anderes aus:
1 |
<?php
|
2 |
|
3 |
namespace spec\Suhm; |
4 |
|
5 |
use PhpSpec\ObjectBehavior; |
6 |
use Prophecy\Argument; |
7 |
|
8 |
class HelloWorldSpec extends ObjectBehavior |
9 |
{
|
10 |
function it_is_initializable() |
11 |
{
|
12 |
$this->shouldHaveType('Suhm\HelloWorld'); |
13 |
|
14 |
var_dump(get_class($this)); |
15 |
|
16 |
$this->dumpThis()->shouldReturn('spec\Suhm\HelloWorldSpec'); |
17 |
}
|
18 |
}
|
Mit dem obigen Code versuchen wir, eine Methode namens dumpThis()
auf der HelloWorld
-Instanz aufzurufen. Wir verketten eine Erwartung an den Methodenaufruf und erwarten, dass der Rückgabewert der Funktion ein String ist, der "spec\Suhm\HelloWorldSpec"
enthält. Dies ist der Rückgabewert von get_class()
in der obigen Zeile.
Auch hier können uns die PhpSpec-Generatoren bei einigen Gerüsten helfen:
1 |
$ phpspec run
|
2 |
Do you want me to create `Suhm\HelloWorld::dumpThis()` for you? y |
Versuchen wir auch, get_class()
aus dumpThis()
heraus aufzurufen:
1 |
<?php
|
2 |
|
3 |
namespace Suhm; |
4 |
|
5 |
class HelloWorld |
6 |
{
|
7 |
|
8 |
public function dumpThis() |
9 |
{
|
10 |
return get_class($this); |
11 |
}
|
12 |
}
|
Wieder, nicht überraschend, erhalten wir:
1 |
10 ✘ it is initializable |
2 |
expected "spec\Suhm\HelloWorldSpec", but got "Suhm\HelloWorld". |
Es sieht so aus, als ob uns hier etwas fehlt. Ich begann damit, dass ich Ihnen sagte, dass sich $this
nicht auf das bezieht, was Sie denken, aber bisher haben unsere Experimente nichts Unerwartetes gezeigt. Außer einer Sache: Wie konnten wir $this->dumpThis()
aufrufen, bevor es existierte, ohne dass PHP bei uns quietschte?
Um dies zu verstehen, müssen wir in den Quellcode von PhpSpec eintauchen. Wer selbst reinschauen möchte, kann den Code auf GitHub nachlesen.
Sehen Sie sich den folgenden Code von src/PhpSpec/ObjectBehavior.php
(der Klasse, die unsere Spezifikation erweitert) an:
1 |
/**
|
2 |
* Proxies all call to the PhpSpec subject
|
3 |
*
|
4 |
* @param string $method
|
5 |
* @param array $arguments
|
6 |
*
|
7 |
* @return mixed
|
8 |
*/
|
9 |
public function __call($method, array $arguments = array()) |
10 |
{
|
11 |
return call_user_func_array(array($this->object, $method), $arguments); |
12 |
}
|
Die Kommentare verraten das meiste davon: "Proxies all call to the PhpSpec Subject"
. Die PHP-Methode __call
ist eine magische Methode, die automatisch aufgerufen wird, wenn eine Methode nicht zugänglich (oder nicht vorhanden) ist.
Das bedeutet, dass, als wir versuchten, $this->dumpThis()
aufzurufen, der Aufruf anscheinend an das PhpSpec-Subjekt weitergeleitet wurde. Wenn Sie sich den Code ansehen, können Sie sehen, dass der Methodenaufruf an $this->object
weitergeleitet wird. (Dasselbe gilt für Eigenschaften in unserer Instanz. Sie werden alle auch mit anderen magischen Methoden an das Subjekt weitergeleitet. Sehen Sie sich die Quelle an, um es selbst zu sehen.)
Lassen Sie uns get_class()
noch einmal konsultieren und sehen, was es über $this->object
zu sagen hat:
1 |
<?php
|
2 |
|
3 |
namespace spec\Suhm; |
4 |
|
5 |
use PhpSpec\ObjectBehavior; |
6 |
use Prophecy\Argument; |
7 |
|
8 |
class HelloWorldSpec extends ObjectBehavior |
9 |
{
|
10 |
function it_is_initializable() |
11 |
{
|
12 |
$this->shouldHaveType('Suhm\HelloWorld'); |
13 |
|
14 |
var_dump(get_class($this->object)); |
15 |
}
|
16 |
}
|
Und schauen Sie, was wir bekommen:
1 |
string(23) "PhpSpec\Wrapper\Subject" |
Mehr zum Subject
Subject
ist ein Wrapper und implementiert das PhpSpec\Wrapper\WrapperInterface
. Es ist ein Kernbestandteil von PhpSpec und ermöglicht alle [scheinbar] Magie, die das Framework leisten kann. Es umschließt eine Instanz der Klasse, die wir testen, sodass wir alle möglichen Dinge tun können, beispielsweise Methoden und Eigenschaften aufrufen, die nicht vorhanden sind, und Erwartungen festlegen.
Wie bereits erwähnt, ist PhpSpec sehr eigenwillig, wie Sie Ihren Code schreiben und spezifizieren sollten. Eine Spezifikation ist einer Klasse zugeordnet. Sie haben nur ein Thema pro Spezifikation, das PhpSpec sorgfältig für Sie verpackt. Das Wichtigste dabei ist, dass Sie $this
so verwenden können, als ob es die eigentliche Instanz wäre, und dass Sie wirklich lesbare und aussagekräftige Spezifikationen erhalten.
PhpSpec enthält einen Wrapper
, der sich um die Instanziierung des Subject
kümmert. Es packt das Subject
mit dem tatsächlichen Objekt, das wir spezifizieren. Da Subject
das WrapperInterface
implementiert, muss es eine getWrappedObject()
-Methode haben, die uns den Zugriff auf das Objekt ermöglicht. Dies ist die Objektinstanz, nach der wir zuvor mit get_class()
gesucht haben.
Versuchen wir es noch einmal:
1 |
<?php
|
2 |
|
3 |
namespace spec\Suhm; |
4 |
|
5 |
use PhpSpec\ObjectBehavior; |
6 |
use Prophecy\Argument; |
7 |
|
8 |
class HelloWorldSpec extends ObjectBehavior |
9 |
{
|
10 |
function it_is_initializable() |
11 |
{
|
12 |
$this->shouldHaveType('Suhm\HelloWorld'); |
13 |
|
14 |
var_dump(get_class($this->object->getWrappedObject())); |
15 |
|
16 |
// And just to be completely sure:
|
17 |
var_dump($this->object->getWrappedObject()->dumpThis()); |
18 |
}
|
19 |
}
|
Und los gehts:
1 |
$ vendor/bin/phpspec run
|
2 |
string(15) "Suhm\HelloWorld" |
3 |
string(15) "Suhm\HelloWorld" |
Auch wenn sich hinter den Kulissen einiges abspielt, arbeiten wir am Ende immer noch mit der eigentlichen Objektinstanz von Suhm\HelloWorld
. Alles ist gut.
Als wir zuvor $this->dumpThis()
aufgerufen haben, haben wir erfahren, wie der Aufruf tatsächlich an das Subject
weitergeleitet wurde. Wir haben auch gelernt, dass Subject
nur ein Wrapper und nicht das eigentliche Objekt ist.
Mit diesem Wissen ist klar, dass wir dumpThis()
für Subject
nicht ohne eine andere magische Methode aufrufen können. Subject
hat auch eine __call()
-Methode:
1 |
/**
|
2 |
* @param string $method
|
3 |
* @param array $arguments
|
4 |
*
|
5 |
* @return mixed|Subject
|
6 |
*/
|
7 |
public function __call($method, array $arguments = array()) |
8 |
{
|
9 |
if (0 === strpos($method, 'should')) { |
10 |
return $this->callExpectation($method, $arguments); |
11 |
}
|
12 |
|
13 |
return $this->caller->call($method, $arguments); |
14 |
}
|
Diese Methode führt eines von zwei Dingen aus. Zuerst wird geprüft, ob der Methodenname mit 'should' beginnt. Wenn dies der Fall ist, handelt es sich um eine Erwartung, und der Aufruf wird an eine Methode namens callExpectation()
delegiert. Wenn nicht, wird der Aufruf stattdessen an eine Instanz von PhpSpec\Wrapper\Subject\Caller
delegiert.
Wir ignorieren den Caller
jetzt. Er enthält auch das umschlossene Objekt und weiss, wie man Methoden darauf aufruft. Der Caller
gibt eine umschlossene Instanz zurück, wenn er Methoden zum Thema aufruft, was es uns ermöglicht, Erwartungen an Methoden zu verketten, wie wir es mit dumpThis()
getan haben.
Schauen wir uns stattdessen die Methode callExpectation()
an:
1 |
/**
|
2 |
* @param string $method
|
3 |
* @param array $arguments
|
4 |
*
|
5 |
* @return mixed
|
6 |
*/
|
7 |
private function callExpectation($method, array $arguments) |
8 |
{
|
9 |
$subject = $this->makeSureWeHaveASubject(); |
10 |
|
11 |
$expectation = $this->expectationFactory->create($method, $subject, $arguments); |
12 |
|
13 |
if (0 === strpos($method, 'shouldNot')) { |
14 |
return $expectation->match(lcfirst(substr($method, 9)), $this, $arguments, $this->wrappedObject); |
15 |
}
|
16 |
|
17 |
return $expectation->match(lcfirst(substr($method, 6)), $this, $arguments, $this->wrappedObject); |
18 |
}
|
Diese Methode ist für das Erstellen einer Instanz von PhpSpec\Wrapper\Subject\Expectation\ExpectationInterface
verantwortlich. Diese Schnittstelle diktiert eine Methode match()
, die callExpectation()
aufruft, um die Erwartung zu überprüfen. Es gibt vier verschiedene Arten von Erwartungen: Positive
, Negative
, PositiveThrow
und NegativeThrow
. Jede dieser Erwartungen enthält eine Instanz von PhpSpec\Matcher\MatcherInterface
, die von der Methode match()
verwendet wird. Schauen wir uns als nächstes Matcher an.
Matcher
Matcher sind das, was wir verwenden, um das Verhalten unserer Objekte zu bestimmen. Wann immer wir schreiben should...
oder shouldNot...
verwenden wir einen Matcher. Eine umfassende Liste von PhpSpec-Matchern finden Sie in meinem persönlichen Blog.
PhpSpec enthält viele Matcher, die alle die PhpSpec\Matcher\BasicMatcher
-Klasse erweitern, die das MatcherInterface
implementiert. Die Arbeitsweise von Matchern ist ziemlich einfach. Schauen wir uns das gemeinsam an und ich ermutige Sie, sich auch den Quellcode anzuschauen.
Schauen wir uns als Beispiel diesen Code aus dem IdentityMatcher
an:
1 |
/**
|
2 |
* @var array
|
3 |
*/
|
4 |
private static $keywords = array( |
5 |
'return', |
6 |
'be', |
7 |
'equal', |
8 |
'beEqualTo'
|
9 |
);
|
10 |
|
11 |
/**
|
12 |
* @param string $name
|
13 |
* @param mixed $subject
|
14 |
* @param array $arguments
|
15 |
*
|
16 |
* @return bool
|
17 |
*/
|
18 |
public function supports($name, $subject, array $arguments) |
19 |
{
|
20 |
return in_array($name, self::$keywords) |
21 |
&& 1 == count($arguments) |
22 |
;
|
23 |
}
|
Die Methode supports()
wird vom MatcherInterface
vorgegeben. In diesem Fall werden für den Matcher im Array $keywords
vier Aliase definiert. Dadurch kann der Matcher Folgendes unterstützen: shouldReturn()
, shouldBe()
, shouldEqual()
oder shouldBeEqualTo()
oder shouldNotReturn()
, shouldNotBe()
, shouldNotEqual()
oder shouldNotBeEqualTo()
.
Vom BasicMatcher
werden zwei Methoden geerbt: positiveMatch()
und negativeMatch()
. Sie sehen so aus:
1 |
/**
|
2 |
* @param string $name
|
3 |
* @param mixed $subject
|
4 |
* @param array $arguments
|
5 |
*
|
6 |
* @return mixed
|
7 |
*
|
8 |
* @throws FailureException
|
9 |
*/
|
10 |
final public function positiveMatch($name, $subject, array $arguments) |
11 |
{
|
12 |
if (false === $this->matches($subject, $arguments)) { |
13 |
throw $this->getFailureException($name, $subject, $arguments); |
14 |
}
|
15 |
|
16 |
return $subject; |
17 |
}
|
Die Methode positiveMatch()
löst eine Ausnahme aus, wenn die Methode matches()
(abstrakte Methode, die Matcher implementieren müssen) false
zurückgibt. Die Methode negativeMatch()
funktioniert umgekehrt. Die Methode matches()
für die IdentityMatcher
verwendet den Operator ===
, um das $subject
mit dem Argument zu vergleichen, das der Matcher-Methode übergeben wurde:
1 |
/**
|
2 |
* @param mixed $subject
|
3 |
* @param array $arguments
|
4 |
*
|
5 |
* @return bool
|
6 |
*/
|
7 |
protected function matches($subject, array $arguments) |
8 |
{
|
9 |
return $subject === $arguments[0]; |
10 |
}
|
Wir könnten den Matcher so verwenden:
1 |
$this->getUser()->shouldNotBeEqualTo($anotherUser); |
Was schließlich negativeMatch()
aufrufen und sicherstellen würde, dass match()
false zurückgibt.
Schauen Sie sich einige der anderen Matcher an und sehen Sie, was sie tun!
Versprechen von mehr Magie
Bevor wir diese kurze Tour durch die Interna von PhpSpec beenden, werfen wir einen Blick auf ein weiteres Stück Magie:
1 |
<?php
|
2 |
|
3 |
namespace spec\Suhm; |
4 |
|
5 |
use PhpSpec\ObjectBehavior; |
6 |
use Prophecy\Argument; |
7 |
|
8 |
class HelloWorldSpec extends ObjectBehavior |
9 |
{
|
10 |
function it_is_initializable(\StdClass $object) |
11 |
{
|
12 |
$this->shouldHaveType('Suhm\HelloWorld'); |
13 |
|
14 |
var_dump(get_class($object)); |
15 |
}
|
16 |
}
|
Indem wir unserem Beispiel den $object
-Parameter mit Typhingabe hinzufügen, verwendet PhpSpec automatisch Reflection, um eine Instanz der Klasse zu injizieren, die wir verwenden können. Aber vertrauen wir bei dem, was wir bereits gesehen haben, wirklich darauf, dass wir wirklich eine Instanz von StdClass
bekommen? Lassen Sie uns get_class()
noch einmal konsultieren:
1 |
$ vendor/bin/phpspec run
|
2 |
string(28) "PhpSpec\Wrapper\Collaborator" |
Nee. Anstelle von StdClass
erhalten wir eine Instanz von PhpSpec\Wrapper\Collaborator
. Um was geht es hierbei?
Collaborator
ist wie Subject
ein Wrapper und implementiert das WrapperInterface
. Es umschließt eine Instanz von \Prophecy\Prophecy\ObjectProphecy
, die von Prophecy abstammt, dem Mocking-Framework, das zusammen mit PhpSpec geliefert wird. Anstelle einer StdClass
-Instanz gibt uns PhpSpec einen Mock. Das macht das Spotten mit PhpSpec lächerlich einfach und ermöglicht es uns, unseren Objekten Versprechen wie folgt hinzuzufügen:
1 |
$user->getAge()->willReturn(10); |
2 |
|
3 |
$this->setUser($user); |
4 |
$this->getUserStatus()->shouldReturn('child'); |
Mit dieser kurzen Tour durch Teile der Interna von PhpSpec hoffe ich, dass Sie sehen, dass es sich um mehr als ein einfaches Test-Framework handelt.
Der Unterschied zwischen TDD und BDD
PhpSpec ist ein Tool zum Ausführen von SpecBDD. Um ein besseres Verständnis zu erhalten, werfen wir einen Blick auf die Unterschiede zwischen Test Driven Development (TDD) und Behavior Driven Development (BDD). Anschließend werfen wir einen kurzen Blick darauf, wie sich PhpSpec von anderen Tools wie PHPUnit unterscheidet.
TDD ist das Konzept, bei dem automatisierte Tests das Design und die Implementierung von Code steuern. Indem wir kleine Tests für jede Funktion schreiben, bevor wir sie tatsächlich implementieren, wissen wir, dass unser Code diese spezifische Funktion erfüllt, wenn wir einen bestandenen Test erhalten. Bei einem bestandenen Test beenden wir nach dem Refactoring die Codierung und schreiben stattdessen den nächsten Test. Das Mantra ist "rot", "grün", "refactor"!
BDD hat seinen Ursprung - und ist diesem sehr ähnlich - TDD. Ehrlich gesagt ist es vor allem eine Frage der Formulierung, die in der Tat wichtig ist, da sie unser Denken als Entwickler verändern kann. Während TDD über Testen spricht, spricht BDD über die Beschreibung von Verhalten.
Bei TDD konzentrieren wir uns darauf, zu überprüfen, ob unser Code so funktioniert, wie wir es erwarten, während wir uns bei BDD darauf konzentrieren, zu überprüfen, ob sich unser Code tatsächlich so verhält, wie wir es wollen. Ein Hauptgrund für das Aufkommen von BDD als Alternative zu TDD besteht darin, das Wort "Test" zu vermeiden. Bei BDD sind wir nicht wirklich daran interessiert, die Implementierung unseres Codes zu testen, wir sind mehr daran interessiert, zu testen, was er tut (sein Verhalten). Wenn wir BDD statt TDD machen, haben wir Geschichten und Spezifikationen. Dies macht das Schreiben herkömmlicher Tests überflüssig.
Geschichten und Spezifikationen sind eng mit den Erwartungen der Projektbeteiligten verbunden. Das Schreiben von Geschichten (mit einem Tool wie Behat) würde vorzugsweise zusammen mit den Stakeholdern oder Domänenexperten erfolgen. Die Geschichten decken das äußere Verhalten ab. Wir verwenden Spezifikationen, um das interne Verhalten zu entwerfen, das erforderlich ist, um die Schritte der Storys zu erfüllen. Jeder Schritt in einer Story kann mehrere Iterationen mit Schreiben von Spezifikationen und Implementieren von Code erfordern, bevor er erfüllt ist. Unsere Geschichten helfen uns zusammen mit unseren Spezifikationen, sicherzustellen, dass wir nicht nur ein funktionierendes, sondern auch das Richtige bauen. BDD hat also viel mit Kommunikation zu tun.
Wie unterscheidet sich PHPSpec von PHPUnit?
Vor einigen Monaten postete ein namhaftes Mitglied der PHP-Community, Mathias Verraes, auf Twitter "Ein Unit-Testing-Framework in einem Tweet". Es ging darum, den Quellcode eines funktionalen Unit-Testing-Frameworks in einen einzigen Tweet zu integrieren. Wie Sie im Wesentlichen sehen können, ist der Code wirklich funktional und ermöglicht es Ihnen, grundlegende Komponententests zu schreiben. Das Konzept des Unit-Tests ist eigentlich ziemlich einfach: Überprüfen Sie eine Art von Assertion und benachrichtigen Sie den Benutzer über das Ergebnis.
Natürlich sind die meisten Test-Frameworks, wie PHPUnit, tatsächlich viel fortgeschrittener und können viel mehr als das Framework von Mathias, aber es zeigt immer noch einen wichtigen Punkt: Sie behaupten etwas und dann führt Ihr Framework diese Assertion für Sie aus.
Schauen wir uns einen sehr einfachen PHPUnit-Test an:
1 |
public function testTrue() |
2 |
{
|
3 |
$this->assertTrue(false); |
4 |
}
|
Wären Sie in der Lage, eine supereinfache Implementierung eines Testframeworks zu schreiben, das diesen Test ausführen könnte? Ich bin mir ziemlich sicher, dass die Antwort "ja" ist, das könnten Sie tun. Schließlich muss die Methode assertTrue()
nur einen Wert mit true
vergleichen und eine Ausnahme auslösen, wenn sie fehlschlägt. Im Kern ist das, was vor sich geht, eigentlich ziemlich geradlinig.
Wie unterscheidet sich PHPSpec? Zunächst einmal ist PhpSpec kein Testwerkzeug. Das Testen Ihres Codes ist nicht das Hauptziel von PhpSpec, aber es wird ein Nebeneffekt, wenn Sie es verwenden, um Ihre Software zu entwerfen, indem Sie inkrementell Spezifikationen für das Verhalten (BDD) hinzufügen.
Zweitens denke ich, dass die obigen Abschnitte bereits deutlich gemacht haben sollten, wie sich PhpSpec unterscheidet. Lassen Sie uns dennoch etwas Code vergleichen:
1 |
// PhpSpec
|
2 |
function it_is_initializable() |
3 |
{
|
4 |
$this->shouldHaveType('Suhm\HelloWorld'); |
5 |
}
|
6 |
|
7 |
// PHPUnit
|
8 |
function testIsInitializable() |
9 |
{
|
10 |
$object = new Suhm\HelloWorld(); |
11 |
|
12 |
$this->assertInstanceOf('Suhm\HelloWorld', $object); |
13 |
}
|
Da PhpSpec sehr eigensinnig ist und einige Aussagen darüber macht, wie unser Code gestaltet ist, bietet es uns eine sehr einfache Möglichkeit, unseren Code zu beschreiben. Andererseits macht PHPUnit keine Aussagen zu unserem Code und lässt uns so ziemlich machen, was wir wollen. Im Grunde alles, was PHPUnit in diesem Beispiel für uns tut, ist $object
gegen die instanceof
Operators auszuführen.
Auch wenn der Einstieg mit PHPUnit einfacher zu sein scheint (ich glaube nicht), dass Sie, wenn Sie nicht aufpassen, leicht in die Fallen von schlechtem Design und schlechter Architektur tappen, da Sie fast alles tun können. Davon abgesehen kann PHPUnit für viele Anwendungsfälle immer noch großartig sein, aber es ist kein Design-Tool wie PhpSpec. Es gibt keine Anleitung - Sie müssen wissen, was Sie tun.
PhpSpec: Ein Design-Tool
Auf der PhpSpec-Website können wir erfahren, dass PhpSpec:
Ein PHP-Toolset, um aufstrebendes Design nach Spezifikation voranzutreiben.
Lassen Sie es mich noch einmal sagen: PhpSpec ist kein Test-Framework. Es ist ein Entwicklungstool. Ein Software-Design-Tool. Es ist kein einfaches Assertion-Framework, das Werte vergleicht und Ausnahmen auslöst. Es ist ein Werkzeug, das uns beim Entwerfen und Erstellen von gut ausgearbeitetem Code unterstützt. Es erfordert, dass wir über die Struktur unseres Codes nachdenken und bestimmte Architekturmuster erzwingen, bei denen eine Klasse einer Spezifikation zugeordnet ist. Wenn Sie das Einzelverantwortungsprinzip brechen und etwas teilweise verspotten müssen, dürfen Sie dies nicht tun.
Viel Spaß beim Spec'ing!
Oh! Und schließlich, = da PhpSpec selbst spezifiziert ist, schlage ich vor, dass Sie zu GitHub gehen und die Quelle erkunden, um mehr zu erfahren.