07/09/2018, 17:20

Swift - Test-Driven Development (TDD) - Chapter 1 - Part 1a - Unit Test

Swift - Test-Driven Development (TDD) - Chapter 1 - Part 1a - Unit Test Phần này mục đích mình sẽ tập trung vào việc giúp mọi người làm quen với unit test. Và chắc chắn cũng có Test-Driven Development (TDD) nơi mà phần test được viết trước khi implement code. 1. Xây dựng Automatic unit test: ...

Swift - Test-Driven Development (TDD) - Chapter 1 - Part 1a - Unit Test

Phần này mục đích mình sẽ tập trung vào việc giúp mọi người làm quen với unit test. Và chắc chắn cũng có Test-Driven Development (TDD) nơi mà phần test được viết trước khi implement code.

1. Xây dựng Automatic unit test:

Automatic unit test giống như Automation Testing vậy. Nó execute code mà không cần navigate đến màn hình đó để test. Các bạn tạo 1 project như sau:
alt text

Rồi vào thư mục test, các bạn sẽ thấy có 2 file sau:
alt text

a. Phân tích tổng thể

Cái bạn thấy ở đây là 1 test case. 1 test case là 1 class bao gồm nhiều phần tests nhỏ. Mỗi test case cần import XCTest framework. Nó sẽ define class XCTestCase và phần test assertions mà mình sẽ nói ở phần sau.

Dòng thứ hai import module - chính là nơi mà các bạn code ra app.

Mặc định: class, struct, enum và các method là internal.

Để nắm rõ phần này, các bạn nên xem lại Access Control ở đây. Điều này có nghĩa chúng có thể được accessed bên trong module trong khi code test ở bên ngoài module.

Để có thể viết code test, các bạn thêm keyword @testable, keyword này làm cho các elements internal của module có thể truy cập được vào test case.

Các bạn xem hình sau:
alt text

b. Phân tích bên trong code:

b1. setUp()

override func setUp() {
       super.setUp()
       // Put setup code here. This method is called ...
}

Đây là method được gọi TRƯỚC khi run từng test method trong class. Ở đây các bạn có thể set up những code nên run trước khi chạy mỗi test-case

b2. tearDown()

override func tearDown() {
       // Put teardown code here. This method is called ...
       super.tearDown()
}

Ngược lại với setUp, đây là method được gọi SAU khi run từng test method trong class. Ở đây các bạn có thể set up những code nên run sau khi chạy mỗi test-case như clean up data hay gì đó.

b3. testExample()

func testExample() {
       // This is an example of a functional test case.
       // Use XCTAssert and related functions to verify your ...
}

Đây là method để test bình thường, mình sẽ đi sâu method này rất nhiều.

b4. testPerformanceExample

func testPerformanceExample() {
       // This is an example of a performance test case.
       self.measure {
         // Put the code you want to measure the time of here.
       }
}

Đây là method về performance. Nó được sử dụng để test method hay function mà liên quan đến thời gian tối đa cho phép (time-critical computations). Code mà bạn để trong self.measure sẽ được gọi 10 lần và thời gian trung bình sẽ được tính toán. Performance test có thể hữu ích khi ta implement hay improve một thuật toán phức tạp nào đó và đảm bảo là performance của nó không bị giảm.

2. Implement Test đầu tiên:

Điều trước tiên các bạn cần nhớ là phải có prefix text, nếu không Swift sẽ không tìm thấy chúng. Đây cũng là một trong những cách disable test, remove prefix text này, bên cạnh những cách khác sẽ đề cập sau.

Bước 1:
Chèn đoạn code sau vào ViewController như trong hình:

func numberOfVowels(in string: String) -> Int {
     let vowels: [Character] = ["a", "e", "i", "o", "u",
                                "A", "E", "I", "O", "U"]
     var numberOfVowels = 0
     for character in string {
       if vowels.contains(character) {
         numberOfVowels += 1
        } 
    }
    return numberOfVowels
}

Mình chỉ note 1 chút là ta cần có [Character], nếu không sẽ bị lầm sang [String]. Còn logic code chắc mọi người đọc cũng dễ hiểu.

alt text

Bước 2:
Qua bên file Test.swift, add 1 func như sau:

func test_NumberOfVowels_WhenPassedBuiKhanhDuy_ReturnsFour() {
     let viewController = ViewController()
     let string = "BuiKhanhDuy"
     let numberOfVowels = viewController.numberOfVowels(in: string)
     XCTAssertEqual(numberOfVowels, 4,
                    "should find 4 vowels in BuiKhanhDuy")
}

Ở đây, mình đã tạo một instance của ViewController và define 1 chuỗi string để dùng test

alt text

Bước 3:
Vào Product/ Test hoặc Command+U, nếu pass, các bạn sẽ thấy kiểu như:

alt text

Có check mark màu xanh ở phía trước. Ngoài ra, các bạn để ý time: 0.000 sec của phần performanceTest, nó tính toán thời gian trung bình thực hiện phần bên trong self.measure (hiện tại chẳng có gì) rồi chia cho 10 ra được kết quả.

Nếu fail thì sao:
alt text

Thì như trên, nó sẽ báo testcase nào bị fail, nguyên nhân thất bại và ý nghĩa lịch sử ^^ (đùa chút)

Trước khi mình qua phần tiếp, mình refactor code 1 chút:
Thay đoạn code numberOfVowels(in:) bằng đoạn code sau:

func numberOfVowels(in string: String) -> Int {
     let vowels: [Character] = ["a", "e", "i", "o", "u",
                                "A", "E", "I", "O", "U"]
     return string.characters.reduce(0) {
       $0 + (vowels.contains($1) ? 1 : 0)
    } 
}

Theo định nghĩa của Apple, phần reduce được khai báo như sau:
func reduce<Result>(_ initialResult: Result, _ nextPartialResult: (Result, Character) throws -> Result) rethrows -> Result
Đoạn refactor này cũng khá dễ hiểu, cho nên mình không giải thích nhiều. Nếu bạn nào thắc mắc thì comment nha.

Như vậy, Product/ Test lại nó sẽ work như bình thường.

0