Skip to content
This repository was archived by the owner on Dec 11, 2023. It is now read-only.

Commit efebedc

Browse files
committed
Add behat
1 parent d9a5a46 commit efebedc

20 files changed

+1764
-217
lines changed

apps/mooc/backend/config/bundles.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
<?php
22

33
return [
4-
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
4+
Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
5+
FriendsOfBehat\SymfonyExtension\Bundle\FriendsOfBehatSymfonyExtensionBundle::class => ['test' => true],
56
];
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
framework:
2+
test: true
3+
4+
services:
5+
_defaults:
6+
autoconfigure: true
7+
autowire: true
8+
9+
CodelyTv\Tests\:
10+
resource: '../../../../tests/src'
11+
12+
CodelyTv\Shared\Domain\RandomNumberGenerator: '@CodelyTv\Tests\Shared\Infrastructure\ConstantRandomNumberGenerator'

apps/mooc/backend/src/Controller/HealthCheck/HealthCheckGetController.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
namespace CodelyTv\Apps\Mooc\Backend\Controller\HealthCheck;
66

7-
use CodelyTv\Shared\Infrastructure\RandomNumberGenerator;
7+
use CodelyTv\Shared\Domain\RandomNumberGenerator;
88
use Symfony\Component\HttpFoundation\JsonResponse;
99
use Symfony\Component\HttpFoundation\Request;
1010
use Symfony\Component\HttpFoundation\Response;

apps/mooc/backend/src/MoocBackendKernel.php

+4-1
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,10 @@ protected function configureContainer(ContainerBuilder $container, LoaderInterfa
3838
$container->setParameter('container.dumper.inline_class_loader', true);
3939
$confDir = $this->getProjectDir().'/config';
4040

41-
$loader->load($confDir.'/*'.self::CONFIG_EXTS, 'glob');
41+
$loader->load($confDir.'/services'.self::CONFIG_EXTS, 'glob');
42+
$loader->load($confDir.'/services_'.$this->environment.self::CONFIG_EXTS, 'glob');
43+
$loader->load($confDir.'/services/*'.self::CONFIG_EXTS, 'glob');
44+
4245
}
4346

4447
protected function configureRoutes(RouteCollectionBuilder $routes): void

behat.yml

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
imports:
2+
- tests/apps/mooc/backend/mooc_backend.yml

composer.json

+9-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,19 @@
1313

