Passed
Pull Request — master (#299)
by Grégoire
04:48
created

JsonContext   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 404
Duplicated Lines 9.9 %

Coupling/Cohesion

Components 1
Dependencies 6

Importance

Changes 0
Metric Value
wmc 42
lcom 1
cbo 6
dl 40
loc 404
rs 9.0399
c 0
b 0
f 0

29 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 5 1
A theResponseShouldBeInJson() 0 4 1
A theResponseShouldNotBeInJson() 0 7 1
A theJsonNodeShouldBeEqualTo() 0 12 2
A theJsonNodesShouldBeEqualTo() 0 6 2
A theJsonNodeShouldMatch() 0 12 2
A theJsonNodeShouldBeNull() 0 8 1
A theJsonNodeShouldNotBeNull() 0 6 1
A theJsonNodeShouldBeTrue() 0 12 2
A theJsonNodeShouldBeFalse() 0 12 2
A theJsonNodeShouldBeEqualToTheString() 12 12 2
A theJsonNodeShouldBeEqualToTheNumber() 12 12 3
A theJsonNodeShouldHaveElements() 0 8 1
A theJsonNodeShouldContain() 8 8 1
A theJsonNodesShouldContain() 0 6 2
A theJsonNodeShouldNotContain() 8 8 1
A theJsonNodesShouldNotContain() 0 6 2
A theJsonNodeShouldExist() 0 11 2
A theJsonNodeShouldNotExist() 0 6 1
A theJsonShouldBeValidAccordingToThisSchema() 0 7 1
A theJsonShouldBeInvalidAccordingToThisSchema() 0 6 1
A theJsonShouldBeValidAccordingToTheSchema() 0 12 1
A theJsonShouldBeInvalidAccordingToTheSchema() 0 8 1
A theJsonShouldBeEqualTo() 0 16 2
A printLastJsonResponse() 0 5 1
A theJsonShouldBeValidAccordingToTheSwaggerSchema() 0 16 1
A theJsonShouldNotBeValidAccordingToTheSwaggerSchema() 0 6 1
A getJson() 0 4 1
A checkSchemaFile() 0 8 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like JsonContext often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use JsonContext, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Behatch\Context;
4
5
use Behat\Gherkin\Node\PyStringNode;
6
7
use Behat\Gherkin\Node\TableNode;
8
use Behat\Mink\Exception\ExpectationException;
9
use Behatch\Json\Json;
10
use Behatch\Json\JsonSchema;
11
use Behatch\Json\JsonInspector;
12
use Behatch\HttpCall\HttpCallResultPool;
13
14
class JsonContext extends BaseContext
15
{
16
    protected $inspector;
17
18
    protected $httpCallResultPool;
19
20
    public function __construct(HttpCallResultPool $httpCallResultPool, $evaluationMode = 'javascript')
21
    {
22
        $this->inspector = new JsonInspector($evaluationMode);
23
        $this->httpCallResultPool = $httpCallResultPool;
24
    }
25
26
    /**
27
     * Checks, that the response is correct JSON
28
     *
29
     * @Then the response should be in JSON
30
     */
31
    public function theResponseShouldBeInJson()
32
    {
33
        $this->getJson();
34
    }
35
36
    /**
37
     * Checks, that the response is not correct JSON
38
     *
39
     * @Then the response should not be in JSON
40
     */
41
    public function theResponseShouldNotBeInJson()
42
    {
43
        $this->not(
44
            [$this, 'theResponseShouldBeInJson'],
45
            'The response is in JSON'
46
        );
47
    }
48
49
    /**
50
     * Checks, that given JSON node is equal to given value
51
     *
52
     * @Then the JSON node :node should be equal to :text
53
     */
54
    public function theJsonNodeShouldBeEqualTo($node, $text)
55
    {
56
        $json = $this->getJson();
57
58
        $actual = $this->inspector->evaluate($json, $node);
59
60
        if ($actual != $text) {
61
            throw new \Exception(
62
                sprintf("The node value is '%s'", json_encode($actual))
63
            );
64
        }
65
    }
66
67
    /**
68
     * Checks, that given JSON nodes are equal to givens values
69
     *
70
     * @Then the JSON nodes should be equal to:
71
     */
72
    public function theJsonNodesShouldBeEqualTo(TableNode $nodes)
73
    {
74
        foreach ($nodes->getRowsHash() as $node => $text) {
75
            $this->theJsonNodeShouldBeEqualTo($node, $text);
76
        }
77
    }
78
79
    /**
80
     * Checks, that given JSON node matches given pattern
81
     *
82
     * @Then the JSON node :node should match :pattern
83
     */
84
    public function theJsonNodeShouldMatch($node, $pattern)
85
    {
86
        $json = $this->getJson();
87
88
        $actual = $this->inspector->evaluate($json, $node);
89
90
        if (preg_match($pattern, $actual) === 0) {
91
            throw new \Exception(
92
                sprintf("The node value is '%s'", json_encode($actual))
93
            );
94
        }
95
    }
96
97
    /**
98
     * Checks, that given JSON node is null
99
     *
100
     * @Then the JSON node :node should be null
101
     */
102
    public function theJsonNodeShouldBeNull($node)
103
    {
104
        $json = $this->getJson();
105
106
        $actual = $this->inspector->evaluate($json, $node);
107
108
        $this->assertNull($actual, sprintf('The node value is `%s`', json_encode($actual)));
109
    }
110
111
    /**
112
     * Checks, that given JSON node is not null.
113
     *
114
     * @Then the JSON node :node should not be null
115
     */
116
    public function theJsonNodeShouldNotBeNull($node)
117
    {
118
        $this->not(function () use ($node) {
119
            return $this->theJsonNodeShouldBeNull($node);
120
        }, sprintf('The node %s should not be null', $node));
121
    }
122
123
    /**
124
     * Checks, that given JSON node is true
125
     *
126
     * @Then the JSON node :node should be true
127
     */
128
    public function theJsonNodeShouldBeTrue($node)
129
    {
130
        $json = $this->getJson();
131
132
        $actual = $this->inspector->evaluate($json, $node);
133
134
        if (true !== $actual) {
135
            throw new \Exception(
136
                sprintf('The node value is `%s`', json_encode($actual))
137
            );
138
        }
139
    }
140
141
    /**
142
     * Checks, that given JSON node is false
143
     *
144
     * @Then the JSON node :node should be false
145
     */
146
    public function theJsonNodeShouldBeFalse($node)
147
    {
148
        $json = $this->getJson();
149
150
        $actual = $this->inspector->evaluate($json, $node);
151
152
        if (false !== $actual) {
153
            throw new \Exception(
154
                sprintf('The node value is `%s`', json_encode($actual))
155
            );
156
        }
157
    }
158
159
    /**
160
     * Checks, that given JSON node is equal to the given string
161
     *
162
     * @Then the JSON node :node should be equal to the string :text
163
     */
164 View Code Duplication
    public function theJsonNodeShouldBeEqualToTheString($node, $text)
165
    {
166
        $json = $this->getJson();
167
168
        $actual = $this->inspector->evaluate($json, $node);
169
170
        if ($actual !== $text) {
171
            throw new \Exception(
172
                sprintf('The node value is `%s`', json_encode($actual))
173
            );
174
        }
175
    }
176
177
    /**
178
     * Checks, that given JSON node is equal to the given number
179
     *
180
     * @Then the JSON node :node should be equal to the number :number
181
     */
182 View Code Duplication
    public function theJsonNodeShouldBeEqualToTheNumber($node, $number)
183
    {
184
        $json = $this->getJson();
185
186
        $actual = $this->inspector->evaluate($json, $node);
187
188
        if ($actual !== (float) $number && $actual !== (int) $number) {
189
            throw new \Exception(
190
                sprintf('The node value is `%s`', json_encode($actual))
191
            );
192
        }
193
    }
194
195
    /**
196
     * Checks, that given JSON node has N element(s)
197
     *
198
     * @Then the JSON node :node should have :count element(s)
199
     */
200
    public function theJsonNodeShouldHaveElements($node, $count)
201
    {
202
        $json = $this->getJson();
203
204
        $actual = $this->inspector->evaluate($json, $node);
205
206
        $this->assertSame($count, sizeof((array) $actual));
207
    }
208
209
    /**
210
     * Checks, that given JSON node contains given value
211
     *
212
     * @Then the JSON node :node should contain :text
213
     */
214 View Code Duplication
    public function theJsonNodeShouldContain($node, $text)
215
    {
216
        $json = $this->getJson();
217
218
        $actual = $this->inspector->evaluate($json, $node);
219
220
        $this->assertContains($text, (string) $actual);
221
    }
222
223
    /**
224
     * Checks, that given JSON nodes contains values
225
     *
226
     * @Then the JSON nodes should contain:
227
     */
228
    public function theJsonNodesShouldContain(TableNode $nodes)
229
    {
230
        foreach ($nodes->getRowsHash() as $node => $text) {
231
            $this->theJsonNodeShouldContain($node, $text);
232
        }
233
    }
234
235
    /**
236
     * Checks, that given JSON node does not contain given value
237
     *
238
     * @Then the JSON node :node should not contain :text
239
     */
240 View Code Duplication
    public function theJsonNodeShouldNotContain($node, $text)
241
    {
242
        $json = $this->getJson();
243
244
        $actual = $this->inspector->evaluate($json, $node);
245
246
        $this->assertNotContains($text, (string) $actual);
247
    }
248
249
    /**
250
     * Checks, that given JSON nodes does not contain given value
251
     *
252
     * @Then the JSON nodes should not contain:
253
     */
254
    public function theJsonNodesShouldNotContain(TableNode $nodes)
255
    {
256
        foreach ($nodes->getRowsHash() as $node => $text) {
257
            $this->theJsonNodeShouldNotContain($node, $text);
258
        }
259
    }
260
261
    /**
262
     * Checks, that given JSON node exist
263
     *
264
     * @Then the JSON node :name should exist
265
     */
266
    public function theJsonNodeShouldExist($name)
267
    {
268
        $json = $this->getJson();
269
270
        try {
271
            $node = $this->inspector->evaluate($json, $name);
272
        } catch (\Exception $e) {
273
            throw new \Exception("The node '$name' does not exist.");
274
        }
275
        return $node;
276
    }
277
278
    /**
279
     * Checks, that given JSON node does not exist
280
     *
281
     * @Then the JSON node :name should not exist
282
     */
283
    public function theJsonNodeShouldNotExist($name)
284
    {
285
        $this->not(function () use ($name) {
286
            return $this->theJsonNodeShouldExist($name);
287
        }, "The node '$name' exists.");
288
    }
289
290
    /**
291
     * @Then the JSON should be valid according to this schema:
292
     */
293
    public function theJsonShouldBeValidAccordingToThisSchema(PyStringNode $schema)
294
    {
295
        $this->inspector->validate(
296
            $this->getJson(),
297
            new JsonSchema($schema)
298
        );
299
    }
300
301
    /**
302
     * @Then the JSON should be invalid according to this schema:
303
     */
304
    public function theJsonShouldBeInvalidAccordingToThisSchema(PyStringNode $schema)
305
    {
306
        $this->not(function () use ($schema) {
307
            return $this->theJsonShouldBeValidAccordingToThisSchema($schema);
308
        }, 'Expected to receive invalid json, got valid one');
309
    }
310
311
    /**
312
     * @Then the JSON should be valid according to the schema :filename
313
     */
314
    public function theJsonShouldBeValidAccordingToTheSchema($filename)
315
    {
316
        $this->checkSchemaFile($filename);
317
318
        $this->inspector->validate(
319
            $this->getJson(),
320
            new JsonSchema(
321
                file_get_contents($filename),
322
                'file://' . str_replace(DIRECTORY_SEPARATOR, '/', realpath($filename))
323
            )
324
        );
325
    }
326
327
    /**
328
     * @Then the JSON should be invalid according to the schema :filename
329
     */
330
    public function theJsonShouldBeInvalidAccordingToTheSchema($filename)
331
    {
332
        $this->checkSchemaFile($filename);
333
334
        $this->not(function () use ($filename) {
335
            return $this->theJsonShouldBeValidAccordingToTheSchema($filename);
336
        }, "The schema was valid");
337
    }
338
339
    /**
340
     * @Then the JSON should be equal to:
341
     */
342
    public function theJsonShouldBeEqualTo(PyStringNode $content)
343
    {
344
        $actual = $this->getJson();
345
346
        try {
347
            $expected = new Json($content);
348
        } catch (\Exception $e) {
349
            throw new \Exception('The expected JSON is not a valid');
350
        }
351
352
        $this->assertSame(
353
            (string) $expected,
354
            (string) $actual,
355
            "The json is equal to:\n". $actual->encode()
356
        );
357
    }
358
359
    /**
360
     * @Then print last JSON response
361
     */
362
    public function printLastJsonResponse()
363
    {
364
        echo $this->getJson()
365
            ->encode();
366
    }
367
368
    /**
369
     * Checks, that response JSON matches with a swagger dump
370
     *
371
     * @Then the JSON should be valid according to swagger :dumpPath dump schema :schemaName
372
     */
373
    public function theJsonShouldBeValidAccordingToTheSwaggerSchema($dumpPath, $schemaName)
374
    {
375
        $this->checkSchemaFile($dumpPath);
376
377
        $dumpJson = file_get_contents($dumpPath);
378
        $schemas = json_decode($dumpJson, true);
379
        $definition = json_encode(
380
            $schemas['definitions'][$schemaName]
381
        );
382
        $this->inspector->validate(
383
            $this->getJson(),
384
            new JsonSchema(
385
                $definition
386
            )
387
        );
388
    }
389
    /**
390
     *
391
     * Checks, that response JSON not matches with a swagger dump
392
     *
393
     * @Then the JSON should not be valid according to swagger :dumpPath dump schema :schemaName
394
     */
395
    public function theJsonShouldNotBeValidAccordingToTheSwaggerSchema($dumpPath, $schemaName)
396
    {
397
        $this->not(function () use ($dumpPath, $schemaName) {
398
            return $this->theJsonShouldBeValidAccordingToTheSwaggerSchema($dumpPath, $schemaName);
399
        }, 'JSON Schema matches but it should not');
400
    }
401
402
403
404
    protected function getJson()
405
    {
406
        return new Json($this->httpCallResultPool->getResult()->getValue());
407
    }
408
409
    private function checkSchemaFile($filename)
410
    {
411
        if (false === is_file($filename)) {
412
            throw new \RuntimeException(
413
                'The JSON schema doesn\'t exist'
414
            );
415
        }
416
    }
417
}
418