07/09/2018, 17:17

Swift - Test-Driven Development (TDD) - Chapter 2 - Planning /Structuring App

Swift - Test-Driven Development (TDD) - Chapter 2 - Planning and Structuring Your Test-Driven iOS App Ở phần này, ta sẽ vận dụng kiến thức có được từ writing test sang driving development. Ta sẽ thiết kế 1 app đơn giản như sau: The task list view The task detail view The task input view ...

Swift - Test-Driven Development (TDD) - Chapter 2 - Planning and Structuring Your Test-Driven iOS App

Ở phần này, ta sẽ vận dụng kiến thức có được từ writing test sang driving development. Ta sẽ thiết kế 1 app đơn giản như sau:

  • The task list view
  • The task detail view
  • The task input view

Bên cạnh đó cũng sẽ đề cập hướng planning và structure của một app. Mình sẽ xây dựng bài viết kiểu như đưa ra user story rồi mình phân tích như thế nào cho hợp lí.

1. The task list view

a. First user story

User stories:

  • As a user, I want to see the list of to-do items when I open the app
  • As a user, I want to add to-do items to the list

Như vậy, từ user stories, ta phân tích:

  • App gồm list of to-do items (UITableView).
  • Item trong list gồm title, optional location và due date.
  • New item có thể được added vào list bằng add (+) button nằm ở navigation bar. alt text

b. Second user story

User stories:

  • As a user, I want to check a to-do item to mark it as finished
  • As a user, I want to see all the checked items following the unchecked items
  • As a user, I want to uncheck a to-do item
  • As a user, I want to delete all the to-do items

Như vậy, từ user stories, ta phân tích:

  • Tại mỗi item, slide left sẽ có 1 Button để check hoặc uncheck, như sau: alt text
  • Item nào được mark là checked sẽ được đẩy xuống Finished section. alt text
  • Tại Finished section, có thể xoá item nếu cần
  • Tap vào Item, thì detail of Item hiển thị.

2. The task detail view

User stories:

  • As a user, given that I have tapped a to-do item in the list, I want to see its details
  • As a user, I want to check a to-do item from its details view

Như vậy, từ user stories, ta phân tích:

  • Màn hình detail show hết các thông tin của item.
  • Thông tin gồm title, due date, location (name and address) và description.
  • Nếu có address, map sẽ hiển thị address.
  • Ta cũng có thể bấm finish ở màn hình này.

Design như hình dưới đây:
alt text

3. The task input view

User stories:

  • As a user, given that I tapped the add (+) button in the item list, I want to see a form to put in the details (title, optional date, optional location name, optional address, and optional description) of a to-do item
  • As a user, I want to add a to-do item to the list of to-do items by tapping on the Save button

Như vậy, từ user stories, ta phân tích:

  • Bấm vào add (+) button ở list view, task input view hiển thị.
  • User có thể add thông tin nhưng chỉ có title là required.
  • The Save button chỉ available và selected sau khi đã điền title.
  • Nút Cancel button dismiss view.

Design như hình dưới đây
alt text

Lưu ý:

  • Lưu ý là ta sẽ không test UI. Unit test không thể biết được app trông đẹp như thế nào, nó chỉ có thể test features phần mà ít liên quan đến presentation.
  • Về nguyên tắc, thì có thể viết được test vị trí màu sắc của UI nhưng nó kiểu không phải là mục đích chính của cái này.
  • Cái ta cần test là UI có present hay không, hay có input được không.

4. The structure of the app

Cả ba phần trên có thể hiểu là the planning của app. Bây giờ ta sẽ đến phần structure của app. Các bạn nhìn vào hình sau:
alt text

a. The table view controller, the delegate, and the data source

Cái hay của TableView thì ai cũng thấy rồi, tuy nhiên thường có 1 tình huống mà nó đảm nhận quá nhiều việc: presenting the view, navigating đến view controllers khác, quản lý presentation của data (reload, update, v..v)

Đôi khi, ta nên chia nhỏ ra thành nhiều lớp con, đảm nhiệm nhiều nhiệm vụ khác nhau. Vì vậy, ta sẽ sử dụng helper class để giúp phần data source và delegate. Phần giao tiếp giữa helper và table view controller ta dùng protocol.

