最新服务器上的版本,以后用这个
wangzhenxin
2023-11-19 bc164b8bdbfbdf1d8229a5ced6b08d7cb8db7361
commit | author | age
2207d6 1 Specify
W 2 =======
3
4 BDD style code blocks for PHPUnit / Codeception
5
6 [![Latest Stable Version](https://poser.pugx.org/codeception/specify/v/stable)](https://packagist.org/packages/codeception/specify)[![Total Downloads](https://poser.pugx.org/codeception/specify/downloads)](https://packagist.org/packages/codeception/specify)[![Latest Unstable Version](https://poser.pugx.org/codeception/specify/v/unstable)](https://packagist.org/packages/codeception/specify)[![License](https://poser.pugx.org/codeception/specify/license)](https://packagist.org/packages/codeception/specify)
7
8 Specify allows you to write your tests in more readable BDD style, the same way you might have experienced with [Jasmine](https://jasmine.github.io/).
9 Inspired by MiniTest of Ruby now you combine BDD and classical TDD style in one test.
10
11 ### Basic Example
12
13 Specify `$this->specify` method to add isolated test blocks for your PHPUnit tests! 
14
15 ```php
16 public function testValidation()
17 {
18     $this->assertInstanceOf('Model', $this->user);
19
20     $this->specify("username is required", function() {
21         $this->user->username = null;
22         $this->assertFalse($this->user->validate(['username']));    
23     });
24
25     $this->specify("username is too long", function() {
26         $this->user->username = 'toolooooongnaaaaaaameeee';
27         $this->assertFalse($this->user->validate(['username']));            
28     });
29
30     $this->specify("username is ok", function() {
31         $this->user->username = 'davert';
32         $this->assertTrue($this->user->validate(['username']));            
33     });
34 }
35 ```
36
37 ### BDD Example
38
39 Specify supports `describe-it` BDD syntax inside PHPUnit
40
41 ```php
42 public function testValidation()
43 {
44     $this->describe("user", function() {
45         $this->it("should have a name", function() {
46             $this->user->username = null;
47             $this->assertFalse($this->user->validate(['username']));    
48         });
49
50         $this->it("should not have long name", function() {
51             $this->user->username = 'toolooooongnaaaaaaameeee';
52             $this->assertFalse($this->user->validate(['username']));            
53         });
54         
55         // use `$this->>should` as shortcut
56         $this->should("be ok with valid name", function() {
57             $this->user->username = 'davert';
58             $this->assertTrue($this->user->validate(['username']));            
59         });
60         
61         // empty codeblocks are marked as Incomplete tests
62         $this->it("should be ok with valid name");                
63     });
64 }
65 ```
66
67
68 ### Specify + Verify Example
69
70 Use [Codeception/Verify](https://github.com/Codeception/Verify) for simpler assertions:
71
72 ```php
73 public function testValidation()
74 {
75     $this->specify("username is required", function() {
76         $this->user->username = null;
77         expect_not($this->user->validate(['username']));    
78     });
79     
80     $this->specify("username is too long", function() {
81         $this->user->username = 'toolooooongnaaaaaaameeee';
82         expect_not($this->user->validate(['username']));            
83     });
84     
85     $this->specify("username is ok", function() {
86         $this->user->username = 'davert';        
87         expect_that($this->user->validate(['username']));            
88     });                
89 }
90 ```
91
92 ## Use Case
93
94 This tiny library makes your tests readable by organizing them in nested code blocks.
95 This allows to combine similar tests into one but put them inside nested sections.
96
97 This is very similar to BDD syntax as in RSpec or Mocha but works inside PHPUnit:
98
99 ```php
100 <?php
101 class UserTest extends PHPUnit\Framework\TestCase 
102 {
103     use Codeception\Specify;
104     
105     /** @specify */
106     protected $user; // is cloned inside specify blocks
107     
108     public function setUp()
109     {
110         $this->user = new User;
111     }
112
113     public function testValidation()
114     {
115         $this->user->name = 'davert';
116         $this->specify("i can change my name", function() {
117            $this->user->name = 'jon';
118            $this->assertEquals('jon', $this->user->name);
119         });       
120         // user name is "davert" again
121         $this->assertEquals('davert', $this->user->name);
122     }
123 }
124 ```
125
126 Each code block is isolated. This means call to `$this->specify` does not change values of properties of a test class.
127 Isolated properties should be marked with `@specify` annotation.
128
129 Failure in `specify` block won't get your test stopped.
130
131 ```php
132 <?php
133 $this->specify("failing but test goes on", function() {
134     $this->fail('bye');
135 });
136 $this->assertTrue(true);
137
138 // Assertions: 2, Failures: 1
139 ?>
140 ```
141
142 If a test fails you will see specification text in the result.
143
144 ## Isolation
145
146 Isolation is achieved by **cloning object properties** for each specify block.
147 Only properties marked with `@specify` annotation are cloned. 
148
149 ```php
150 /** @specify */
151 protected $user; // cloning
152
153 /** 
154  * @specify 
155  **/
156 protected $user; // cloning
157
158 protected $repository; // not cloning
159 ```
160
161 Objects are cloned using deep cloning method. 
162
163 **If object cloning affects performance, consider turning the clonning off**.
164
165 **Mocks are isolated** by default. 
166
167 A mock defined inside a specify block won't be executed inside an outer test,
168 and mock from outer test won't be triggered inside codeblock.
169
170 ```php
171 <?php
172 $config = $this->createMock(Config::class);
173 $config->expects($this->once())->method('init');
174
175 $config->init();
176 // success: $config->init() was executed
177
178 $this->specify('this should not fail', function () {
179     $config = $this->createMock(Config::class);
180     $config->expects($this->never())->method('init')->willReturn(null);
181     // success: $config->init() is never executed 
182 });
183 ```
184
185 ## Examples: DataProviders alternative
186
187 ```php
188 <?php
189 $this->specify("should calculate square numbers", function($number, $square) {
190     $this->assertEquals($square, $number*$number);
191 }, ['examples' => [
192         [2,4],
193         [3,9]
194 ]]);
195 ```
196
197 You can also use DataProvider functions in `examples` param.
198
199 ```php
200 <?php
201 $this->specify("should calculate square numbers", function($number, $square) {
202     $this->assertEquals($square, $number*$number);
203 }, ['examples' => $this->provider()]);
204 ```
205
206 Can also be used with real data providers:
207
208 ```php
209 <?php
210 /**
211  * @dataProvider someData
212  */
213 public function testExamplesAndDataProvider($param)
214 {
215     $this->specify('should assert data provider', function ($example) use ($param) {
216         $this->assertGreaterThanOrEqual(5, $param + $example);
217     }, ['examples' => [[4], [7], [5]]]);
218 }
219
220 public function someData()
221 {
222     return [[1], [2]];
223 }
224 ```
225
226 ## Before/After
227
228 There are also before and after callbacks, which act as setUp/tearDown but for specify.
229
230 ```php
231 <?php
232 $this->beforeSpecify(function() {
233     // prepare something;    
234 });
235 $this->afterSpecify(function() {
236     // reset something
237 });
238 $this->cleanSpecify(); // removes before/after callbacks
239 ?>
240 ```
241
242 ## API
243
244 Available methods:
245
246 * `$this->specify(name, callable fn = null, params = [])` - starts a specify code block. If `fn` is null, marks test as incomplete. 
247 * `$this->describe(name, callable fn = null)` - starts a describe code block. Same as `specify` but expects to receive more nested into `fn`.
248 * `$this->it(name, callable fn = null)` - starts a code block. Alias to `specify`.
249 * `$this->should(name, callable fn = null)` - starts a code block. Same as `specify` but prepends word "should" into description.
250
251
252 ## Installation
253
254 *Requires PHP >= 7.*
255
256 * Install with Composer:
257
258 ```
259 composer require codeception/specify --dev
260 ```
261
262 * Include `Codeception\Specify` trait into `PHPUnit\Framework\TestCase`.
263 * Add `/** @specify **/` docblock for all properties you want to make isolated inside tests.
264
265 * For PHPUnit add `Codeception\Specify\ResultPrinter` printer into `phpunit.xml`
266
267 ```xml
268 <phpunit colors="true" printerClass="Codeception\Specify\ResultPrinter">
269 </phpunit>
270 ```
271
272 ## Recommended
273
274 * Use [Codeception/AssertThrows](https://github.com/Codeception/AssertThrows) for exception assertions
275 * Use [Codeceptoin/DomainAssert](https://github.com/Codeception/DomainAssert) for verbose domain logic assertions
276 * Сombine this with [Codeception/Verify](https://github.com/Codeception/Verify) library, to get BDD style assertions.
277
278 License: MIT