Skip to content

Commit 81c7c62

Browse files
authored
Merge pull request #18 from JMLamodiere/mock_secondary_ports_in_behat
Mock secondary (hexagonal) ports in behat (PHP's cucumber)
2 parents 83b6885 + fde27c7 commit 81c7c62

File tree

8 files changed

+76
-44
lines changed

8 files changed

+76
-44
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ branches:
3030
- integration_infra_medium_domain_wiremock
3131
- integration_infra_medium_domain_no_di
3232
- integration_infra_sociable
33+
- mock_secondary_ports_in_behat
3334

3435
before_install:
3536
- make preinstall

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,12 @@ decoupling HTTP tests with the library being used for HTTP calls.
3535
replaces medium sized tests with [Overlapping Sociable Tests](https://www.jamesshore.com/v2/blog/2018/testing-without-mocks#sociable-tests)
3636
to allow easily test and evolve individual behaviours (ex : class serialization) while still being able to
3737
split/merge/refactor classes inside some class clusters by not checking specific calls between them.
38-
1. (todo...)
38+
1. [mock_secondary_ports_in_behat](https://github.com/JMLamodiere/tdd-demo-forumphp2020/tree/mock_secondary_ports_in_behat) branch
39+
[(see Pull Request)](https://github.com/JMLamodiere/tdd-demo-forumphp2020/pull/18): Mock Secondary Ports
40+
(according to [Hexagonal architecture](https://alistair.cockburn.us/hexagonal-architecture/)) in
41+
[Behat](https://behat.org). Makes behat tests much faster and
42+
easier to write. Pre-requisite : well defined secondary ports and Integration tests on their
43+
Infrastructure layer implementation.
3944

4045
## API documentation
4146

behat.yml.dist

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ default:
44
contexts:
55
- FeatureContext:
66
kernel: '@kernel'
7-
dbal: '@doctrine.dbal.default_connection'
8-
wireMock: '@WireMock\Client\WireMock'
9-
accuweatherApiKey: '%%accuweather.apikey%%'
107

118
extensions:
129
Behat\Symfony2Extension:

config/packages/test/services.yaml

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
1-
parameters:
2-
# See .env.test
3-
accuweather.apikey: '%env(ACCUWEATHER_API_KEY)%'
4-
51
services:
62
# default configuration for services in *this* file
73
_defaults:

config/services.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ services:
3232
App\Application\Command\RegisterRunningSessionHandler:
3333
public: true
3434

35+
# Mark Secondary Ports (Hexagonal Architecture meaning) public to allow mocking them in behat tests
36+
App\Domain\WeatherProvider:
37+
public: true
38+
App\Domain\RunningSessionRepository:
39+
public: true
40+
3541
guzzle.accuweather:
3642
class: GuzzleHttp\Client
3743
arguments:
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Prophecy\Prophecy\ObjectProphecy;
6+
use Prophecy\Prophet;
7+
8+
trait BehatProphecyTrait
9+
{
10+
private ?Prophet $prophet = null;
11+
12+
protected function prophesize(?string $classOrInterface = null): ObjectProphecy
13+
{
14+
if (null === $this->prophet) {
15+
$this->prophet = new Prophet();
16+
}
17+
18+
return $this->prophet->prophesize($classOrInterface);
19+
}
20+
21+
/**
22+
* @AfterScenario
23+
*/
24+
public function verifyProphecyDoubles(): void
25+
{
26+
if (null === $this->prophet) {
27+
return;
28+
}
29+
30+
$this->prophet->checkPredictions();
31+
}
32+
}
Lines changed: 28 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,55 +1,46 @@
11
<?php
22

3-
use App\Infrastructure\Database\PostgresRunningSessionRepository;
4-
use App\Infrastructure\Database\PostgresRunningSessionRepositoryTest;
5-
use App\Infrastructure\Http\CurrentConditionDeserializerTest;
3+
use App\Domain\RunningSessionFactory;
4+
use App\Domain\RunningSessionRepository;
5+
use App\Domain\WeatherProvider;
66
use App\Infrastructure\Symfony\Serializer\RegisterRunningSessionDeserializerTest;
77
use Behat\Behat\Context\Context;
8-
use Doctrine\DBAL\Connection;
98
use PHPUnit\Framework\Assert;
9+
use Prophecy\Prophecy\ObjectProphecy;
1010
use Symfony\Component\HttpFoundation\Request;
1111
use Symfony\Component\HttpFoundation\Response;
1212
use Symfony\Component\HttpKernel\KernelInterface;
13-
use WireMock\Client\WireMock;
1413

1514
class FeatureContext implements Context
1615
{
16+
use BehatProphecyTrait;
17+
1718
private KernelInterface $kernel;
18-
private Connection $dbal;
19-
private WireMock $wireMock;
2019
private ?Response $response;
21-
private string $accuweatherApiKey;
20+
/** @var ObjectProphecy|WeatherProvider */
21+
private $weatherProvider;
22+
/** @var ObjectProphecy|RunningSessionRepository */
23+
private $runningSessionRepository;
2224

23-
public function __construct(KernelInterface $kernel, Connection $dbal, WireMock $wireMock, string $accuweatherApiKey)
25+
public function __construct(KernelInterface $kernel)
2426
{
2527
$this->kernel = $kernel;
26-
$this->wireMock = $wireMock;
27-
Assert::assertTrue($this->wireMock->isAlive(), 'Wiremock should be alive');
28-
$this->dbal = $dbal;
29-
$this->accuweatherApiKey = $accuweatherApiKey;
30-
}
3128

32-
/**
33-
* @BeforeScenario
34-
*/
35-
public function resetState()
36-
{
37-
$this->wireMock->reset();
38-
$this->dbal->executeStatement('TRUNCATE TABLE '.PostgresRunningSessionRepository::TABLE_NAME);
29+
$this->weatherProvider = $this->prophesize(WeatherProvider::class);
30+
$kernel->getContainer()->set(WeatherProvider::class, $this->weatherProvider->reveal());
31+
32+
$this->runningSessionRepository = $this->prophesize(RunningSessionRepository::class);
33+
$kernel->getContainer()->set(RunningSessionRepository::class, $this->runningSessionRepository->reveal());
3934
}
4035

4136
/**
4237
* @Given current temperature is :temperature celcius degrees
4338
*/
4439
public function currentTemperatureIs($temperature)
4540
{
46-
$uri = '/currentconditions/v1/623?apikey='.$this->accuweatherApiKey;
47-
$body = CurrentConditionDeserializerTest::createBody($temperature);
48-
49-
$this->wireMock->stubFor(WireMock::get(WireMock::urlEqualTo($uri))
50-
->willReturn(WireMock::aResponse()
51-
->withHeader('Content-Type', 'application/json')
52-
->withBody($body)));
41+
$this->weatherProvider
42+
->getCurrentCelciusTemperature()
43+
->willReturn($temperature);
5344
}
5445

5546
/**
@@ -69,10 +60,14 @@ public function iRegisterARunningSessionWith($id, $distance, $shoes)
6960
public function aRunningSessionShouldBeAddedWith($id, $distance, $shoes, $temperature)
7061
{
7162
Assert::assertEquals(201, $this->response->getStatusCode());
72-
PostgresRunningSessionRepositoryTest::thenRunningSessionTableShouldContain($this->dbal, $id, [
73-
'distance' => $distance,
74-
'shoes' => $shoes,
75-
'temperature_celcius' => $temperature,
76-
]);
63+
64+
$this->runningSessionRepository
65+
->add(RunningSessionFactory::create(
66+
$id,
67+
$distance,
68+
$shoes,
69+
$temperature
70+
))
71+
->shouldHaveBeenCalled();
7772
}
7873
}

tests/Infrastructure/Database/PostgresRunningSessionRepositoryTest.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ public function testRunningSessionIsInserted()
4343
$this->repository->add($session);
4444

4545
//Then (Assert)
46-
self::thenRunningSessionTableShouldContain($this->dbal, 55, [
46+
self::thenRunningSessionTableShouldContain(55, [
4747
'distance' => 122.3,
4848
'shoes' => 'The shoes!',
4949
'temperature_celcius' => 34.5,
@@ -54,9 +54,9 @@ public function testRunningSessionIsInserted()
5454
* @throws \Doctrine\DBAL\Exception
5555
* @throws ExpectationFailedException
5656
*/
57-
public static function thenRunningSessionTableShouldContain(Connection $dbal, int $id, array $expectedArray): void
57+
private function thenRunningSessionTableShouldContain(int $id, array $expectedArray): void
5858
{
59-
$row = $dbal->fetchAssociative(
59+
$row = $this->dbal->fetchAssociative(
6060
'SELECT distance, shoes, temperature_celcius '
6161
.' FROM RUNNING_SESSION'
6262
.' WHERE ID = :id', [':id' => $id]);

0 commit comments

Comments
 (0)