Skip to content

Commit 4d67cab

Browse files
LGouttefangemcustiel
authored andcommitted
Added unique expectations loaded via annotations (#32)
* Added unique expectations loaded via annotations * Fixed scrutinizer comments
1 parent bd6b1c3 commit 4d67cab

File tree

10 files changed

+243
-2
lines changed

10 files changed

+243
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,4 @@ composer.phar
44
# Commit your application's lock file http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file
55
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
66
composer.lock
7+
.idea

README.md

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ extensions:
4242
logs_path: /var/log/my_app/tests/logs # defaults to codeception's tests output dir
4343
debug: true # defaults to false
4444
startDelay: 1 # default to 0
45-
expectations_path: /my/expectations/path
45+
expectations_path: /my/expectations/path # defaults to tests/_expectations
46+
unique_expectations_path: /my/expectations/path # defaults to tests/_unique_expectations
4647
```
4748
Note: Since Codeception version 2.2.7, extensions configuration can be added directly in the suite configuration file. That will avoid phiremock to be started for every suite.
4849
@@ -60,6 +61,7 @@ $loader = require APP_ROOT . '/vendor/autoload.php';
6061
* **debug:** Where to write debug data to log files
6162
* **startDelay:** Time to wait after Phiremock was started to start running the tests (used to give time to Phiremock to boot)
6263
* **expectations_path:** Specifies a directory to search for json files defining expectations to load by default.
64+
* **unique_expectations_path:** Specifies a directory to search for json files defining expectations to load via annotations.
6365

6466
### Module
6567
The module allows you to connect to a Phiremock server and to interact with it in a semantic way through the codeception actor in your tests.
@@ -134,6 +136,32 @@ Retrieves all the requests received by Phiremock server matching the one specifi
134136
$I->grabRequestsMadeToRemoteService(A::getRequest()->andUrl(Is::equalTo('/some/url')));
135137
```
136138

139+
#### @expectation Annotations
140+
141+
Allows you to to set up an expectation via a json file
142+
143+
```php
144+
/**
145+
* @expectation("get_client_timeout")
146+
*/
147+
public function test(FunctionalTester $I)
148+
{
149+
...
150+
}
151+
```
152+
153+
That will load by default the file at `tests/_unique_expectations/get_client_timeout.json`
154+
155+
Multiple annotation formats are accepted
156+
157+
```
158+
* @expectation get_client_timeout
159+
* @expectation get_client_timeout.json
160+
* @expectation(get_client_timeout.json)
161+
* @expectation(get_client_timeout)
162+
* @expectation("get_client_timeout")
163+
```
164+
137165
## Use case
138166

139167
### Yii2-Curl

src/Module/Phiremock.php

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
use Codeception\Module as CodeceptionModule;
2222
use Codeception\TestInterface;
23+
use Codeception\Util\ExpectationAnnotationParser;
24+
use GuzzleHttp\Client;
2325
use Mcustiel\Phiremock\Client\Phiremock as PhiremockClient;
2426
use Mcustiel\Phiremock\Client\Utils\RequestBuilder;
2527
use Mcustiel\Phiremock\Domain\Expectation;
@@ -51,6 +53,21 @@ public function _before(TestInterface $test)
5153
if ($this->config['resetBeforeEachTest']) {
5254
$this->haveACleanSetupInRemoteService();
5355
}
56+
$expectations = (new ExpectationAnnotationParser())->getExpectations($test);
57+
if(!empty($expectations)){
58+
$client = new Client([
59+
'base_uri' => "{$this->config['host']}:{$this->config['port']}",
60+
]);
61+
foreach ($expectations as $expectation){
62+
63+
$client->post(PhiremockClient::API_EXPECTATIONS_URL, [
64+
'headers' => [
65+
'Content-Type' => 'application/json'
66+
],
67+
'body' => file_get_contents($expectation),
68+
]);
69+
}
70+
}
5471
parent::_before($test);
5572
}
5673

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
<?php
2+
3+
4+
namespace Codeception\Util;
5+
6+
7+
use Codeception\Configuration;
8+
use Codeception\Exception\ParseException;
9+
use Codeception\Test\Cest;
10+
use Codeception\TestInterface;
11+
12+
class ExpectationAnnotationParser
13+
{
14+
const DEFAULT_EXPECTATIONS_PATH = "tests/_unique_expectations/";
15+
const PATH_CONFIG_KEY = 'unique_expectations_path';
16+
/**
17+
* @var array
18+
*/
19+
private $config;
20+
21+
/**
22+
* ExpectationAnnotationParser constructor.
23+
*
24+
* @param array $config
25+
*
26+
* @throws \Codeception\Exception\ConfigurationException
27+
*/
28+
public function __construct($config = [])
29+
{
30+
if (empty($config)) {
31+
$config = Configuration::config();
32+
}
33+
$this->config = $config;
34+
}
35+
36+
37+
/**
38+
* @param TestInterface|Cest $test
39+
*
40+
* @return array
41+
*/
42+
public function getExpectations(TestInterface $test)
43+
{
44+
if (!$test instanceof Cest) {
45+
return [];
46+
}
47+
$expectations = Annotation::forMethod($test->getTestClass(), $test->getTestMethod())->fetchAll('expectation');
48+
49+
return array_map([$this, 'parseExpectation'], $expectations);
50+
}
51+
52+
/**
53+
* @param $expectationsPath
54+
*
55+
* @return string
56+
*/
57+
protected function getExpectationFullPath($path)
58+
{
59+
$expectationsPath = isset($this->config['paths'][self::PATH_CONFIG_KEY]) ? $this->config['paths'][self::PATH_CONFIG_KEY] : self::DEFAULT_EXPECTATIONS_PATH;
60+
61+
return codecept_root_dir($expectationsPath.$path);
62+
}
63+
64+
/**
65+
* @param $expectation
66+
*
67+
* @return string
68+
* @throws ParseException
69+
*/
70+
public function parseExpectation($expectation)
71+
{
72+
$matches = [];
73+
$expectationRegex = '/\(?\"?(?<filePath>[a-zA-Z0-9_]+)(.json)?\"?\)?/';
74+
preg_match($expectationRegex, $expectation, $matches);
75+
76+
if (empty($matches)) {
77+
throw new ParseException("The 'expectation' annotation could not be parsed (found: '$expectation')");
78+
}
79+
80+
$expectationPath = $this->getExpectationFullPath("{$matches['filePath']}.json");
81+
if (!file_exists($expectationPath)) {
82+
throw new ParseException("The expectation at $expectationPath could not be found ");
83+
}
84+
85+
return $expectationPath;
86+
}
87+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"request": {
3+
"method": "GET",
4+
"url": {
5+
"isEqualTo" : "/expectation/1"
6+
}
7+
},
8+
"response": {
9+
"statusCode": 200,
10+
"body": "response",
11+
"headers": {
12+
"Content-Type": "application/json"
13+
}
14+
}
15+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"request": {
3+
"method": "GET",
4+
"url": {
5+
"isEqualTo" : "/expectation/2"
6+
}
7+
},
8+
"response": {
9+
"statusCode": 200,
10+
"body": {
11+
"id": "id"
12+
},
13+
"headers": {
14+
"Content-Type": "application/json"
15+
}
16+
}
17+
}

