Mockito là một framework phổ biến được dùng bởi các Java developer và vô cùng mạnh mẽ chúng ta đều biết no là một thư viện vô cùng hữu ích trong Unit Test. Nó là cách tốt nhất để tạo ra các mock (mô phỏng/giả lập) và sử dụng nó trong Java và Kotlin. Nhưng do là một thư viện được xây dựng cho Java, bởi thế nên nó sẽ có những hạn chế nhất định khi sử dụng cho Kotlin. Đó cũng là lý do cho sự ra đời của MockK - một mocking framework được viết hoàn toàn bằng Kotlin và dành riêng cho Kotlin. Giống như Mockito, MockK cho phép bạn tạo mock và stub các đối tượng bên trong test code của mình. Việc mock các object cho phép bạn test trên các đối tượng độc lập. Bất kỳ sự phụ thuộc vào object nào khi test đều có thể được mock để cung cấp các điều kiện cố định, do đó, đảm bảo các test luôn ổn định và rõ ràng.
1. Annotation
Đầu tiên chúng ta hãy làm quen với những Annotation thường được sử dụng để hỗ trợ việc test. Tất nhiên sẽ còn một số Annotation khác mà mình không đề cập ở đây các bạn có thể đọc thêm trên trang doc của mockK mình để bên dưới để tìm hiểu thêm.
@MockK
Mock là phương pháp tạo ra 1 đối tượng giả. Chúng ta sẽ dùng đối tượng này để test những phần logic của app mà nó có tham gia. Trên thực tế, những đối tượng được mock thường là những phần để khởi tạo tường minh nó thường khó khăn hoặc tốn tài nguyên, hay cũng có thể là những đối tượng mà chủ đích của ta không phải là test logic của nó nhưng vẫn cần có nó.
@InjectMockKs
Tạo một thể hiện của lớp và đưa các đối tượng giả được tạo ra với các chú thích @Mock(hoặc @Spy) vào thể hiện này.
@RelaxedMockK
Nếu bạn muốn tạo một lớp giả nhưng không muốn giả lập các phương thức trong nó vì chúng không liên quan đến bài test. RelaxedMockK sẽ giúp bạn trả về các đối tượng giả có chứa các giá trị trống. Nhưng hãy nhớ rằng, relaxedmockk cũng có thể dẫn đến lỗi, khi bạn quên bắt chước một phương pháp bắt buộc. Mình khuyên các bạn nên sử dụng các chế độ mô phỏng nghiêm ngặt theo mặc định và chỉ những chế độ thoải mái nếu bạn thực sự cần nó.
@SpyK
Tương tự như mock thì spy cũng dùng để tạo ra 1 đối tượng. Tuy nhiên đối tượng này không phải "giả hoàn toàn". Chúng ta cần phân biệt giữa mock và spy. Khi dùng mock tạo đối tượng, thì đối tượng đó hoàn toàn không phải đối tượng thật. Chúng ta có thể truy cập đến các biến và các phương thức của nó, tuy nhiên chúng đều là null hoặc không thực hiện gì. Đó chính là lý do chúng ta cần giả lập giá trị. Còn với spy, chúng ta vẫn có thể giả định các biến hay kết quả trả về của đối tượng giống như mock. Tuy nhiên, với những phần mà chúng ta không giả định thì chúng vẫn sẽ mang những giá trị hay sự thực thi mà chúng ta đã code cho nó.
@Before
Các phương thức được chú thích bằng chú thích @Before được thực thi trước mỗi lần kiểm tra. Sẽ có lợi khi chúng ta muốn thực thi một số mã lặp lại trước khi chạy thử nghiệm như chúng ta có thể mô phỏng các đối tượng hoặc khởi tạo cần thiết cho các chú thích.
@Test
Anotation dùng cho function có nhiệm vụ test. Chúng ta sẽ viết test code trong funcction này.
@After
Các phương thức được chú thích bằng chú thích @ after được thực thi sau mỗi lần chạy thử nghiệm. Nó rất hữu ích khi chúng ta muốn bỏ chọn tất cả các đối tượng mà chúng ta đã mock.
class TrafficSystem {
lateinit var car1: Car
lateinit var car2: Car
lateinit var car3: Car
}
class CarTest {
@MockK
lateinit var car1: Car
@RelaxedMockK
lateinit var car2: Car
@MockK(relaxUnitFun = true)
lateinit var car3: Car
@SpyK
var car4 = Car()
@InjectMockKs
var trafficSystem = TrafficSystem()
@Before
fun setUp() = MockKAnnotations.init(this, relaxUnitFun = true)
// turn relaxUnitFun on for all mocks
@Test
fun calculateAddsValues1() {
// ... use car1, car2, car3 and car4
}
@Affter
fun tearDown() {
unmockkAll()
}
}
2. Hàm số
mockk(): Là một cách gọi khác của Annotation MockK
spyk(): Là một cách gọi khác của Annotation SpyK
every: bắt đầu một khối sơ khai
answers { code }: chỉ định rằng các câu trả lời cuộc gọi phù hợp với một khối mã có phạm vi answer scope
returns value: chỉ định rằng cuộc gọi đã so khớp trả về một giá trị được chỉ định
throws ex: chỉ định rằng cuộc gọi đã so khớp ném ra một ngoại lệ
assertEquals: Khẳng định rằng hai đối tượng bằng nhau.
verify: Kiểm tra các function trong khối xảy ra ít hất 1 lần
verify(atLeast = 3): Kiểm tra function được gọi ít nhất 3 lần
verify(atMost = 2): Kiểm tra function được gọi nhiều nhất 3 lần
verify(exactly = 1): Kiểm tra function được gọi đúng 1 lần
verifyAll: xác minh rằng tất cả các cuộc gọi đã xảy ra mà không cần kiểm tra thứ tự của chúng.
verifySequence: xác minh rằng các cuộc gọi đã xảy ra theo một trình tự được chỉ định.
verifySequence: xác minh rằng các cuộc gọi đã xảy ra theo một thứ tự cụ thể.
wasNot Called: xác minh rằng mô hình (hoặc danh sách mô phỏng) hoàn toàn không được gọi.
confirmVerified: Để kiểm tra kỹ xem tất cả các cuộc gọi đã được xác minh bởi các verify... cấu trúc hay chưa. Sẽ không có ý nghĩa gì khi sử dụng nó cho verifySequence và verifyAll, vì các phương pháp xác minh này đã hoàn toàn bao gồm tất cả các cuộc gọi có xác minh.
class MockedClass {
fun sum(a: Int, b: Int) = a + b
}
val obj = mockk<MockedClass>()
val slot = slot<Int>()
every {
obj.sum(any(), capture(slot))
} answers {
1 + firstArg<Int>() + slot.captured
}
obj.sum(1, 2) // returns 4
obj.sum(1, 3) // returns 5
obj.sum(2, 2) // returns 5
verifyAll {
obj.sum(1, 3)
obj.sum(1, 2)
obj.sum(2, 2)
}
verifySequence {
obj.sum(1, 2)
obj.sum(1, 3)
obj.sum(2, 2)
}
verifyOrder {
obj.sum(1, 2)
obj.sum(2, 2)
}
val obj2 = mockk<MockedClass>()
val obj3 = mockk<MockedClass>()
verify {
listOf(obj2, obj3) wasNot Called
}
confirmVerified(obj)
Có rất nhiều câu hỏi liên quan đến việc dọn dẹp các mock sao cho chính xác sau khi thực hiện xong các test case. Mình nhận thấy rằng đây là một chủ đề quan trọng và muốn đưa ra một số giải pháp cho việc này.
clearAllMocks: Xóa tất cả các chế độ giả. Nếu bạn có những bài kiểm tra tuần tự đơn giản thì đây sẽ là một lựa chọn tối ưu.
clearMocks: Xóa các chế độ thông thường hoặc đối tượng được chỉ định cụ thể.
clearStaticMockk, clearConstructorMockk: Cũng tương tự như vậy làm sạch các mocks static hoặc constructor được chỉ định cụ thể.
unmockkObject: Trả về chuyển đổi trở lại của mô hình chính quy hoặc đối tượng cụ thể.
unmockkAll: Một lần nữa có lẽ nếu bạn chỉ có các bài kiểm tra tuần tự và cần đưa mọi thứ trở lại trạng thái ban đầu thì đây là lựa chọn tốt nhất.
3. Tệp cài đặt
Để điều chỉnh các tham số trên toàn cầu, có một số cài đặt bạn có thể chỉ định trong tệp tài nguyên.
Cách sử dụng:
1. Tạo một io/mockk/settings.properties tệp trong src/main/resources.
2. Đặt bất kỳ tùy chọn nào sau đây:
3. relaxed=true|false
4. relaxUnitFun=true|false
5. recordPrivateCalls=true|false
6. stackTracesOnVerify=true|false
7. stackTracesAlignment=left|center
stackTracesAlignment xác định xem nên căn chỉnh các dấu vết ngăn xếp ở giữa (mặc định) hay ở bên trái (phù hợp hơn với JVM stackTraces thông thường).
4. Cách viết test case
Mình sẽ làm một demo cơ bản để mọi người hình dung về việc test như thế nào. Còn nếu muốn tìm hiểu test đầy đủ cho một Project các bạn có thể tham khảo bài viết này của mình.
StringChecker
Class StringChecker {
fun isTextEmpty(text : String) : Boolean{
return TextUtils.isEmpty(text)
}
}
Đối với mỗi bài kiểm tra, mình thường có 3 bước cần thiết liên quan đó là
1. Give
2. When
3. Then
Class StringCheckerTest {
lateint var stringChecker : StringChecker
@Before
fun setUp() {
MockKAnnotations.init(this, relaxUnitFun = true)
mockkStatic(TextUtils::class)
}
@Test
fun stringChecker_Is_Empty() {
//Given
stringChecker = mockk< stringChecker >()
every { TextUtils.isEmpty(any()) } returns true
// stubb the call to method
//when
val result = stringChecker. isTextEmpty(“test”)
//then
assert(result, true)
}
}
Như đã đề cập ở trên, 3 bước trong khi viết bài kiểm tra, chúng ta hãy thảo luận ở đây. Bạn có thể thấy trong phương pháp kiểm tra đơn vị ở trên mà chúng được đề cập.
Trong khối đã cho, chúng tôi cung cấp các yêu cầu để viết các bài kiểm tra đơn vị.
Trong khi khối, chúng ta gọi phương thức thực sự là lớp.
Trong khối sau đó, chúng tôi xác minh xem thử nghiệm có hoạt động chính xác hay không.
5. Tổng kết
Trong bài viết này, chúng ta đã thảo luận về các tính năng quan trọng nhất của MockK. MockK là một thư viện mạnh mẽ cho ngôn ngữ Kotlin và cung cấp rất nhiều tính năng hữu ích. Để biết thêm thông tin về MockK, chúng ta có thể kiểm tra tài liệu trên trang web MockK .
Comments