Swift - Test-Driven Development (TDD) - Chapter 1 - Part 2b - Understanding TDD

Swift - Test-Driven Development (TDD) - Chapter 1 - Part 2b - Understanding TDD

====
Updated ngày 30/06

Updated một chút: Vì những bất tiện và không rõ ràng về thông tin của kipalog, mình mới dọn về nhà mới ở đây, thoải mái, đẹp đẽ rõ ràng hơn kipalog. Mong các bạn có thể theo dõi tại trang web của mình để cập nhật thêm nha

Mình mới viết một loạt bài về chủ đề Clean Architecture và Service Locator. Các bạn có thể theo dõi.

Page trên facebook ở đây: ở đây

Phần 2b này, chúng ta sẽ đi qua một ví dụ khác để có cái nhìn rõ hơn về cấu trúc cũng như quy tắc của TDD.

Ví dụ 2

1. Red

Như đã đề cập ở phần trước, ta cần 1 cái test khác, bởi vì production code chỉ work với 1 input cụ thể. Feature của mình cần phải work với tất cả các input. Add vào phần test, đoạn code sau:

func test_MakeHeadline_ReturnsStringWithEachWordStartCapital2() {
     let string = "Here is another Example"
     let headline = viewController.makeHeadline(from: string)
     XCTAssertEqual(headline, "Here Is Another Example")
   }

Xong rồi Run test nào. Đương nhiên là nó fail gòy. Fail này là do XCAssert. Không phải do phần code, cho nên ta coi như đã xong phần Red. Qua phần Green nào.

2. Green

Bây giờ quay trở lại phần code production và thay dòng hard-code bằng đoạn code sau:

func makeHeadline(from string: String) -> String {
    let words = string.components(separatedBy: " ")
    var headline = ""
    for var word in words {
      let firstCharacter = word.remove(at: word.startIndex)
      headline += "\(String(firstCharacter).uppercased())\(word) "
    }
    headline.remove(at: headline.index(before: headline.endIndex))
    return headline
  }

Mình sẽ phân tích từng phần:

  • let words = string.components(separatedBy: " ") đoạn này lấy từng từ trong string truyền vào
  • trong vòng lặp for, lấy từng từ một. Lấy kí tự đầu tiên ra bằng dòng let firstCharacter = word.remove(at: word.startIndex)
  • viết hoa nó lên String(firstCharacter).uppercased() xong rồi ghép nó với khúc phía sau "\(String(firstCharacter).uppercased())\(word) " -> để ý có space ở đây để cách với từ tiếp theo.
  • ghép từng từ lại thành 1 câu trở lại headline += "\(String(firstCharacter).uppercased())\(word) "
  • xoá space sau cùng headline.remove(at: headline.index(before: headline.endIndex)) và return lại kết quả.

Run test và kết quả pass hết. Phần tiếp theo chính là Refactor.

Lưu ý phần refactor cần phải có. Nếu bạn thấy mình không có gì để refactor tức là bạn vẫn chưa xong.

3. Refactor

Nhìn vào 2 đoạn code mà bạn vừa viết dưới đây:
alt text

Nhìn vào, thật sự mình chả hiểu input là gì? Output là gì? Mặc dù chính mình vừa mới viết xong ^^ Ta refactor như sau:

func test_MakeHeadline_ReturnsStringWithEachWordStartCapital() {
     let input           = "this is A test headline"
     let expectedOutput  = "This Is A Test Headline"
     let headline = viewController.makeHeadline(from: input)
     XCTAssertEqual(headline, expectedOutput)
   }

func test_MakeHeadline_ReturnsStringWithEachWordStartCapital2() {
     let input           = "Here is another Example"
     let expectedOutput  = "Here Is Another Example"
     let headline = viewController.makeHeadline(from: input)
     XCTAssertEqual(headline, expectedOutput)
   }

Các bạn đã thấy nó chia ra rõ ràng, input là gì? expectedOutput là gì? dòng nào thực hiện hàm v.v. Nó tuân theo cấu trúc logic mà mình đã bàn ở phần một: precondition, invocation, and assertion.

alt text

4. Check test code

Về cơ bản, những lần thay đổi code ở phần test không cần phải được tested. Nhưng làm sao ta biết được liệu những test case này có được update kịp thời không? Hay là vẫn test như trước đó ? Tốt nhất là nên thử rằng tests ok. Có nghĩa là ta làm nó fail đi. Comment dòng code sau cùng trong production code đi:

// headline.remove(at: headline.index(before: headline.endIndex))

Như vậy, string được trả về sẽ dư 1 space. Run nào. Oh Yeahhh Fail cả hai. Như vậy là ok.

Note: Nếu bạn để ý, 1 test failed sẽ không làm dừng lại quá trình test. Nhìn chung là vậy, nhưng bạn cũng có thể thay đổi continueAfterFailure thành false in setUp().

Trước khi kết bài, mình nhìn lại production code một chút. Nhìn nó như kiểu Objective-C translate qua Swift vậy. Mình cho nó nhìn dễ thương như Swift một chút, như sau:

func makeHeadline(from string: String) -> String {
     let words = string.components(separatedBy: " ")
     let headlineWords = words.map { (word) -> String in
       var mutableWord = word
       let first = mutableWord.remove(at: mutableWord.startIndex)
       return String(first).uppercased() + mutableWord
       }
     return headlineWords.joined(separator: " ")
   }

Có hai sự khác biệt là dùng map thay cho for và joined, tức là kết hợp các phần tử riêng lại. Run lại thì thấy ok, test pass hết.

5. Tổng kết

Qua hai phần này, mặc dù các ví dụ đơn giản nhưng ít nhiều mình cũng đã biết thêm về TDD workflowworkflow như thế nào. Hi vọng những bài tới sẽ tốt thêm.

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

Male avatar

Bùi Khánh Duy

29 bài viết.
17 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
Male avatar
4 3
RxSwift: Bài 1 Observable và Just, Of, From ===== Updated ngày 30/06 Updated một chút: Vì những bất tiện và không rõ ràng về thông tin của kipalo...
Bùi Khánh Duy viết 1 năm trước
4 3
Male avatar
4 0
Autolayout và lifecycle trong IOS ===== Updated ngày 30/06 Updated một chút: Vì những bất tiện và không rõ ràng về thông tin của kipalog, mình mớ...
Bùi Khánh Duy viết 10 tháng trước
4 0
Male avatar
2 3
RxSwift: Bài 2 Subscribing to observables ===== Updated ngày 30/06 Updated một chút: Vì những bất tiện và không rõ ràng về thông tin của kipalog,...
Bùi Khánh Duy viết 1 năm trước
2 3
Bài viết liên quan
White
2 3
Xin chào mọi người. Mình xin chia sẽ một UILabel Helper nhỏ dùng trong truờng hợp cần tính chiều cao của UILabel để xác định "Show More" button có ...
DonDinh viết gần 4 năm trước
2 3
White
12 4
(Link) (Link) (Link) Ở 2 phần tut trước, mình đã hướng dẫn khá chi tiết cách viết một ứng dụng camera có tích hợp chức năng nhận diện khuôn mặ...
HoangPH viết hơn 3 năm trước
12 4
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


Male avatar
{{userFollowed ? 'Following' : 'Follow'}}
29 bài viết.
17 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!