Completed
Pull Request — master (#7)
by Jan
03:02
created

ConstraintTestCase   A

Complexity

Total Complexity 17

Size/Duplication

Total Lines 198
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 8

Test Coverage

Coverage 97.87%

Importance

Changes 5
Bugs 0 Features 1
Metric Value
wmc 17
c 5
b 0
f 1
lcom 2
cbo 8
dl 0
loc 198
ccs 46
cts 47
cp 0.9787
rs 10

7 Methods

Rating   Name   Duplication   Size   Complexity  
C applyTestProvider() 0 58 10
getConstraint() 0 1 ?
getCaseFileNames() 0 1 ?
B testApply() 0 30 1
A mockWalker() 0 4 1
A expectConstraintException() 0 7 1
B exceptionHook() 0 37 4
1
<?php
2
3
/*
4
 * This file is part of the JVal package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
namespace JVal\Testing;
11
12
use JVal\Constraint;
13
use JVal\Context;
14
use JVal\Exception\ConstraintException;
15
use JVal\Registry;
16
use JVal\Resolver;
17
use JVal\Walker;
18
19
/**
20
 * Test case for testing validation constraints.
21
 */
22
abstract class ConstraintTestCase extends BaseTestCase
23
{
24
    private $expectedExceptionClass;
25
    private $expectedExceptionPath;
26
    private $expectedExceptionTarget;
27
28
    /**
29
     * @dataProvider applyTestProvider
30
     *
31
     * @param string    $file
32
     * @param string    $title
33
     * @param mixed     $instance
34
     * @param \stdClass $schema
35
     * @param bool      $isInstanceValid
36
     * @param array     $expectedErrors
37
     */
38 185
    public function testApply(
39
        $file,
40
        $title,
41
        $instance,
42
        \stdClass $schema,
43
        $isInstanceValid,
44
        array $expectedErrors
45
    ) {
46 185
        $constraint = $this->getConstraint();
47 185
        $schemaContext = new Context();
48 185
        $validationContext = new Context();
49 185
        $walker = new Walker(new Registry(), new Resolver());
50
51 185
        $pathBefore = $schemaContext->getCurrentPath();
52 185
        $constraint->normalize($schema, $schemaContext, $walker);
53 185
        $this->assertSame($pathBefore, $schemaContext->getCurrentPath());
54
55 185
        $constraint->apply($instance, $schema, $validationContext, $walker);
56 185
        $actualErrors = $validationContext->getViolations();
57
58 185
        $this->assertValidationResult(
59 185
            $file,
60 185
            $title,
61 185
            $instance,
62 185
            $schema,
63 185
            $isInstanceValid,
64 185
            $expectedErrors,
65
            $actualErrors
66 185
        );
67 185
    }
68
69
    /**
70
     * @codeCoverageIgnore (data provider is executed before test is launched)
71
     *
72
     * Provider of #testApply().
73
     */
74
    public function applyTestProvider()
75
    {
76
        $caseDir = realpath(__DIR__.'/../../tests/Data/cases');
77
        $tests = [];
78
79
        foreach ($this->getCaseFileNames() as $caseName) {
80
            $caseFile = "{$caseDir}/{$caseName}.json";
81
            $case = $this->loadJsonFromFile($caseFile);
82
83
            foreach ($case->tests as $test) {
84
                if (!isset($test->valid) && !isset($test->invalid)) {
85
                    throw new \Exception(sprintf(
86
                        'Test case "%s %s" has neither "valid" or "invalid" data (file: %s)',
87
                        $case->title,
88
                        $test->title,
89
                        $caseFile
90
                    ));
91
                }
92
93
                if (isset($test->valid)) {
94
                    foreach ($test->valid as $i => $instance) {
95
                        $tests[] = [
96
                            $caseFile,
97
                            "{$case->title} {$test->title}, valid instance #{$i}",
98
                            $instance,
99
                            $test->schema,
100
                            true,
101
                            [],
102
                        ];
103
                    }
104
                }
105
106
                if (isset($test->invalid)) {
107
                    foreach ($test->invalid as $i => $set) {
108
                        if (!isset($set->violations)) {
109
                            throw new \Exception(sprintf(
110
                                'Invalid test must have a "violations" property in %s',
111
                                $caseFile
112
                            ));
113
                        }
114
115
                        $tests[] = [
116
                            $caseFile,
117
                            "{$case->title} {$test->title}, invalid instance #{$i}",
118
                            $set->instance,
119
                            $test->schema,
120
                            false,
121
                            array_map(function ($violation) {
122
                                return (array) $violation;
123
                            }, $set->violations),
124
                        ];
125
                    }
126
                }
127
            }
128
        }
129
130
        return $tests;
131
    }
132
133
    /**
134
     * Returns an instance of the constraint to be tested.
135
     *
136
     * @return Constraint
137
     */
138
    abstract protected function getConstraint();
139
140
    /**
141
     * Returns an array of case file names for #testApply().
142
     *
143
     * @return array
144
     */
145
    abstract protected function getCaseFileNames();
146
147
    /**
148
     * Returns a mocked walker instance.
149
     *
150
     * @return \PHPUnit_Framework_MockObject_MockObject|Walker
151
     */
152 83
    protected function mockWalker()
153
    {
154 83
        return $this->mock('JVal\Walker');
155
    }
156
157
    /**
158
     * Asserts a constraint exception will be thrown at a given path
159
     * and optionally on a given target.
160
     *
161
     * @param string $exceptionName
162
     * @param string $path
163
     * @param string $target
164
     */
165 61
    protected function expectConstraintException($exceptionName, $path, $target = null)
166
    {
167 61
        $this->expectedExceptionClass = "JVal\\Exception\\Constraint\\{$exceptionName}";
168 61
        $this->expectedExceptionPath = $path;
169 61
        $this->expectedExceptionTarget = $target;
170 61
        $this->expectException();
171 61
    }
172
173
    /**
174
     * Implements the default hook, asserting that the exception thrown
175
     * is an instance of ConstraintException and that its path and target
176
     * match the expectations.
177
     *
178
     * @param \Exception $ex
179
     *
180
     * @throws \Exception
181
     */
182 61
    protected function exceptionHook(\Exception $ex)
183
    {
184
        // @codeCoverageIgnoreStart
185
        if (empty($this->expectedExceptionClass)) {
186
            throw $ex;
187
        }
188
        // @codeCoverageIgnoreEnd
189
190 61
        $this->assertThat(
191 61
            $ex,
192 61
            new \PHPUnit_Framework_Constraint_Exception(
193 61
                $this->expectedExceptionClass
194 61
            )
195 61
        );
196
197 61
        if ($ex instanceof ConstraintException) {
198 61
            $this->assertEquals(
199 61
                $this->expectedExceptionPath,
200 61
                $ex->getPath(),
201
                'Exception was not thrown at the expected path.'
202 61
            );
203
204 61
            if (!empty($this->expectedExceptionTarget)) {
205 2
                $this->assertEquals(
206 2
                    $this->expectedExceptionTarget,
207 2
                    $ex->getTarget(),
208
                    'Exception does not have the expected target.'
209 2
                );
210 2
            }
211
212 61
            return;
213
        }
214
215
        // @codeCoverageIgnoreStart
216
        $this->fail('Exception thrown is not a ConstraintException');
217
        // @codeCoverageIgnoreEnd
218
    }
219
}
220