I. Đặt vấn đề
1. Test code coverage là gì ?
- Trong lĩnh vực kiểm thử thì test code coverage: Là việc tạo các trường hợp test để thỏa mãn một số điều kiện bao phủ code – code coverage (ví dụ như, người thiết kế test có thể tạo ra các trường hợp test sao cho tất cả các câu lệnh của chương trình đều được thực thi ít nhất 1 lần).
- Code coverage là độ phủ của các dòng code đã được chạy qua các test case mà chúng ta đã viết. Điều này có nghĩa là nếu ta xác định mục tiêu của Unit sai, chia các case sai thì bug vẫn tồn tại. Bug ở đây thường là các bug về logic, bug giữa các unit khác nhau.
- Tuy nhiên code coverage chỉ đảm bảo các câu lệnh rẽ nhánh đã chaỵ qua hay không? Bạn có thể phát hiện code của mình viết thừa logic trong code qua những câu lệnh rẽ nhánh không bao giờ code chạy qua. Ý nghĩa của code coverage là đảm bảo các dòng code đã được chạy qua bởi các test case
2. Test code coverage được tính trên những thành phần nào ?
- Code coverage là tính độ bao phủ trung bình mà code đã chạy qua:
- lines (dòng code),
- functions & methods (các hàm và các phương thức),
- classes & traits
3. Tại sao phải viết test code coverage cho dự án ?
- Đảm bảo code được viết ra là code sạch, được viết theo một logic đúng và hợp lý.
- Đảm bảo ít bugs nhất xảy ra với những gì chúng ta code ra.
- Đảm bảo chúng ta đang code đúng specs, yêu cầu mà khách hàng đưa ra.
- Sản phẩm đưa đến tester, QA là một sản phẩm hoàn thiện ít bugs đẩy nhanh quá trình
release
dự án.
II. Nội dung chính
Việc viết test nên được viết song song trong quá trình chúng ta code sản phẩm. Việc này giúp chúng ta định hình được base chuẩn cho code và cách viết code đúng. Trong laravel chúng ta cần viết code theo những phần sau:
1. Console Tests:
Console tests là việc test các command mà ta tạo ra xem nó có chạy không. Trong thực tế thì người ta ít khi sử dụng console tests
để tính code coverage.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <span class="token comment">/** * Test a console command. * * @return void */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">testConsoleCommand</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">artisan</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'question'</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">expectsQuestion</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'What is your name?'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'Taylor Otwell'</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">expectsQuestion</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Which language do you program in?'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'PHP'</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">expectsOutput</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'Your name is Taylor Otwell and you program in PHP.'</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertExitCode</span><span class="token punctuation">(</span><span class="token number">0</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
2. Feature Tests
Thực chất trong laravel đã định danh cho feature test
là HTTP Tests. Một ví dụ đơn giản của feature tests
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <span class="token php language-php"><span class="token delimiter important"><?php</span> <span class="token keyword">namespace</span> <span class="token package">TestsFeature</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">IlluminateFoundationTestingRefreshDatabase</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">IlluminateFoundationTestingWithoutMiddleware</span><span class="token punctuation">;</span> <span class="token keyword">use</span> <span class="token package">TestsTestCase</span><span class="token punctuation">;</span> <span class="token keyword">class</span> <span class="token class-name">ExampleTest</span> <span class="token keyword">extends</span> <span class="token class-name">TestCase</span> <span class="token punctuation">{</span> <span class="token comment">/** * A basic test example. * * @return void */</span> <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">testBasicTest</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$response</span> <span class="token operator">=</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">get</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'/'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$response</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">assertStatus</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> </span> |
Có ví dụ đơn giản như sau: Mình có 1 api tạo một post mới vào hệ thống.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | <span class="token keyword">public</span> <span class="token keyword">function</span> <span class="token function">test_it_can_store_post</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$url</span> <span class="token operator">=</span> <span class="token single-quoted-string string">'/api/posts'</span><span class="token punctuation">;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">postJson</span><span class="token punctuation">(</span><span class="token variable">$url</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertStatus</span><span class="token punctuation">(</span><span class="token number">422</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertJsonStructure</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'errors'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'*'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token variable">$input</span> <span class="token operator">=</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'title'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'This is test'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'description'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'example'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">postJson</span><span class="token punctuation">(</span><span class="token variable">$url</span><span class="token punctuation">,</span> <span class="token variable">$input</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertStatus</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertJson</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'status'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'success'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> |
Feature tests trên là việc test api để kiểm tra một số điều như sau:
- Check xem api đã pass validate input đầu vào hay chưa.
1 2 3 4 5 6 7 8 | <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">postJson</span><span class="token punctuation">(</span><span class="token variable">$url</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertStatus</span><span class="token punctuation">(</span><span class="token number">422</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertJsonStructure</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'errors'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'*'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token punctuation">[</span><span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
- Check xem api đã chạy qua hết code chưa.
1 2 3 4 5 6 | <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">postJson</span><span class="token punctuation">(</span><span class="token variable">$url</span><span class="token punctuation">,</span> <span class="token variable">$input</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertStatus</span><span class="token punctuation">(</span><span class="token number">200</span><span class="token punctuation">)</span> <span class="token operator">-</span><span class="token operator">></span><span class="token function">assertJson</span><span class="token punctuation">(</span><span class="token punctuation">[</span> <span class="token single-quoted-string string">'status'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'success'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
3. Integration Tests:
Trong laravel nó được định nghĩa là: Database Testing. Là việc kiểm tra code của chúng ta đã chạy xong và dự liệu đã được lưu trong database hay chưa. Integration Tests
đôi lúc cũng được viết chúng trong Feature Tests
1 2 3 4 5 | <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">assertDatabaseHas</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'posts'</span><span class="token punctuation">,</span> <span class="token punctuation">[</span> <span class="token single-quoted-string string">'title'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'This is test'</span><span class="token punctuation">,</span> <span class="token single-quoted-string string">'description'</span> <span class="token operator">=</span><span class="token operator">></span> <span class="token single-quoted-string string">'example'</span><span class="token punctuation">,</span> <span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
4. Unit Test:
Unit tests là việc sử dụng mock
để gọi đến các classes, functions để tạo các Mocking và test xem code coverage như thế nào.
1 2 3 4 5 6 | <span class="token keyword">use</span> <span class="token package">AppService</span><span class="token punctuation">;</span> <span class="token variable">$this</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">mock</span><span class="token punctuation">(</span>Service<span class="token punctuation">:</span><span class="token punctuation">:</span><span class="token keyword">class</span><span class="token punctuation">,</span> <span class="token keyword">function</span> <span class="token punctuation">(</span><span class="token variable">$mock</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token variable">$mock</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">shouldReceive</span><span class="token punctuation">(</span><span class="token single-quoted-string string">'process'</span><span class="token punctuation">)</span><span class="token operator">-</span><span class="token operator">></span><span class="token function">once</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> |
Sau khi mock thành công vào các classes, functions ta kiểm tra rằng code đã gọi đến hết các hàm, phương thức và đầu ra có đúng không.
III. Một số lưu ý khi viết test
- Chỉ nên test và cover những phần code có ý nghĩa, trong Laravel thì chúng ta không cần viết test cho các phần base của Laravel ví dụ như:
Provider
,Middleware
, hay 1 sốcommand
(Console
)… - Nên chủ động tách code cho hợp lý, chẳng hạn như: trong Controller không nên sử lý logic, ta nên chuyển phần code thao tác đến database vào Repositories, code logic tính toán, xử lý điều kiện vào services, factory, … Và khi đó chúng ta sẽ test api (
feature tests
), repositories thao tác đến database (Integration Tests
), những phần xử lý logic services, …(Unit Tests
). - Nên viết test trước khi viết code.
- Tránh việc cố tình pass tests để kiếm code coverage.
IV. Tạm kết
Chắc rằng qua bài viết các bạn cũng đã có những lưu ý vể việc viết test code coverage cho dự án rồi đúng không nào. Rất mong được sự góp ý từ các bạn