1414
"symfony/framework-bundle": "^4.3",
1515
"symfony/dotenv": "^4.3",
16-
"symfony/yaml": "^4.3"
16+
"symfony/yaml": "^4.3",
17+
18+
"lambdish/phunctional": "^1.0"
1719
},
1820
"require-dev": {
1921
"ext-xdebug": "*",
2022

21-
"roave/security-advisories": "dev-master"
23+
"roave/security-advisories": "dev-master",
24+
25+
"behat/behat": "^3.5",
26+
"behat/mink-extension": "^2.3",
27+
"behat/mink-browserkit-driver": "^1.3",
28+
"friends-of-behat/symfony-extension": "^2.0"
2229
},
2330
"autoload": {
2431
"psr-4": {

composer.lock

+1,418-198
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/Shared/Domain/.gitkeep

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Shared\Domain;
6+
7+
interface RandomNumberGenerator
8+
{
9+
public function generate(): int;
10+
}

src/Shared/Infrastructure/RandomNumberGenerator.php renamed to src/Shared/Infrastructure/PhpRandomNumberGenerator.php

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
namespace CodelyTv\Shared\Infrastructure;
66

7-
final class RandomNumberGenerator
7+
use CodelyTv\Shared\Domain\RandomNumberGenerator;
8+
9+
final class PhpRandomNumberGenerator implements RandomNumberGenerator
810
{
911
public function generate(): int
1012
{

tests/apps/mooc/backend/.gitkeep

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
Feature: Api status
2+
In order to know the server is up and running
3+
As a health check
4+
I want to check the api status
5+
6+
Scenario: Check the api status
7+
Given I send a GET request to "/health-check"
8+
Then the response content should be:
9+
"""
10+
{
11+
"mooc-backend": "ok",
12+
"rand": 1
13+
}
14+
"""
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
mooc_backend:
2+
extensions:
3+
FriendsOfBehat\SymfonyExtension:
4+
kernel:
5+
class: CodelyTv\Apps\Mooc\Backend\MoocBackendKernel
6+
bootstrap: apps/mooc/bootstrap.php
7+
Behat\MinkExtension:
8+
sessions:
9+
symfony:
10+
symfony: ~
11+
base_url: ''
12+
13+
suites:
14+
health_check:
15+
paths: [ tests/apps/mooc/backend/features/health_check ]
16+
contexts:
17+
- CodelyTv\Tests\Shared\Infrastructure\Behat\ApiRequestContext
18+
- CodelyTv\Tests\Shared\Infrastructure\Behat\ApiResponseContext
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Tests\Shared\Infrastructure\Behat;
6+
7+
use Behat\Gherkin\Node\PyStringNode;
8+
use Behat\Mink\Session;
9+
use Behat\MinkExtension\Context\RawMinkContext;
10+
use CodelyTv\Tests\Shared\Infrastructure\Mink\MinkHelper;
11+
use CodelyTv\Tests\Shared\Infrastructure\Mink\MinkSessionRequestHelper;
12+
13+
final class ApiRequestContext extends RawMinkContext
14+
{
15+
private $request;
16+
17+
public function __construct(Session $session)
18+
{
19+
$this->request = new MinkSessionRequestHelper(new MinkHelper($session));
20+
}
21+
22+
/**
23+
* @Given I send a :method request to :url
24+
*/
25+
public function iSendARequestTo($method, $url): void
26+
{
27+
$this->request->sendRequest($method, $this->locatePath($url));
28+
}
29+
30+
/**
31+
* @Given I send a :method request to :url with body:
32+
*/
33+
public function iSendARequestToWithBody($method, $url, PyStringNode $body): void
34+
{
35+
$this->request->sendRequestWithPyStringNode($method, $this->locatePath($url), $body);
36+
}
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Tests\Shared\Infrastructure\Behat;
6+
7+
use Behat\Gherkin\Node\PyStringNode;
8+
use Behat\Mink\Session;
9+
use Behat\MinkExtension\Context\RawMinkContext;
10+
use CodelyTv\Tests\Shared\Infrastructure\Mink\MinkHelper;
11+
use RuntimeException;
12+
13+
final class ApiResponseContext extends RawMinkContext
14+
{
15+
private $sessionHelper;
16+
private $minkSession;
17+
18+
public function __construct(Session $minkSession)
19+
{
20+
$this->minkSession = $minkSession;
21+
$this->sessionHelper = new MinkHelper($this->minkSession);
22+
}
23+
24+
/**
25+
* @Then the response content should be:
26+
*/
27+
public function theResponseContentShouldBe(PyStringNode $expectedResponse): void
28+
{
29+
$expected = $this->sanitizeOutput($expectedResponse->getRaw());
30+
$actual = $this->sanitizeOutput($this->sessionHelper->getResponse());
31+
32+
if ($expected !== $actual) {
33+
throw new RuntimeException(
34+
sprintf("The outputs does not match!\n\n-- Expected:\n%s\n\n-- Actual:\n%s", $expected, $actual)
35+
);
36+
}
37+
}
38+
39+
/**
40+
* @Then the response should be empty
41+
*/
42+
public function theResponseShouldBeEmpty(): void
43+
{
44+
$actual = trim($this->sessionHelper->getResponse());
45+
46+
if (!empty($actual)) {
47+
throw new RuntimeException(
48+
sprintf("The outputs is not empty, Actual:\n%s", $actual)
49+
);
50+
}
51+
}
52+
53+
/**
54+
* @Then print last api response
55+
*/
56+
public function printApiResponse(): void
57+
{
58+
print_r($this->sessionHelper->getResponse());
59+
}
60+
61+
/**
62+
* @Then print response headers
63+
*/
64+
public function printResponseHeaders(): void
65+
{
66+
print_r($this->sessionHelper->getResponseHeaders());
67+
}
68+
69+
/**
70+
* @Then the response status code should be :expectedResponseCode
71+
*/
72+
public function theResponseStatusCodeShouldBe($expectedResponseCode): void
73+
{
74+
if ($this->minkSession->getStatusCode() !== (int) $expectedResponseCode) {
75+
throw new RuntimeException(
76+
sprintf(
77+
'The status code <%s> does not match the expected <%s>',
78+
$this->minkSession->getStatusCode(),
79+
$expectedResponseCode
80+
)
81+
);
82+
}
83+
}
84+
85+
private function sanitizeOutput(string $output)
86+
{
87+
return json_encode(json_decode(trim($output), true));
88+
}
89+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Tests\Shared\Infrastructure;
6+
7+
use CodelyTv\Shared\Domain\RandomNumberGenerator;
8+
9+
final class ConstantRandomNumberGenerator implements RandomNumberGenerator
10+
{
11+
public function generate(): int
12+
{
13+
return 1;
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Tests\Shared\Infrastructure\Mink;
6+
7+
use Behat\Mink\Driver\DriverInterface;
8+
use Behat\Mink\Session;
9+
use Symfony\Component\BrowserKit\AbstractBrowser;
10+
use Symfony\Component\DomCrawler\Crawler;
11+
use Symfony\Component\HttpFoundation\Request;
12+
13+
final class MinkHelper
14+
{
15+
private $session;
16+
17+
public function __construct(Session $session)
18+
{
19+
$this->session = $session;
20+
}
21+
22+
public function sendRequest($method, $url, array $optionalParams = []): Crawler
23+
{
24+
$defaultOptionalParams = [
25+
'parameters' => [],
26+
'files' => [],
27+
'server' => ['HTTP_ACCEPT' => 'application/json', 'CONTENT_TYPE' => 'application/json'],
28+
'content' => null,
29+
'changeHistory' => true,
30+
];
31+
32+
$optionalParams = array_merge($defaultOptionalParams, $optionalParams);
33+
34+
$crawler = $this->getClient()->request(
35+
$method,
36+
$url,
37+
$optionalParams['parameters'],
38+
$optionalParams['files'],
39+
$optionalParams['server'],
40+
$optionalParams['content'],
41+
$optionalParams['changeHistory']
42+
);
43+
44+
$this->resetRequestStuff();
45+
46+
return $crawler;
47+
}
48+
49+
public function getResponse(): string
50+
{
51+
return $this->getSession()->getPage()->getContent();
52+
}
53+
54+
public function getResponseHeaders(): array
55+
{
56+
return $this->normalizeHeaders(
57+
array_change_key_case($this->getSession()->getResponseHeaders(), CASE_LOWER)
58+
);
59+
}
60+
61+
public function resetServerParameters(): void
62+
{
63+
$this->getClient()->setServerParameters([]);
64+
}
65+
66+
public function getRequest(): Request
67+
{
68+
return $this->getClient()->getRequest();
69+
}
70+
71+
private function getSession(): Session
72+
{
73+
return $this->session;
74+
}
75+
76+
private function getDriver(): DriverInterface
77+
{
78+
return $this->getSession()->getDriver();
79+
}
80+
81+
private function getClient(): AbstractBrowser
82+
{
83+
return $this->getDriver()->getClient();
84+
}
85+
86+
private function normalizeHeaders(array $headers): array
87+
{
88+
return array_map('implode', array_filter($headers));
89+
}
90+
91+
private function resetRequestStuff(): void
92+
{
93+
$this->getSession()->reset();
94+
$this->resetServerParameters();
95+
}
96+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types = 1);
4+
5+
namespace CodelyTv\Tests\Shared\Infrastructure\Mink;
6+
7+
use Behat\Gherkin\Node\PyStringNode;
8+
use Symfony\Component\DomCrawler\Crawler;
9+
10+
final class MinkSessionRequestHelper
11+
{
12+
/** @var MinkHelper */
13+
private $sessionHelper;
14+
15+
public function __construct($sessionHelper)
16+
{
17+
$this->sessionHelper = $sessionHelper;
18+
}
19+
20+
public function sendRequest($method, $url, array $optionalParams = []): void
21+
{
22+
$this->request($method, $url, $optionalParams);
23+
}
24+
25+
public function sendRequestWithPyStringNode($method, $url, PyStringNode $body): void
26+
{
27+
$this->request($method, $url, ['content' => $body->getRaw()]);
28+
}
29+
30+
public function request($method, $url, array $optionalParams = []): Crawler
31+
{
32+
return $this->sessionHelper->sendRequest($method, $url, $optionalParams);
33+
}
34+
}

0 commit comments

Comments
 (0)