Protocol sẽ define inferface của một class trông như thế nào. Nó có 1 ưu điểm là nếu ta cần replace 1 implementation với phiên bản tốt hơn, ta chỉ cần dev dựa trên 1 interface rõ ràng rồi, những phần trước đó hay bên trong ta không quan tâm.

b. The table view cells

Item có 1 title, có optional date và location name. Phần table view cells này chỉ hiển thị phần data. Ta chỉ cần code trên custom table view cell của ta.

c. The model

The model của application chứa to-do item, location, item manager (cái mà cho phép thêm/ sửa/ xoá items và chịu trách nhiệm quản lý items). Vì vậy, controller sẽ gửi yêu cầu đến item manager về vấn đề hiển thị item lên cell. Ngoài ra item manager cũng sẽ đảm nhiệm lưu trữ items lên disc.

Đối với những người mới bắt đầu thường có xu hướng quản lý object model bên trong viewController. Sau đó controller có 1 reference đến collection of items, và việc thêm bớt sẽ thực hiện trực tiếp trên đó luôn. Điều này không được recommend bởi vì nếu chúng ta muốn thay đổi việc lưu trữ của items (ví dụ như khi dùng Core Data), việc thêm/ xoá này sẽ phải được thực hiện ở bên trong controller. Thật khó để có 1 cái nhìn tổng quan về một lớp như vậy.

Sẽ dễ dàng hơn nếu có 1 interface rõ ràng nối controller với lớp model. Bởi vì nếu ta cần thay đổi cách model được quản lý, View Controller vẫn được giữ nguyên. Ta thậm chí có thể complete lớp model nếu giữ lớp interface như cũ. Chúng ta sẽ thấy việc tách rời ra giúp code dễ dàng hơn như thế nào.

d. Other view controllers

Mình còn 2 view controllers khác là task detail view controller và 1 view controller cho input task

Khi user tap vào to-do item trong list, màn hình detail sẽ xuất hiện.
New to-do items sẽ được added vào the list thông qua việc sử dụng input view controller.

e. The development strategy

Ta sẽ xây dựng app từ trong ra ngoài. Ta sẽ bắt đầu với Model, rồi đến Controllers rồi networking, xong rồi gắn lại.

Thông thường, mình thích xây dựng app theo kiểu feature-by-feature hơn là TDD. Nhưng nếu chia thành các layer nhỏ ra thay vì features, nó sẽ dễ theo dõi và có cái nhìn tổng thể hơn về mọi việc. Sau này khi cần refresh memory hay những thông tin liên quan cũng dễ cho bạn hơn.

OK, bây giờ ta tạo 1 cái app nhỏ như sau:
alt text

Lưu ý là Xcode đã set up cái test target sao cho phần testing của implementations tương ứng với nhau mà ta sẽ viết trong application.

Phần Edit Scheme..., mình đã trình bày kĩ ở bài trước. Các bạn xem ở đây nhé

5. Setting up useful Xcode behaviors for testing

a. Useful build behaviors

Xcode có 1 feature gọi là "behaviors", với việc sử dụng behaviors và tabs, Xcode có thể cho ta xem những thông tin hữu ích dựa vào trạng thái của nó. Để enable, các bạn vào Xcode -> Behaviors -> Edit Behaviors.

Xong, ta làm các thao tác như sau:
alt text

Khi bắt đầu build, Xcode compiles các files và links chúng với nhau. Bạn cần activate build log lên để thấy nó hoạt động như thế nào. Build và run để xem thử Behaviors như thế nào ^^

b. Testing behaviors

Ta setup như sau cho dễ nhìn:
alt text

Khi các test cases bắt đầu run, ta vẫn muốn thấy code editor nữa. Nên add 1 behavior để show Coding tab. Ngoài ra, ta cũng muốn thấy thêm Test Navigator và debugger với console view.

Khi các test case thành công, thì Xcode sẽ show 1 bảng bezel để thông báo các tests đã thành công. Ta cũng có thể hide cái navigator và debugger để tập trung refactor hoặc viết code.

6. Summary

Trong bài này, ta đã có cái nhìn tổng quát về app mà mình sẽ viết ở những bài sau. Trong các bài viết sắp tới, mình sẽ viết lớp data model của app sử dụng TDD. Mình sẽ sử dụng struct để tạo model bởi vì models được represented tốt nhất trong Swift bởi value types.
Mình cũng sẽ dùng Equatable protocol để so sánh các model dễ dàng hơn

0