Unit Test trong Swift với Quick và Nimble

Trong phát triển phần mềm, đảm bảo chất lượng mã nguồn thông qua kiểm thử tự động là một bước quan trọng. Bài viết này sẽ hướng dẫn bạn cách sử dụng hai framework phổ biến QuickNimble để viết Unit Test trong Swift theo phong cách Behavior-Driven Development (BDD).

1. Unit Test là gì?

Unit Test là kiểm thử các thành phần nhỏ nhất của chương trình (thường là hàm hoặc lớp) để đảm bảo chúng hoạt động đúng với yêu cầu. Nó giúp phát hiện lỗi sớm trong quá trình phát triển và đảm bảo các thay đổi không làm hỏng các phần khác của ứng dụng.

2. Quick và Nimble là gì?

  • Quick: Một framework kiểm thử theo phong cách BDD giúp tổ chức các test case một cách rõ ràng và dễ đọc. Quick sử dụng cú pháp gần gũi với ngôn ngữ tự nhiên, giúp việc đọc hiểu các bài test dễ dàng hơn.
  • Nimble: Thư viện hỗ trợ các cú pháp matcher giúp so sánh kết quả test với kết quả mong đợi theo cách tự nhiên và dễ hiểu.

3. Cài đặt Quick và Nimble

Để bắt đầu, chúng ta cần thêm các thư viện này vào dự án Swift thông qua CocoaPods.

  • Mở terminal và điều hướng đến thư mục dự án của bạn.
  • Chạy lệnh sau để tạo file Podfile nếu chưa có
pod init
  • Mở Podfile và thêm các dòng sau vào mục target 'YourProjectTests'
pod 'Quick'
pod 'Nimble'
  • Chạy lệnh sau để cài đặt
pod install

4. Tạo một Unit Test với Quick và Nimble

Giả sử chúng ta có một lớp Calculator đơn giản với một phương thức cộng hai số:

class Calculator {
    func add(_ a: Int, _ b: Int) -> Int {
        return a + b
    }
}

Để viết test cho phương thức add này, bạn có thể làm theo các bước sau:

  • Tạo file test: Xcode sẽ tự động tạo ra một file test khi bạn tạo dự án. Nếu chưa có, bạn có thể tạo một file mới bằng cách nhấn chuột phải vào thư mục Tests và chọn New File > Unit Test Case Class.
  • Import Quick và Nimble: Trong file test của bạn, import cả hai thư viện
import Quick 
import Nimble
  • Viết test case: Dưới đây là cách viết Unit Test cho lớp Calculator
class CalculatorSpec: QuickSpec {
    override func spec() {
        describe("Calculator") {
            var calculator: Calculator!

            beforeEach {
                calculator = Calculator()
            }

            it("should return the sum of two numbers") {
                let result = calculator.add(2, 3)
                expect(result).to(equal(5))
            }
        }
    }
}

5. Giải thích chi tiết

  • describe: Để nhóm các test case có liên quan đến nhau. Trong ví dụ này, chúng ta nhóm tất cả các test liên quan đến lớp Calculator.
  • beforeEach: Được chạy trước mỗi test case, dùng để khởi tạo đối tượng Calculator trước khi kiểm tra.
  • it: Mô tả một test case cụ thể. Trong ví dụ này, chúng ta kiểm tra phương thức add có hoạt động chính xác hay không.
  • expect: Là cú pháp của Nimble, giúp so sánh kết quả thực tế (result) với kết quả mong đợi (5).

6. Mocking trong Unit Test

Khi test các thành phần phức tạp, bạn có thể cần dùng mock để mô phỏng hành vi của đối tượng phụ thuộc. Ví dụ, nếu Calculator có một phụ thuộc Logger, bạn có thể dùng mock như sau:

protocol Logger {
    func log(_ message: String)
}

class Calculator {
    var logger: Logger

    init(logger: Logger) {
        self.logger = logger
    }

    func add(_ a: Int, _ b: Int) -> Int {
        let result = a + b
        logger.log("Added \(a) to \(b) to get \(result)")
        return result
    }
}

Tạo mock:

class MockLogger: Logger {
    var logMessage: String?

    func log(_ message: String) {
        logMessage = message
    }
}

Test với mock:

class CalculatorSpec: QuickSpec {
    override func spec() {
        describe("Calculator") {
            var calculator: Calculator!
            var mockLogger: MockLogger!

            beforeEach {
                mockLogger = MockLogger()
                calculator = Calculator(logger: mockLogger)
            }

            it("should log the correct message when adding numbers") {
                calculator.add(2, 3)
                expect(mockLogger.logMessage).to(equal("Added 2 to 3 to get 5"))
            }
        }
    }
}

7. Kiểm thử Asynchronous

Quick và Nimble cũng hỗ trợ kiểm thử các hành vi bất đồng bộ, chẳng hạn như các tác vụ mạng.

class NetworkManager {
    func fetchData(completion: @escaping (String) -> Void) {
        DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
            completion("Success")
        }
    }
}

Viết test bất đồng bộ:

class NetworkManagerSpec: QuickSpec {
    override func spec() {
        describe("NetworkManager") {
            var networkManager: NetworkManager!

            beforeEach {
                networkManager = NetworkManager()
            }

            it("should return 'Success' after a delay") {
                waitUntil(timeout: 2) { done in
                    networkManager.fetchData { data in
                        expect(data).to(equal("Success"))
                        done()
                    }
                }
            }
        }
    }
}

waitUntil: Được dùng để đợi một khối mã bất đồng bộ hoàn tất. Khi done() được gọi, test sẽ kết thúc.

8. Kết luận

Quick và Nimble không chỉ giúp việc viết Unit Test trong Swift trở nên trực quan, dễ đọc mà còn giúp quản lý các bài test phức tạp hơn như sử dụng mock hoặc xử lý các tác vụ bất đồng bộ. Sử dụng chúng sẽ giúp bạn dễ dàng phát hiện lỗi sớm và tăng cường chất lượng mã nguồn.

Nếu bạn muốn kiểm thử hiệu năng hoặc viết test với nhiều case, Quick cũng hỗ trợ các công cụ mạnh mẽ khác mà bạn có thể khám phá thêm.

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Related Posts

Unit Test cho thư viện CocoaPods bằng Podspec

Trong bài viết này, bạn sẽ học cách tạo một thư viện CocoaPods, cấu hình Unit Test sử dụng podspec, cách tích hợp vào dự án chính, và cách chạy…

Read more
5 1 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất
Phản hồi nội tuyến
Xem tất cả bình luận