tests/tests/acceptance/BasicTestCest.php

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ class BasicTestCest
1111
{
1212
public function _before(AcceptanceTester $I)
1313
{
14-
$I->haveACleanSetupInRemoteService();
1514
}
1615

1716
public function _after(AcceptanceTester $I)
1817
{
18+
$I->haveACleanSetupInRemoteService();
1919
}
2020

2121
// tests
@@ -127,4 +127,46 @@ public function testGrabRequestsMadeToRemoteService(AcceptanceTester $I)
127127
$I->assertSame($value, $headers[$key]);
128128
}
129129
}
130+
131+
/**
132+
* @param AcceptanceTester $I
133+
* @expectation("test_first_get")
134+
*/
135+
public function testAnnotationExpectationIsLoaded(AcceptanceTester $I)
136+
{
137+
$requestBuilder = A::getRequest()->andUrl(Is::equalTo('/expectation/1'));
138+
$response = file_get_contents('http://localhost:18080/expectation/1');
139+
140+
$requests = $I->grabRequestsMadeToRemoteService($requestBuilder);
141+
$I->assertCount(1, $requests);
142+
143+
$I->assertEquals("response", $response);
144+
}
145+
146+
/**
147+
* @param AcceptanceTester $I
148+
* @expectation("test_first_get")
149+
* @expectation("test_second_get")
150+
*/
151+
public function testMultipleAnnotationsAreLoaded(AcceptanceTester $I)
152+
{
153+
$requestBuilder = A::getRequest()->andUrl(Is::matching('/\\/expectation\\/\\d+/'));
154+
file_get_contents('http://localhost:18080/expectation/1');
155+
file_get_contents('http://localhost:18080/expectation/2');
156+
$requests = $I->grabRequestsMadeToRemoteService($requestBuilder);
157+
$I->assertCount(2, $requests);
158+
}
159+
160+
/**
161+
* @param AcceptanceTester $I
162+
*
163+
* @expectation test_first_get
164+
* @expectation test_first_get.json
165+
* @expectation(test_first_get.json)
166+
* @expectation(test_first_get)
167+
* @expectation("test_first_get")
168+
*/
169+
public function testAnnotationFormats(AcceptanceTester $I)
170+
{
171+
}
130172
}

tests/tests/unit.suite.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
actor: UnitTester
2+
modules:
3+
enabled:
4+
- \Helper\Unit
5+
- Asserts
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
use Codeception\Exception\ParseException;
4+
use Codeception\Util\ExpectationAnnotationParser;
5+
6+
class ExpectationParserTestCest
7+
{
8+
/** @var ExpectationAnnotationParser */
9+
private $parser;
10+
11+
public function _before()
12+
{
13+
$this->parser = new ExpectationAnnotationParser();
14+
}
15+
16+
public function jsonExtensionIsOptional()
17+
{
18+
$this->parser->parseExpectation("test_first_get");
19+
$this->parser->parseExpectation("test_first_get.json");
20+
}
21+
22+
public function expectationNotFoundThrowsParseError(UnitTester $I)
23+
{
24+
$I->expectException(ParseException::class,function(){
25+
$this->parser->parseExpectation("random.expectation");
26+
});
27+
}
28+
}

tests/tests/unit/_bootstrap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<?php

0 commit comments

Comments
 (0)