Bạn đang xem bài viết Hướng Dẫn Viết Unit Test Trong Angular được cập nhật mới nhất tháng 10 năm 2023 trên website Ezlearning.edu.vn. Hy vọng những thông tin mà chúng tôi đã chia sẻ là hữu ích với bạn. Nếu nội dung hay, ý nghĩa bạn hãy chia sẻ với bạn bè của mình và luôn theo dõi, ủng hộ chúng tôi để cập nhật những thông tin mới nhất.
Lợi ích của Unit test
Cải thiện design: Bắt tay vào code mà không để ý quá nhiều đến design là một lỗi phổ biến của các developer, việc viết unit test sẽ bắt buộc bạn phải suy nghĩ, thậm chí nghĩ đi nghĩ lại về design
Dễ dàng refactor: bởi vì bạn đã có các case test đảm bảo code của bạn chạy đúng như mong đợi, bạn có thể dễ dàng refactor code mà không lo sẽ tạo ra những bug mới
Test là một tài liệu về spec: nếu viết test chuẩn và đủ case, thì chỉ cần đọc test, ta cũng có thể hiểu rõ spec còn nhanh hơn cả đọc code
Việc viết test giúp developer tự tin hơn
Bạn có thể cho rằng việc viết test quá tốn thời gian, nhất là viết test FE, có khi nó còn lâu hơn cả viết code nữa, nhưng mình nghĩ nó sẽ giúp tiết kiệm thời gian hơn khi bạn muốn refactor code hay phát triển tính năng mới, giảm thiểu được cả thời gian để fix bug so với việc không viết test.
Giờ chúng ta sẽ bắt tay vào một ví dụ nhỏ nhưng khá đầy đủ sử dụng Angular, Jasmine và Karma. Phần tiếp theo của bài viết sẽ đề cập đến những điều sau:
Giải thích một chút về Karma và Jasmine
Karma config
Tạo một file test đơn giản
Test angular form
Test component với service
Tạo project Angular với Jasmine và KarmaInstall angular-cli và tạo project mới
Giải thích 1 chút về Jasmine và Karma không lại bảo nãy giờ cứ nói hoài mà không biết là gì
Jasmine: là một framework dùng để viết test, nó có các function có sẵn hỗ trợ viết các loại test khác nhau
Karma: là một Javascript tool được sử dụng để load ứng dụng và thực thi test của bạn. Karma sẽ được thực thi bằng dòng lệnh và sẽ hiển thị kết quả cho bạn biết mỗi khi bạn chạy trình duyệt.
Để chạy thử chỉ cần gõ ng test. Lệnh này sẽ thực thi các case test, mở trình duyệt, hiển thị kết quả trên trình duyệt và trong console.
Karma configGiờ hãy nhìn vào file Karma config được sinh ra từ angular-cli
frameworks: đây là nơi jasmine được set là framework để testing, nếu bạn muốn dùng 1 framework khác thì có thể điền vào đây.
autoWatch: nếu nó được set là true, test sẽ chạy chế độ watch mode, có nghĩa là mỗi khi bạn thay đổi bất cứ dòng code nào trong file test và lưu lại, nó sẽ tự động build lại và chạy lại.
browsers: đây là nơi để set trình duyệt và test sẽ chạy, mặc định sẽ là Chrome, nhưng bạn có thể sử dụng trình duyệt khác bằng cách khai báo ở đây.
Bắt đầu với một file test đơn giảnLướt qua 1 chút thì ở đây chúng ta đã
import các thư viện angular testing mà chúng ta sẽ sử dụng
Import các component phụ thuộc
Sử dụng describe để bắt đầu phần viết test với title trùng với tên component test.
Sử dụng async trước mỗi case test, mục đích là để các phần code không đồng bộ có thể kết thúc trước khi tiếp tục, nó tương tự với cách dùng async lúc viết code ý.
Trước khi chạy bất cứ case test nào trong Angular, bạn cần phải config Angular testbed. Nó giúp tạo 1 môi trường cho component đang được test. Bất cứ module, component, service nào mà component bạn đang test cần, thì đều phải được đưa vào trong testbed. Cuối cùng, sau khi config xong, bạn sẽ gọi đến hàm compileComponents().
Tiếp theo, chúng ta có 2 case test. Mình sẽ giải thích từng case một:
Case đầu tiên, chúng ta sẽ kiểm tra xem component có thực sự chứa text chúng ta kì vọng trong phần title hay không. Đầu tiên, chúng ta cần 1 instance của app.component, vì vậy chúng ta sử dụng hàm createComponent(), tạo 1 đối tượng fixture, cho phép chúng ta tạo 1 instance của component. Sau khi đã có instance của app.component, chúng ta có thể kiểm tra giá trị của thuộc tính title có giống với text chúng ta kì vọng hay không bằng hàm toEqual().
Test angular formBắt đầu với 1 file HTML chúng tôi có chứa form
Form này khá đơn giản nên không cần giải thích gì nhiều. Nút submit sẽ bị disabled nếu form invalid.
Tiếp theo đến contact.component.ts
Giờ đến file test
Trông phức tạp hơn nhiều so với file test trên nhỉ, đừng lo, mình sẽ giải thích cụ thể từng phần đây
Đầu tiên vẫn là phần import, không có gì khác lắm, ngoại trừ import thêm By để chọn element từ DOM.
Trong beforeEach(), ta dùng promise cho hàm compileComponent(), khi promise được resolved, chúng ta sẽ tạo instance của component trong này luôn, để đỡ phải viết lại trong mỗi case test.
Case đầu tiên chỉ đơn giản là kiểm tra giá trị thuộc tính text của component.
Case thứ 2 kiểm tra giá trị của biến submittedlà true khi hàm onSubmit() được gọi đến.
Case thứ 4 kiểm tra khi truyền vào những giá trị invalid, thuộc tính valid của form sẽ là false.
Cuối cùng, case thứ 5 ngược lại với case thứ 4, khi truyền vào những giá trị valid, thuộc tính valid của form sẽ là true.
Trước khi kết thúc bài viết, chúng ta sẽ đến với phần cuối cùng, là làm sao xử lý service khi component cần test sử dụng chúng.
Test component với serviceVí dụ một component sử dụng service
Component này sẽ gọi sang service để lấy danh sách users, lúc viết test thì việc lấy users từ đâu cũng ko quan trọng, nên ta có thể tạo component mocking cho service này.
Về cơ bản thì cũng giống mấy ví dụ trên, chỉ khác ở chỗ khi UserService được gọi đến, nó sẽ thay bằng UserServiceMock. UserServiceMock là một dummy service để trả về dummy data để chạy test. Vậy thôi, đó là cách bạn nên dùng để mock service cho component.
Nguồn: https://medium.com/swlh/angular-unit-testing-jasmine-karma-step-by-step-e3376d110ab4
Hướng Dẫn Viết Unit Test Trong React
Toàn bộ project để bạn tham khảo
Tại sao phải test?Rất hiển nhiên là chúng ta viết test nhằm mục đích hạn chế được càng nhiều lõi càng tốt, đảm bảo những gì chúng ta viết ra chạy đúng như chúng ta mong muốn. Một vài điểm trừ khi chúng ta phải viết test
Là nó tốn thời gian và tương đối khó khăn (dù là lập trình viên kinh nghiệm cũng gặp không ít vất vả khi mới bắt đầu viết test)
Test pass không có nghĩa ứng dụng, function của chúng ta chạy đúng 100%
Cũng đôi khi, test fail, nhưng ứng dụng, function vẫn chạy hoàn toàn bình thường
Trong vài trường hợp đặc biệt, chạy test trong CI có thể tốn tiền
Test cái gì?Test các chức năng, function của ứng dụng, những cái mà user sẽ sử dụng. Nó giúp chúng ta tự tin vỗ ngực, ứng dụng đáp ứng đúng nhu cầu sử dụng
Không test cái gìThích quan điểm của Kent C về việc không nên đi quá chi tiết việc hiện thực. Việc mà code nó hiện thực như thế nào chúng ta không quan tâm, user không quan tâm, chúng ta chỉ quan tâm đầu vào-đầu ra của một function.
Các thư viện của người khác viết cũng là thứ không cần thiết phải test, nó là trách nhiệm của người viết thư viện. Nếu không tin thì đừng dùng nó. Còn nếu thật sự có tâm bạn hãy hỗ trợ cho thư viện đó trên github bằng cách bổ sung test cho nó.
Một vài triết lý cá nhân khi testNhiều integration test, không dùng snapshot test, vài unit test, vài e-to-e test.
Hãy viết thật nhiều integration test, unit test thì tốt nhưng nó không thật sự là cách mà người dùng sử dụng ứng dụng. Việc test chi tiết code hiện thực ra sao với unit test rất dễ.
Integration test nên dùng mock (dữ liệu giả lập) ít nhất có thể
Không nên test những cái tiểu tiết như tên hàm, tên biến, cách khai báo biến số, hằng số có hợp lý.
Shallow vs mountMount là phần html, css, js thật sự khi chạy, như cách mà browser sẽ thấy, nhưng theo cách giả lập. Nó không có render, paint bất cứ thứ gì lên UI, nhưng làm như thể nó là browser thật sự và chạy code ngầm bên dưới.
Không bỏ thời gian ra để paint ra UI giúp test chạy nhanh hơn. Tuy nhiên nó vẫn chưa nhanh bằng shallow render
Đó là lý do bạn phải unmount và cleanup sau mỗi test, nếu không test này sẽ gây side-effect lên test kia.
Mount/render thường được sử dụng cho integration test và shallow sử dụng cho unit test.
Kiểu shallow render sẽ chỉ render ra một component đang test mà không bao gồm các component con, như vậy để tách biệt việc test trên từng component độc lập.
Lấy ví dụ như component cha, con như sau
Nếu chúng ta dùng shallow render component App, chúng ta sẽ nhận được DOM như sau, phần ChildComponent sẽ không bao gồm bộ “ruột” bên trong
Với mount, thì chúng ta có
react-testing-library là một thư viện khá ổn cho việc viết unit test react, tuy nhiên Enzyme là nền tảng cần nắm chắc, chúng ta sẽ đề cập nó trước
Enzyme Cài đặt npm install enzyme enzyme-to-json enzyme-adapter-react-16Sơ qua những gì chúng ta sẽ import
3 cái import đầu tiên là cho React và component đang test, sau đó đến phần của Enzyme, toJson là để chuyển shallow component của chúng ta ra thành JSON để lưu thành snapshot
Cuối cùng là Adapter để làm việc được với react 16
Thực hiện test chi tiết với EnzymeTrong component trên, chúng ta cố tình gõ sai chữ incremen, ứng dụng sẽ không chạy, nhưng khi chạy test thì vẫn pass
File test
Thứ nhất là cách viết test như vậy có vấn đề, chúng ko mô phỏng cách mà user sẽ sử dụng, chúng ta gọi thẳng increment().
Vậy người nông dân biết phải làm sao?
React-testing-libraryTừ thư viện react-testing-library, nó đưa ra một nguyên lý chung như sau
Test càng gần với thực tế sử dụng của ứng dụng, test càng đem đến sự tự tin cho chúng ta
Hãy tâm niệm nguyên lý này trong đầu, chúng ta sẽ còn bàn tiếp về nó
useStateHay bắt đầu test React hook, chúng ta đã và đang sử dụng nó nhiều hơn là class component
Prop sẽ được nhận từ component cha là App
Với nguyên lý như đã nói, chúng ta sẽ thực hiện test như thế nào
Thực hiện việc test
Vì không sử dụng shallow render, nên chúng ta phải gọi afterEach(cleanup) để dọn dẹp sau mỗi lực thực hiện test
getByText là phương thức nằm trong hàm render, còn vài kiểu query khác nữa, nhưng đây là kiểu mà chúng ta dùng nó nhiều nhất, có thể nói là đủ dùng.
useReducerReducer chúng ta sẽ test
Action
Cuối cùng là component sử dụng action và reducer đã định nghĩa
Component này sẽ đổi giá trị của stateprop từ false sang true bằng việc dispatch một SUCCESS action
Thực hiện test
Trước tiên chúng ta test cái reducer bên trong khối describe, thực hiện một test đơn giản với giá trị initial state và sau khi có action success.
Với ví dụ trên, reducer và action rất chi là đơn giản, bạn có thể nói không cần thực hiện unit test cho nó làm gì, nhưng trong thực tế sử dụng reducer sẽ không hề đơn giản thế, và việc test reducer là thực sự cần thiết, không những vậy, chúng ta còn phải test theo hướng chi tiết hiện thực bên trong.
useContextGiờ chúng ta đi đến việc test một component con có thể cập nhập context state trong component cha.
Thường thì context sẽ được khởi tạo trong một file riêng
Chúng ta sẽ cần một component cha, nắm giữ Context provider. Giá trị truyền vào cho provider sẽ là giá trị state và hàm setState
Component con, đây là component chúng ta muốn test
Lưu ý: các giá trị của state, khởi tạo, cập nhập điều nằm trong App.js, chúng ta chỉ truyền giá trị này xuống các component con thông qua context, mọi thứ điều thực hiện ở App, cái này quan trọng cần nhớ để hiểu lúc test
Với context chúng ta cũng không hề thay đổi cách làm như với useState, vẫn là tìm và đặt expect thông qua kết quả nhận được cuối cùng.
Tại sao lại như vậy?
How to Test React Components: the Complete Guide
Hướng Dẫn Viết Unit Test Cho Laravel (P2)
Chào mừng các bạn tới với phần 2 của serie hướng dẫn viết Unit Test trong Laravel. Sau phần 1 nói về những lợi ích mà Unit Test có thể mang lại cho ứng dụng của chúng ta, phần này mình sẽ hướng dẫn các bạn sử dụng PHPUnit và chạy những dòng Unit Test đầu tiên
Để có thể bắt đầu chạy test cho Laravel, trước tiên chúng ta cần một project Laravel nhỉ.
Bước 1: Tạo một ứng dụng Laravel mới:
composer create-project –prefer-dist laravel/laravel laravel-test-example
Với “laravel-test-example” là tên thư mục chứa project Laravel.
Chú ý: Nếu sử dụng MacOS/Linux, bạn phải set lại permission cho thử mục storage và bootstrap/cache
Bước 2: Bắt tay vào sử dụng PHPUNit
Thật may mắn cho chúng ta, PHPUnit sẽ được Laravel cài đặt sẵn khi khởi tạo ứng dụng mới.
Laravel sẽ khởi tạo sẵn chúng ta hai thư mục Là Feature và Unit. 2 thư mục này cũng sẽ tương ứng phương thức bạn test ứng dụng. Một số ví dụ về cách bạn sẽ test.
Feature Test: Bạn sẽ POST một dự liệu lên một REST endpoint, và kiểm tra xem API có trả về kết quả như mong muốn.
Unit Test: Khởi tạo một Model, và kiểm tra Model đó có đầy đủ các tham số như mong muốn hay không.
Ngoài thư mục ./tests/ thì bạn cần quan tâm tới file chúng tôi Nơi đây sẽ chứa toàn bộ các tham số để chạy PHPUnit.
Do trong serie này, mình sẽ hưỡng dẫn chủ yếu về Unit Test nên bạn không cần quá quan tâm tới thư mục Feature. Bên cạnh đó, bạn hoàn toàn có thể xoá bỏ Test Suite Feture đi.
./tests/FeatureSau khi save lại file, mở terminal lên và gõ command như sau
vendorbinphpunit
Để kiểm tra xem PHPUnit đã chạy được chưa. Và ta sẽ được kết quả như sau:
Nếu như sau khi chạy ta được dòng chữ xanh nghĩa là tất cả các Test của chúng ta chạy thành công. Còn nếu có vấn đề gì khi chạy test, ta sẽ nhận được thông báo:
Bước 3: Khởi tạo file Test
Bạn sử dụng command này đẻ tạo file Test:
php artisan make:test ExampleTest -unit
Ta thấy trong câu lệnh có sử dụng –unit. Bằng việc thêm –unit là sẽ bảo Laravel hãy tạo vào thư mục Unit thay vì Feature.
Quy ước khí khởi tạo file test:
Các file test cần được ánh xạ 1-1 với codebase và tên file được thêm chữ Test.
File trong thư mục app File trong thư mục test./app/Foo.php
./tests/Unit/FooTest.php
./app/Bar.php
./tests/Unit/BarTest.php
./app/Controller/Baz.php
./tests/Unit/Controller/BazTest.php
Chúng ta thử mở file chúng tôi có sẵn trong folder Unit
Trước tiên ta nhận thấy khởi đầu file là namespace. Các Namespace khác nhau sẽ giúp ta tránh được tình trạng lặp tên.
Nhìn vào function testBasicTest ta thấy function đó gọi tới một hàm assertTrue. Vậy assertTrue là gì ta ???
assertTrue là một Assertion, Mình có tham khảo định nghĩa Asertion trên wiki như sau:
Assertion là một câu lệnh khẳng định cho kết quả true – false, nó được đặt trong chương trình để chỉ cho lập trình viên thấy rằng xác nhận này luôn đúng ở vị trí đặt câu lệnh, hay nói một cách khác assertion kiểm tra một câu lệnh là đúng.
Nói tó gọn như sau, ta sẽ truyền thao số vào các hàm Assertion và hàm sẽ trả ra true và false. Nếu true thì pass test , và ngược lại.
PHPUnit có hơn 90 Assertion, nhưng đa phần mình sử dụng một phần rất nhỏ trong số đấy. Đây là một số assertion thuờng gặp:
All Rights Reserved
Phần 1: Cách Viết Unit Test Trong C#
Bài viết sẽ hướng dẫn các bạn từng bước tạo, chạy và tối ưu Unit tests sử dụng Microsoft unit test framework. Chúng ta sẽ bắt với 1 project C# đang trong giai đoạn phát triển, chúng ra sẽ tạo các tests và chạy nó, xem xét kết quả.
Yêu cầu – Visual Studio 2023 – xem code ví dụ tại Sample Project for Creating Unit Tests.
1.Tạo một unit test projectBạn cần tạo trước project với code ví dụ tại Sample Project for Creating Unit Tests.
Trong New Project dialog box, chọn Installed, chọn Visual C#, và chọn Test.
trong danh sách, chọn Unit Test Project.
Trong Name box, đặt tên là BankTest, và chọn OK.The BankTests project is added to the the Bank solution.
Trong BankTests project, add reference đến BankAccount (project bạn muốn viết Unit test) trong solution.
2.Tạo một test classTrong test project bạn đổi tên chúng tôi thành chúng tôi Sau đó bạn mở file chúng tôi và bạn sẽ thấy code bên dưới
using System; using Microsoft.VisualStudio.TestTools.UnitTesting; namespace BankTests { [TestClass] public class BankAccountTests { [TestMethod] public void TestMethod1() { } } }Yêu cầu của một Test class
attribute [TestClass] là bắt buộc trong Microsoft unit testing framework để được quản lý cho bất kỳ class nào chứa unit test methods
Mỏi test method phải chứa attribute [TestMethod].
3.Tạo test method đầu tiênChúng ta sẽ viết unit test cho Debit method trong class BankAccount . .
Bằng các phân tích, chúng ta thấy được Debit method có ít nhất 3 trường hợp cần test:
Method ném ra Exception ArgumentOutOfRangeException Nếu số tiền rút lớn hơn số tiền có trong tài khoản.
Method ném ra Exception ArgumentOutOfRangeException nếu số tiền rút nhỏ hơn 0.
Nếu việc kiểm tra 1 và 2 là đúng, method sẽ trừ số tiền rút ra khỏi tài khoản.
Trong thử nghiệm đầu tiên của chúng ta, chúng ta xác minh bằng một số tiền hợp lệ (số ít hơn số dư tài khoản và lớn hơn số không) và trả về một số tiền chúng ta đã xác định trước.
Tạo một test method Chúng ta cập nhật file chúng tôi như bên dưới
using Microsoft.VisualStudio.TestTools.UnitTesting; using BankAccountNS; namespace BankTests { [TestClass] public class BankAccountTests { [TestMethod] public void Debit_WithValidAmount_UpdatesBalance() { double beginningBalance = 11.99; double debitAmount = 4.55; double expected = 7.44; BankAccount account = new BankAccount("Mr. Bryan Walton", beginningBalance); account.Debit(debitAmount); double actual = account.Balance; Assert.AreEqual(expected, actual, 0.001, "Account not debited correctly"); } } }Trong test method Debit_WithValidAmount_UpdatesBalance chúng ta giả định tai khoản có beginningBalance = 11.99 và chúng ta sẽ rút debitAmount = 4.55 vậy số tiền còn lại phải là expected = 7.44. Chúng ta sử dụng AreEqual method để kiểm chứng tài khoản còn lại đúng với mong muốn.
Yêu cầu của Test method
Test method phải có attribute [TestMethod].
Test method phải return void.
Test method không có tham số.
4.Build và run test
Trong Build menu, chọn Build Solution. Nếu không có lỗi, Test Explorer window sẽ xuất hiện(bên trái), Debit_WithValidAmount_UpdatesBalance sẽ được liệt kê trong nhóm Not Run Tests.
Chọn Run All để run test. Trong Test Explorer status bar bắt đầu run. sau khi kết thúc test, thanh bar sẽ chuyển sang màu xanh lá test methods pass, hoặc đỏ nếu tests fail.
5.Sửa lỗi và chạy lại TestChúng ta cần kiểm tra lại method Debit.
Nguyên nhân lỗi
m_balance += amount;Cần sửa lại thành
m_balance -= amount;Chạy lại test Trong Test Explorer, chọn Run All để chạy lại hết các test method. Thanh trạng thái chuyển sanh màu xanh lá, và test được chuyển sang Passed Tests group.
Phần 2: Sử dụng Unit Test để cải thiện code
Bài viết Phần 1: Cách viết Unit Test trong C Sharp được dịch từ
https://msdn.microsoft.com/en-us/library/ms182532.aspx#BKMK_Create_a_unit_test_project
Hướng Dẫn Viết Unit Test Cho Laravel (P3)
Vậy là chung ta đã trai qua 2 phần về Unit Test, trong phần này mình sẽ hướng dẫn các bạn cách Mock và ứng dụng của Mock để test một chương trình.
Mocking là một kĩ thụât giúp ta khởi tạo một đối tượng ảo của một class có thật. Trong quá trình test, chúng ta chỉ sử dụng một số phương thức nhất định của đối tượng nào đó nhưng mà không muốn nó thực hiện, thay vào đó chỉ mong muốn nó được gọi tới và trà một giá trị. Mình đưa một ví dụ sẽ giúp bạn hiểu được câu nói kia có nghĩa là gì.
Theo ví du này ta thấy được rằng phương thức showValue() sẽ gọi tới cơ dở dữ liệu để hiển thị dữ liệu. Khi thực hiện việc test phương thức này, ta không muốn phải kết nối tới bất kì một cô sở dữ liệu. Việc kết nối sẽ khiển cho vịêc tốc độ chạy test giảm đi rõ rệt, bên cạnh đó, nếu không config chính xác bạn có thể hoàn toàn làm sai lệch cơ sở dữ liệu bạn đang dùng. Trong những trường hợp này, các tốt nhất là “fake” phương thức getValueFromDatabase() và làm giả dữ liệu trả ra từ phương thức đó. Việc này giúp chúng ta đẩm bảo rằng code sẽ không gọi tới cơ sở dữ liệu cũng nhưng dữ liệu sẽ nhất quán sau các lần test. Và đó chính là Mocking.
Ta có thể hiểu được là Mocking giúp chúng ta tạo các dữ liệu test với định dạng mà ta mong muốn, bên cạnh đó là điều khiển luồng dữ liệu hoạt động. Nhờ đó mà dữ liệu test sẽ không phải lưu trữ cùng như lấy ra từ database. Bên cạnh đó, Mocking tạo ra các đối tượng giả lập mà không cần đầy đủ chức năng như một đối tượng thật, chỉ những phương thức hoặc thuộc tính mà ta mong muốn sử dụng trong test.
Có 5 loại Mocking, mỗi loại sẽ được sử dụng trong các trường hợp cụ thể là :
DummiesDummy objects là các đối tượng được truyền vào các phương thức nhưng không được sử dụng. Thông thuơng, chúng dùng để truyền vào các phương thức để cho đủ các parameter cần thiết để một function có thể hoạt động.
SpiesTest Spies là một đối tượng để kiểu tra việc tương tác giữa các đối tượng với nhau. Khi mà kết quả của một test case phụ thuộc vào vịêc một phương thức nào đó có được tương tác đúng theo ý muốn của mình không như số lần phương thức đó được gọi, số lượng các tham số truyền vào có đúng hay không,…
Như ví dụ này, ta chỉ kỉêm tra rằng MyDependency có được gọi tới và được truyền một tham số là bar hay không.
StubsTest stubs là tạo ra một object với các tham số được lập trình sẵn. Thông thường, các stubs sẽ trả ra các giá trị mà sẽ được hard-code vào.
FakeTest Fake là một object được giản lược và có thể thao tác lên gần giống với những đối trượng ta tương tác lên ở “production”. Đây là một khái niệm tương đối trìu tượng với những cách hiểu khác nhau.
MockKhi mọi người nói tới Mock thì thuờng nhắc tới là Test Doubles. Test Doubles là một thuật ngữ ám chỉ vịêc bạn thay thế một đối tượng trên production dùng cho vịêc test. Đối tượng này vừa dùng để test chính bản thân cũng như giúp phương thức tổng có thể chạy được từ đó ta có thể test.
Một Mock Object thực sự chạy khi các phương thức của đối tượng này được gọi tới. Nó có thể throw các exception tương ứng nếu chúng được gọi mà không được chỉ định gọi hay kiểm tra số lưọng các tham số truyền vào có đúng như những gì chúng mong đợi.
Mặc dù PHPUnit cũng có thể tạo ra các Mock Object nhưng không cung cấp đẩy đủ chức năng như Mockery Thật may mắn là Laravel sẽ cài sẵn cho bạn Mockery nên chúng ta không phải mất công cài đặt hay config mà có thể bắt tay vào làm vịêc.
Trước tiên chúng ta cần một đoạn code mẫu để có thể sử dụng. Ta sẽ sử dụng 2 class là Math và Calculate. Class Math sẽ phụ thuộc và Calucule. Hay tạo một file với đường dẫn là “app/Math.php”
Chú ý: Khi viết code, luôn sử dụng dependency injections thông qua contructor thay vì vịêc khởi tạo chúng bằng cú pháp “new”. Vịêc này sẽ giúp chúng ta dễ dàng test hơn khi tạo các Mock.
Thêm một tip nhỏ là hay luôn sử dụng Facade thay các helper function của Laravel, vịêc này sẽ giúp ích cho chúng ta ít nhiều trong việc test đó. https://laravel.com/docs/master/mocking#mocking-facades
Chạy thử test trong terminator:
Vây là ta test và thành công. Nhưng tạo sao của chúng ta có 2 function assertion là assertTrue và assertEquals mà trong kết quả ta lại nhận được 3.
Không những vậy, ta còn kiểm tra rằng phương thức areaOfSquare được gọi duy nhất một lần trong getArea. Nếu chúng ta thay once bằng twice hoăc times({number}) thì test case sẽ báo lỗi.
Ngoài ra còn một số kĩ thụât và một số hàm Expectation mà bạn có tham khảo tại docs của Mockery: http://docs.mockery.io/en/latest/reference/expectations.html
All Rights Reserved
Tìm Hiểu Unit Testing Trongcore
Tìm hiểu Unit Testing trong chúng tôi Core
Unit test là gì?
Unit test đơn giản giúp chúng ta kiểm tra tính đúng đắn của một đơn vị code (hầu hết là các method) có làm việc như mong muốn không? Nó cho phép bạn kiểm tra các phương thức độc lập và kiểm tra các logic với các điều kiện dữ liệu khác nhau.
Tại sao phải dùng Unit test?
Một lý do lớn là unit test giúp dễ dàng thay đổi code hơn nhiều khi chúng ta chắc rằng chúng ta không thay đổi tính năng (refactoring) mà chỉ là tối ưu code thôi. Điều này có nghĩa là unit test giúp bạn có thể dễ dàng phát hiện ra những chỗ bị lỗi khi thay đổi code nhờ các test case báo fail ở bất kỳ function nào. Điều này rất tuyệt vời bởi vì các bug bị giảm đi rất nhiều trong ứng dụng của chúng ta ngay từ các đơn vị code nhỏ nhất. Bug càng phát hiện sớm càng giảm chi phí cho việc fix.
Unit test làm cho thiết kế tốt hơn
Unit test giúp ảnh hưởng đến thiết kế của chúng ta bằng cách luôn theo xu hướng giảm sự phụ thuộc giữa các module đến mức cao nhất. Nếu một số chỗ không thể test được thì tức là có sự phụ thuộc không hề nhẹ ở đây tức là thiết kế chưa được tối ưu cho việc test cũng như bảo trì (tightly coupled). Khi chúng ta thiết kế các class với unit test trong tư duy bạn sẽ tập trung vào việc tách ra các thành phần độc lập và có thể test được.
Trong một số trường hợp, bạn thường không phải nghĩ về thiết kế. Các unit test sẽ hướng dẫn bạn chắc chắn rằng các logic là đúng và các thành phần được thiết kế tách biệt và giảm sự phụ thuộc thông qua việc sử dụng các abstraction và dependency injection. Điều này có được nếu như chúng ta thiết kế tuân theo Single Reponsibility Principle (nguyên tắc S trong SOLID).
Tìm hiểu về test-driven development
Mình thường viết code ngắn và sau đó viết lên dần dần song song với việc viết các unit test và thực hành như vậy tôi gọi là tư duy hướng test (test driven thinking). Test-driven development (TDD) là một quy trình khác khi mà các test được viết trước cả khi viết code. Các test như là các đặc tả (spec) mà bạn phải viết test khi function chưa được hình thành. Ban đầu test sẽ fail vì chưa có code tồn tại. Sau đó bạn chỉ cần viết code để cho các test case đó pass.
Trong khi TDD thường hướng tới unit testing, unit testing thì lại không yêu cầu phải làm TDD. Tôi muốn các bạn rõ điều này vì nó hơi bị dễ nhầm lẫn giữa các term này.
Thêm unit test vào project
Một cách tiếp cận tốt để unit test trong .NET là thêm các test vào solution của chúng ta dưới dạng class library hoàn toàn tách biệt với các project code chính. Giờ chúng ta sẽ thêm mới một .NET Core Class library vào solution như hình.
Tiếp đến là thêm một unit test framework, mình sử dụng xUnit được hỗ trợ cho .NET core và làm việc với tính năng có sẵn trong Visual Studio là Test Runner. Nunit cũng là một framework phổ biến khác, có một số khác nhau giữa 2 thằng này nhưng tựu chung là nó gần như nhau.
Thêm các thư viện bổ sung để mock tức là giả lập các đối tượng với các logic khác nhau và dữ liệu khác nhau trong unit test. Điều này có thể tham khảo tại bài viết này về mock, stubbing và faking đối tượng ở đây. Chúng ta sẽ dành 1 phút để sử dụng thư viện Moq để giả lập các hành vi của một interface trong test case, đây là tính năng rất mạnh mẽ của unit test.
Cuối cùng chúng ta cần Add reference từ project test đến project code chính để truy cập các class cho việc test.
Viết unit test
Ok vậy chúng ta đã hiểu một số lý thuyết rồi, bao gồm có việc hiểu khái niệm unit test, tại sao phải dùng chúng. Chúng ta sẽ test ứng dụng với xUnit framework.
Test đầu tiên chúng ta sẽ sử dụng class TeamStatCalculator. Cụ thể là method GetTotalGoalsForSeason()
public class TeamStatCalculator { private readonly ITeamStatRepository _teamStatRepository; public TeamStatCalculator(ITeamStatRepository teamStatRepository) { _teamStatRepository = teamStatRepository; } public int GetTotalGoalsForSeason(int seasonId) { var teamStatsBySeason = _teamStatRepository } }Cả nhà thấy nó giống với cách viết của project TEDU-17 không? Đi vào chi tiết chúng ta có thể thấy phương thức này đơn giản là query từ _teamStatRepository và lấy ra danh sách các đội bóng theo mùa sau đó gọi hàm Sum() trong Linq để trả ra tổng bàn thắng cho toàn bộ. Quá dễ phải không nào?
Một điều ghi nhớ là chúng ta có thể thấy _teamStatRepository là một thành phần phụ thuộc được inject vào thông qua constructor. Trong thực tế, repository này có thể lấy dữ liệu từ database, một file hoặc một Rest API nào đó nhưng chúng ta không cần quan tâm vì chúng ta ủy thác việc lấy dữ liệu từ nguồn nào cho phần triển khai của IteamStatRepository interface. Chúng ta chỉ quan tâm là với một interface trừu tượng như thế nó có các phương thức trừu tượng và không dính gì đến phần triển khai. Chúng ta sẽ thấy vì sao điều này rất quan trọng ngay sau đây. Thêm nữa, nếu code test của bạn viết vào 1 file, mở database lên thông qua connection hoặc làm gì đó thông qua network thì nó sẽ thành test tích hợp mất (integration test) rồi. Cái này là phạm trù khác của test.
[Fact] public void GetTotalGoalsForSeason_returns_expected_goal_count() { var mockTeamStatRepo = new Mock(); mockTeamStatRepo { new TeamStatSummary {SeasonId = 1,Team = "team 1",GoalsFor=1}, new TeamStatSummary {SeasonId=1,Team = "team 2",GoalsFor=2}, new TeamStatSummary {SeasonId = 1,Team = "team 3",GoalsFor=3} }); var teamStatCalculator = new TeamStatCalculator(mockTeamStatRepo.Object); var result = teamStatCalculator.GetTotalGoalsForSeason(1); we should get this back from the method Assert.True(result==6); }Cấu trúc unit test
Unit test của chúng ta nên follow theo cách tiếp cận “AAA”:
Arrange làm các công việc cài đặt hay chuẩn bị dữ liệu cần thiết;
Act là thực thi function hay method cần được test và lấy kết quả.
Assert là verify kết quả trả (actual result) ra có đúng như mong muốn không (expected result)
Chúng ta có thể thấy một mớ test code xuất hiện trong phần Arrange. Đó là chúng ta tạo một mock cho IteamStatRepository và định nghĩa vài hành vi của nó cũng như dữ liệu sẽ trả về nếu gọi hàm FindAll(). Chúng ta sẽ inject đối tượng vừa mock vào TeamStatCalculator khi tạo ra nó.
Nếu chúng ta không cho phép inject Repository từ ngoài vào thì không thể giả lập được nó và setup được các hành vi như FindAll() trả về cái gì được mà nó sẽ bị đóng gói cứng ở trong triển khai của TeamStatRepository rồi, vậy là DI rất quan trọng cho unit test.
Khi setup hoàn thành, act sẽ đơn giản là gọi method cần test với GetTotalGoalsForSeason(1) tham số là 1 và nhận kết quả. Chỉ có một dòng nhỏ trong test nhưng nếu bạn hạy test khi debug bạn sẽ thấy nó thực sự kỳ diệu. Đây là sự kỳ diệu của IoC/DI. Chúng ta hoàn toàn nghịch đảo việc tạo repository và có thể khiến nó làm bất cứ điều gì.
Cuối cùng, trong phần assert của chúng ta, bạn biết sẽ có 6 bàn thắng trong test data và vì thế phương thức của bạn làm việc đúng.
Chạy unit test trong Visual Studio với Test Explorer và nhận kết quả pass màu xanh.
Thay vào đó, bạn có thể run test với command line trong công cụ CLI mới. Trong thư mục project đơn giản là run dotnet test để chạy test runner từ command line.
Unit test controller
Controller là một thành phần quan trọng trong ứng dụng chúng tôi Core, chúng ta cũng phải test nó để đảm bảo toàn bộ luồng hoạt động trơn chu. Chúng ta thường muốn controller của mình nhỏ và không thực hiện bất cứ logic nào hoặc là truy cập dữ liệu. Chúng ta chỉ cần verify là controller action nhận đầu vào sau trả ra kết quả là được.
Hãy viết unit test cho method Index() trong HomeController
public IActionResult Index([FromServices] IGameRepository gameRepository) { var model = new IndexViewModel { Players = _playerRepository.GetAll().ToList(), Games = gameRepository.GetTodaysGames().ToList() }; return View(model); }Sau đó chúng ta có hàm test:
[Fact] public void Index_returns_viewresult_with_list_of_players_and_games() { var mockPlayerRepo = new Mock(); { new Player {Name = "Sidney Crosby"}, new Player {Name="Patrick Kane"} }); var mockGameRepo = new Mock(); { new Game { HomeTeam = "Montreal Canadiens", AwayTeam = "Toronto Maple Leafs", Date = DateTime.Today}, new Game { HomeTeam = "Calgary Flames", AwayTeam = "Vancouver Canucks", Date = DateTime.Today}, new Game { HomeTeam = "Los Angeles Kings", AwayTeam = "Anaheim Ducks", Date = DateTime.Today}, }); var controller = new HomeController(mockPlayerRepo.Object); var result = controller.Index(mockGameRepo.Object); var viewResult = Assert.IsType(result); var model = Assert.IsAssignableFrom(viewResult.ViewData.Model); Assert.Equal(2, model.Players.Count); Assert.Equal(3, model.Games.Count); }Không có gì khác biệt hơn khi chúng ta test TeamStatCalculator. Chúng ta cũng vẫn dùng “AAA” và mock vài thành phần phụ thuộc cần có cho controller này như PlayerRepository hay GameRepository, inject chúng vào controller sau đó gọi hàm Index(). Sự khác nhau lớn nhất là phần assert nơi chúng ta kiểm tra ViewResult và model để chắc chắn là nó trả về kết quả đúng. Hơn nữa, đảm bảo rằng controller cũng được test.
Tóm lại
Ok vậy là chúng ta đã tìm hiểu về Unit test trong .NET Core thông qua ví dụ, chúng ta thấy rằng việc viết unit test rất quan trọng và quan trọng hơn là code của chúng ta phải có IoC và DI để hỗ trợ việc test. Nó giúp ứng dụng của chúng ta tăng chất lượng code giảm rủi ro khi thay đổi và giảm effort test lại khi có thay đổi.
Những lợi ích này là không thể phủ nhận, để các bạn nắm rõ hơn về unit test trong dự án thực tế các bạn có thể tham gia khóa học Kỹ thuật Unit test cho .NET Developer tại TEDU
Tác giả: Bạch Ngọc Toàn
Chú ý: Tất cả các bài viết trên chúng tôi đều thuộc bản quyền TEDU, yêu cầu dẫn nguồn khi trích lại trên website khác.
Cập nhật thông tin chi tiết về Hướng Dẫn Viết Unit Test Trong Angular trên website Ezlearning.edu.vn. Hy vọng nội dung bài viết sẽ đáp ứng được nhu cầu của bạn, chúng tôi sẽ thường xuyên cập nhật mới nội dung để bạn nhận được thông tin nhanh chóng và chính xác nhất. Chúc bạn một ngày tốt lành!