Bạn là một nhà phát triển ứng dụng của Apple, bạn muốn ứng dụng mình phát triển được bày ở trên cửa hàng ứng dụng (App Store) của Apple, hay bạn muốn phát triển một ứng dụng nội bộ chỉ dành riêng cho nhân viên trong công ty, tổ chức của bạn. Để thực hiện được việc này bạn cần phải có một tài khoản nhà phát triển của Apple cung cấp. Tuy nhiên mỗi một loại tài khoản lại có một mục đích khác nhau. Nếu bạn đang băn khoăn về việc chọn tài khoản nào phù hợp với trường hợp của mình thì ở bài viết này mình sẽ giải đáp thắc mắc đó.
Có những loại tài khoản nào?
Apple cung cấp hai loại tài khoản: Apple Developer Program và Apple Developer Enterprise Program. Vậy chúng ta nên chọn tài khoản nào cho ứng dụng của mình?
Apple Developer Program
Tài khoản này dành cho các nhà phát triển hoặc công ty muốn cho phép phân phối qua App Store cho bất kỳ ai. Các bản dựng được phát hành trong quá trình phát triển vẫn được cài đặt qua App Center.
Ưu điểm
Giá rẻ nhất $99 một năm
Có thể đẩy ứng dụng lên cửa hàng ứng dụng của Apple cho tất cả mọi người tiếp cận và tải xuống
Bạn có thể thực hiện build/test trên 100 thiết bị khác nhau, điều này khá là tiện lợi cho việc phát triển ứng dụng của bạn
Ứng dụng của bạn có thể tìm kiếm được bằng các công cụ tìm kiếm khác nhau như Google, Bing …
Nhược điểm
Trong quá trình phát triển các thiết bị được phép bị giới hạn ở 100 thiết bị, các thiết bị này cần phải đăng ký trước khi cài đặt các bản buil.
Do nó được công khai cho tất cả mọi người có thể tải xuống và sử dụng, nên nó sẽ không phù hợp với các ứng dụng nội bộ chỉ dành riêng cho nhân viên
Ứng dụng của bạn phải đảm bảo các yêu cầu khắt khe của Apple
In-app purchase bắt buộc phải qua Apple Payment center, Apple sẽ thu 30% phí.
Chọn loại tài khoản này nếu bạn có ý định đưa ứng dụng của mình lên cửa hàng ứng dụng của Apple để tất cả mọi người có thể tiếp cận và tải xuống app của bạn.
Bạn có thể xem thêm thông tin về chương trình tài khoản này ở đây
Apple Developer Enterprise Program
Đây là loại tài khoản cho phép các tổ chức lớn phát triển và triển khai các ứng dụng độc quyền, sử dụng nội bộ cho nhân viên của họ. Chương trình này dành cho các trường hợp sử dụng cụ thể yêu cầu phân phối riêng tư trực tiếp cho nhân viên sử dụng hệ thống nội bộ an toàn hoặc thông qua giải pháp Quản lý thiết bị di động.
Ưu điểm
Không bị giới hạn số lượng thiết bị cài đặt
Các thiết bị không cần đăng ký trước để có thể cài đặt ứng dụng
Ứng dụng của bạn không cần phải tuân theo các quy tắc của Apple và không bị Apple review.
Vì ứng dụng không cần thiết lập trong cửa hàng ứng dụng Apple nên bạn không cần điền mô tả ứng dụng đầy đủ, tạo hình ảnh để quảng cáo ứng dụng hoặc tạo chính sách bảo mật công khai, v.v.
In-App purchases không cần thiết phải qua Apple Payment center vì vậy không phải chịu phí của Apple
Ứng dụng của bạn sẽ không bị các bên khác tìm kiếm được thông qua các công cụ tìm kiếm, vì vậy nó bảo mật hơn
Nhược điểm
Người dùng sẽ phải cài lại ứng dụng hàng năm trước khi hồ sơ bị hết hạn, trừ phi bạn sử dụng giải pháp MDM(Mobile Devices Manager)
Việc cài đặt ứng dụng phức tạp hơn vì người dùng phải cài đặt, tin cậy nhà phát triển thay vì tự động như khi tải trên App Store
Giá cao nhất $299 một năm
Bạn nên chọn loại tài khoản này nếu bạn cần phát triển các ứng dụng nội bộ, độc quyền dành riêng cho nhân viên trong công ty, tổ chức mà không muốn đẩy ứng dụng lên cửa hàng ứng dụng của Apple.
Bạn có thể xem thêm thông tin về chương trình tài khoản này ở đây
Xin chào mọi người, hôm nay mình sẽ chia sẻ với các bạn về hàm ARRAY_AGG (JSON_ARRAYAGG), ARRAY_DISTINCT trong SQL và ví dụ cụ thể để truy vấn dữ liệu.
Chúng ta có 1 bảng dữ liệu như sau:
Đề bài: Tổng hợp các giá trị bao gồm cả giá trị NULL trong cột Cus_rank thành một mảng (group by id). Loại bỏ các giá trị trùng lặp trong mảng đó.
Để giải quyết được bài toán trên chúng ta sẽ phải sử dụng đến hàm ARRAY_AGG, vậy hàm ARRAY_AGG có chức năng gì?
1. Chức năng hàm ARRAY_AGG
Cú pháp:
ARRAY_AGG(expression [ORDER BY [sort_expression {ASC | DESC}], [...])
Chức năng: tổng hợp tập hợp các giá trị bao gồm NULL trong một cột thành một mảng
Chi tiết:
ARRAY_AGG() nhận một biểu thức trả về giá trị thuộc bất kỳ loại nào hợp lệ cho một phần tử mảng.
Mệnh đề ORDER BY là mệnh đề tùy chọn. Nó chỉ định thứ tự các hàng được xử lý trong tập hợp, xác định thứ tự của các phần tử trong mảng kết quả.
Tương tự như các hàm tổng hợp khác như AVG(), COUNT(), MAX(), MIN() và SUM(), ARRAY_AGG() thường được sử dụng với mệnh đề GROUP BY.
Chú ý:
Thứ tự của các phần tử trong mảng là ngẫu nhiên.
Kiểu dữ liệu của các phần tử trong mảng trả về giống với kiểu dữ liệu của các giá trị trong cột.
2. Giải toán
Chúng ta cùng nhau đi giải quyết từng yêu cầu của bài toán trên nhé!
Đối với yêu cầu ‘Tổng hợp các giá trị bao gồm cả giá trị NULL trong cột Cus_rank thành một mảng (group by id)’ chúng ta sử dụng hàm ARRAY_AGG() để thực hiện truy vấn như sau:
Select id, ARRAY_AGG(cus_rank) as cus_rank_1 from Customer group by id;
Tuy nhiên, trong MySQL Workbench lại không hỗ trợ hàm ARRAY_AGG => chúng ta sẽ thay thế bằng hàm JSON_ARRAYAGG như sau:
Select id, JSON_ARRAYAGG(cus_rank) as cus_rank_1 from Customer group by id;
Kết quả trả về như sau:
Đối với case Loại bỏ các giá trị trùng lặp trong mảng, thường chúng ta sẽ sử dụng hàm array_distinct:
Cú pháp:array_distinct(array) - Removes duplicate values from the array.
Tuy nhiên, trong MySQL Workbench JSON_ARRAYAGG() không hỗ trợ DISTINCT. Bạn có thể SELECT DISTINCT trong truy vấn subquery và sau đó tổng hợp, như sau:
SELECT id, JSON_ARRAYAGG(cus_rank) AS cus_rank_1 FROM (SELECT DISTINCT id, cus_rank from Customer) a GROUP BY id;
Kết quả trả về như sau:
Trên đây là một vài chia sẻ của mình, nếu các bạn có cách làm nào hay, đừng ngần ngại comment nhé. Hẹn gặp lại mọi người trong bài viết tiếp theo.
Xin chào mọi người, hôm nay mình sẽ chia sẻ với các bạn về hàm Lag và Lead trong SQL và ví dụ cụ thể để truy vấn dữ liệu.
Chúng ta có 1 bảng dữ liệu như sau:
Đề bài: Lấy ra giá trị cus_rank liền trước (Pre_cus_rank) của cùng 1 cus_id trong bảng Customer. Đối với case null thì gán Pre_cus_rank = cus_rank.
Để giải quyết được bài toán trên chúng ta sẽ phải sử dụng đến hàm LAG, vậy chức năng của hàm LAG là gì?
1. Chức năng hàm LAG
Cú pháp:
LAG (value_expr [, offset ]) [ IGNORE NULLS | RESPECT NULLS ] OVER ( [ PARTITION BY window_partition ] ORDER BY window_ordering )
Chức năng: Hàm LAG trả về các giá trị cho một hàng tại một khoảng lệch nhất định phía trên (trước) hàng hiện tại trong phân vùng.
Chi tiết:
value_expr: Cột mục tiêu hoặc biểu thức mà hàm hoạt động trên đó.
offset: Một tham số tùy chọn chỉ định số lượng hàng trước hàng hiện tại cần trả về giá trị. Phần bù có thể là một số nguyên không đổi hoặc một biểu thức đánh giá thành một số nguyên. Nếu bạn không chỉ định chênh lệch, Amazon Redshift sẽ sử dụng 1 làm giá trị mặc định. Độ lệch bằng 0 cho biết hàng hiện tại.
IGNORE NULLS: Thông số kỹ thuật tùy chọn chỉ ra rằng Amazon Redshift nên bỏ qua các giá trị null khi xác định hàng nào sẽ sử dụng. Giá trị null được bao gồm nếu IGNORE NULLS không được liệt kê.
RESPECT NULLS: Cho biết Amazon Redshift phải bao gồm các giá trị null khi xác định hàng nào sẽ sử dụng. RESPECT NULLS được hỗ trợ theo mặc định nếu bạn không chỉ định IGNORE NULLS.
OVER: Chỉ định phân vùng và thứ tự.
PARTITION BY window_partition: Đối số tùy chọn đặt phạm vi bản ghi cho mỗi nhóm trong mệnh đề OVER.
8 ORDER BY window_ordering: Sắp xếp các hàng trong mỗi phân vùn
2. Giải toán
Chúng ta cùng nhau đi giải quyết từng yêu cầu của bài toán trên nhé!
Đối với yêu cầu ‘Lấy ra giá trị cus_rank liền trước của cùng 1 cus_id trong bảng Customer’ chúng ta sử dụng hàm lag để thực hiện truy vấn như sau:
Select *, lag(Cus_rank,1) over (partition by id order by update_dt) as Pre_cus_rank from Customer
Kết quả trả về như sau:
Đối với case null thì gán Pre_cus_rank = cus_rank của bài toán, ta thực hiện truy vấn như sau:
with a as (Select *, lag(Cus_rank,1) over (partition by id order by update_dt) as Pre_cus_rank from Customer) select *, case when Pre_cus_rank <> Cus_rank then Pre_cus_rank else Cus_rank end as Pre_cus_rank_1 from a;
Kết quả trả về như sau:
Trên đây là trường hợp lấy ra giá trị liền trước của 1 bản ghi (dùng hàm LAG), ngược lại muốn lấy giá trị tiếp sau của bản ghi đó chúng ta sẽ sử dụng hàm LEAD
Cú pháp:LEAD (value_expr [, offset ]) [ IGNORE NULLS | RESPECT NULLS ] OVER ( [ PARTITION BY window_partition ] ORDER BY window_ordering )
Áp dụng:
with a as (Select *, lead(Cus_rank) over (partition by id order by update_dt) as Update_cus_rank from Customer) select *, case when Update_cus_rank <> Cus_rank then Update_cus_rank else Cus_rank end as Update_cus_rank_1 from a;
Kết quả:
Để tìm hiểu thêm về hàm LAG, LEAD, các bạn có thể tham khảo các link sau:
Trên đây là một vài chia sẻ của mình, nếu các bạn có cách nào giải toán hay, đừng ngần ngại commnet nhé. Hẹn gặp lại mọi người trong bài viết tiếp theo.
Trước khi Xcode 11 ra mắt, để unwrap một optional value chúng ta vẫn thường phải dùng Guard/if let, điều này khá bất tiện trong khi viết Unit test. Khi viết test case chúng ta không nên đưa các câu lệnh điều kiện vào trong các func test vì nó sẽ tạo ra một logic mới trong Unit Test nó khiến test case của chúng ta rắc rối và phức tạp.
Để các bạn dễ hình dung hơn, mình có tạo một Struct Person có thuộc tính address là optional (có thể nil) như dưới đây:
struct Person {
let name: String
let address: String?
}
Thông thường chúng ta sẽ thực hiện unwrap như sau:
func test_Address_caseNil() throws {
let personModel: Person = Person(name: "John", address: nil)
// unwrap optional value
guard let address = personModel.address else {
XCTFail("Expected non-nil address")// throw fail
return
}
XCTAssertEqual(address, "Hanoi")
}
Trong trường hợp này địa chỉ đang nil nên test case này sẽ bị lỗi và throw thông báo “Expected non-nil address”
Có một cách thông dụng hơn là không unwrap mà sử dụng trực tiếp giá trị optional để verify test case như sau:
func test_address_caseNil() throws {
let personModel: Person = Person(name: "John", address: nil)
XCTAssertEqual(personModel.address, "Hanoi")
}
Khi chạy test case này ta nhận được thông báo lỗi như sau:
test_address_caseNil(): XCTAssertEqual failed: ("nil") is not equal to ("Optional("Hanoi")")
Sử dụng cách này thì khá là tiện và nhanh, tuy nhiên nó có một nhược điểm là các thông báo lỗi thường không rõ ràng. Ngoài ra nó cũng sẽ không sử dụng được trong các trường hợp đặc biệt.
Sử dụng XCTUnwrap
XCTUnwrap() được giới thiệu trên Xcode 11, nó làm nhiệm vụ kiểm tra giá trị optional có nil hay không? nếu nil nó sẽ throw ra lỗi, không nil thì trả về giá trị. Từ đó ta có thể thoải mái sử dụng giá trị đó để thực hiện việc testing.
func test_address_caseNil() throws {
let personModel: Person = Person(name: "John", address: nil)
// result
let result = try XCTUnwrap(personModel.address)
XCTAssertEqual(personModel.address, "Hanoi")
}
Sử dụng XCTUnwrap giúp source code của chúng ta gọn gàng sạch sẽ hơn rất nhiều so với các cách thông thường khác.
Để sử dụng được XCTUnwrap bạn nhớ thêm throws cho func test để khi kiểm tra dữ liệu bị nil nó sẽ throw lỗi và đánh dấu test case này bị fail. Nếu bạn muốn thông báo rõ ràng hơn hay đơn giản là bạn muốn viết thông báo lỗi dễ hiểu bạn có thể thêm thuộc tính như sau
let result = try XCTUnwrap(personModel.address, "Width is nil, please config data for test case")
Thông báo lỗi ta nhận được sẽ như sau:
test_address_caseNil(): XCTUnwrap failed: expected non-nil value of type "String" - Width is nil, please config data for test case
Lúc này thông báo lỗi đã rõ ràng hơn, từ đó bạn có thể xử lí vấn đề một cách nhanh hơn.
Hi vọng bài viết sẽ giúp cho các bạn có thêm lựa chọn để xử lí các tình huống khi viết Unit Test, từ đó sử dụng nó một cách hiệu quả và phù hợp với các tình huống.
Thông thường khi các bạn mới vào nghề khi viết xong các test case của mình các bạn thường sử dụng Command + U để chạy. Điều này không sai, tuy nhiên nếu bạn đang thực hiện trên một project lớn thì mình nghĩ bạn không nên dùng cách này vì nó có thể khiến bạn mất rất nhiều thời gian để có được kết quả test. Sau đây mình sẽ giới thiệu cho các bạn một số cách để bạn chạy test case một cách nhanh chóng và hiệu quả hơn.
Chạy test case trên Test Navigator
Các bạn có thể tuỳ chọn việc test cả một file hay test từng test case một trên Test Navigator
Chạy test cho cả Target tests: Dùng khi bạn muốn chạy test và lấy báo cáo cho cả target này, khi này Xcode sẽ chạy nhiều test case cùng lúc nên có thể sẽ mất nhiều thời gian
Chạy test cho chỉ một file tests: Dùng khi bạn viết xong toàn bộ test case của một file và muốn chạy test để kiểm tra báo cáo hay khi bạn muốn kiểm tra xem giữa các test case có bị conflict hay không?
Chạy test một case cụ thể: Dùng khi bạn vừa viết xong test case và muốn kiểm tra xem func đã chạy đúng hay chưa, đây là trường hợp bạn nên dùng khi viết UT vì nó chạy nhanh nên tốn ít thời gian mà vẫn đảm bảo mục đích của bạn
Bạn có thể chạy một hoặc nhiều test case mà bạn muốn bằng cách chọn các test case đó -> chuột phải -> Run x Methods
Chạy test case trực tiếp trên file Tests
Khi các bạn viết xong các test case của mình và bấm Command + S, trên func test case của bạn sẽ xuất hiện một button cho bạn chạy test luôn, điều này giúp bạn dễ dàng kiểm tra được test case của mình. Ngoài ra Xcode cũng cung cấp cho chúng ta một nút chạy test cho toàn bộ file ở trên đầu của file. Bạn có thể xem chi tiết ở hình dưới đây
Chạy lại test case vừa test
Để chạy lại test các test case bạn vừa mới test thì bạn dùng tổ hợp phím sau:
⌃ Control + ⌥ Option + ⌘ Command + G
Đây là tổ hợp phím rất hữu dụng và được sử dụng thường xuyên vì nó giúp bạn chạy lại test case một cách nhanh chóng khi các bạn phải sửa lại các test case bị fail do viết sai input/output.
Chạy test tất cả file trong Test Plan
Khi bạn muốn xem báo cáo tất cả cho file Test Plan của bạn, bạn chỉ cần nhấn tổ hợp phím Command + U.
Bật Code Coverage trên Sidebar
Theo mặc định thì code coverage sẽ được bật khi bạn chạy test, tuy nhiên nếu bạn nhỡ tay tắt nó đi mà không biết bật nó ở đâu thì làm theo hướng dẫn sau: Adjust Editor Options -> Code Coverage
Khi này Xcode sẽ cho bạn biết được dòng code đó của bạn được chạy qua bao nhiêu lần, nếu con số là 0 thì có vẻ bạn chưa có test case nào được viết để test đoạn code đó. Từ dữ liệu đó bạn có thể thực hiện viết thêm test case nếu cần.
Hi vọng bài viết giúp ích cho các bạn. Xin cảm ơn!
Thông thường thì các ứng dụng phát triển càng lâu thì tính năng của nó sẽ càng nhiều, điều này cũng làm cho khối lượng source code và Unit Test cũng tăng theo, có một số dự án xây dựng theo mô hình module hoá, khi này sẽ có rất nhiều module trong dự án cần thực hiện test nếu chúng ta chạy tất cả các file của project thì nó sẽ tốn rất nhiều thời gian, làm giảm đi năng suất làm việc của bản thân. Vì vậy để thực hiện các test case một cách nhanh chóng và hiệu quả Xcode cho phép chúng ta tự tạo Test Plan theo đúng kế hoạch mà chúng ta muốn. Chúng ta có thể tạo một test plan để test một màn hình riêng biệt cho đến hàng trăm màn hình hoặc có thể là một module mà chúng ta đang làm hay tất cả test case của ứng dụng.
Tạo mới Test Plan
Để tạo mới Test Plan ta làm theo các bước như sau:
Tạo mới test plan
Đặt tên cho test plan, bạn có thể đặt tên tuỳ ý miễn sao nó đúng với ý nghĩa là được. Sau đó bấm “Create” để Xcode thực hiện.
Sau khi tạo xong, test plan mới tạo sẽ được thêm vào danh sách, bạn có thể kiểm tra bằng cách thao tác như hình phía dưới đây
Để thực hiện cấu hình cho test plan mới này chúng ta bấm vào Edit Test Plan ở trên hình.
Do test plan mới được tạo nên bạn sẽ thấy nó trống, lúc này chưa có một file/class/module/target nào được thêm vào để test nên chúng ta cần thêm nó vào bằng cách bấm vào dấu + trên màn hình dưới đây:
Tương tự nếu bạn muốn loại bỏ một target nào thì ta có thể bấm dấu – để xoá khỏi test plan.
Chọn target mà bạn muốn thêm vào test plan và bấm “Add” để thêm target vào test plan.
Xcode sẽ thêm tất cả các file test chúng ta đã viết vào trong mục Tests, ở đây bạn sẽ nhìn thấy tất cả các file test mà bạn viết, bạn có thể bỏ tích một func hay 1 file để Xcode không chạy phần đó khi run test plan của bạn.
Để khi chạy test chúng ta xem được Code Coverage thì chúng ta cần đổi giá trị Code Coverage bên Configurations sang ON
Lúc này khi chạy Test Plan này chúng ta sẽ xem được kết quả bằng cách Report Navigator -> Local -> Test -> Coverage
Dựa vào báo cáo này bạn sẽ biết được bạn đã viết test case chạy qua được bao nhiêu phần trăm của source code từ đó bạn sẽ có thể đưa ra các action phù hợp.
Lưu ý Code Coverage chỉ là con số chỉ ra rằng source code test case của bạn đã chạy qua bao nhiêu dòng code, chứ nó không phải là chỉ số đảm bảo độ tin cậy source code của bạn. Vì vậy nó chỉ là điều kiện đủ, code coverage càng cao thì điều đó chứng tỏ Test Case của bạn đã chạy qua càng nhiều dòng code. Để đảm bảo chất lượng source code thì các bạn cần có một chút tư duy về testing, biết xác định các cặp input và output để thực hiện test, cần xác định các điều kiện biên, ngoại lệ, trường hợp lỗi, trường hợp thành công …. thì việc viết test case mới có hiệu quả.
Bạn đang tìm một phương pháp để tăng chất lượng source code? Bạn đang gặp vấn đề về việc source code của bạn có quá nhiều bug? Unit tests là một trong những lựa chọn giúp bạn hạn chế vấn đề đó.
Hiện nay rất nhiều dự án yêu cầu viết Unit tests nhằm mục đích đảm bảo chất lượng source code, vì vậy bài viết này mình sẽ chia sẻ với các bạn về Unit Tests trong Swift để các bạn có thể trang bị cho mình được một kĩ năng mới, để có thể sẵn sàng và tự tin chiến các dự án hiện tại hoặc trong tương lai.
Unit Testing là gì?
Unit tests là tự động chạy và kiểm thử một đoạn mã để đảm bảo nó hoạt động đúng như dự định và đúng với tài liệu yêu cầu.
Unit tests trong ngôn ngữ lập trình là việc viết các func test để đảm bảo source code hoạt động đúng như tài liệu yêu cầu. Với một đầu vào cụ thể sẽ cho ra một đầu ra cụ thể như tài liệu yêu cầu. Việc viết Unit tests để kiểm tra source code của bạn giúp bạn tự tin hơn khi release hay tái cấu trúc source code, vì bạn sẽ đảm bảo source code của mình chạy đúng mong đợi khi bạn chạy bộ test case của bạn thành công.
Các quan điểm trái ngược về Unit tests
Hiện nay có rất nhiều quan điểm trái ngược nhau về việc một dự án có cần phải viết Unit Tests hay không? Rất nhiều Developer thì cho rằng việc viết test tốn quá nhiều thời gian nó làm ảnh hưởng tới việc bàn giao công việc đúng thời hạn. Một số Developer thì cho rằng việc viết Unit tests không đem lại quá nhiều lợi ích mà công việc lại lặp đi lặp lại quá nhàm chán. Tuy nhiên theo mình nếu bạn viết Unit tests đúng cách thì sẽ giúp bạn giảm được thời gian phát triển ứng dụng, tuy thời gian phát triển ban đầu có tăng thêm nhưng bạn sẽ giảm được số lượng bug cơ bản có thể xảy ra từ đó giảm thời gian fix bugs và hạn chế các lỗi phát sinh sau khi sửa.
Viết Unit Tests với Xcode
Để giúp các nhà phát triển có thể viết Unit Tests cho các ứng dụng của họ, Apple đã tạo ra XCTest framework. Giờ đây các nhà phát triển có thể sử dụng framework này đê viết Unit tests cũng như chạy các test case để kiểm tra chất lượng source code của họ.
Một số hàm dùng để kiểm tra của XCTest Framework
XCTAssert(): Nó khá thông dụng có thể sử dụng trong hầu hết các trường hợp, VD: XCTAssert(result == 5)
XCTAssertTrue(): test case của bạn sẽ pass nếu biểu thức kiểm tra có kết quả là true. VD: XCTAssertTrue(view.isHidden)
XCTAssertEqual(a, b), XCTAssertNotEqual: Kiểm tra xem giá trị của 2 biểu thức.
XCTAssertFalse(): ngược với XCTAssertTrue
XCTAssertGreaterThan(a, b): Thường dùng khi bạn kiểm tra 2 giá trị số
XCTAssertGreaterThanOrEqual(a, b): tương tự như XCTAssertGreaterThan, nếu 2 giá trị = nhau thì test case vẫn pass
XCTAssertLessThan, XCTAssertLessThanOrEqual: Tương tự như mục 5 và 6
XCTAssertNil(a), XCTAssertNotNil: Dùng khi cần kiểm tra một var/func có nil hay không
XCTAssertNoThrow() Dùng khi cần kiểm tra xem func có throw lỗi hay không
XCTAssertThrowsError() dùng khi cần kiểm tra func có throw error và kiểm tra được error
Để các bạn dễ hình dung hơn mình sẽ đưa ra một ví dụ như sau:
Tài liệu yêu cầu bạn phải viết một hàm để tính chu vi của hình chữ nhật khi biết chiều dài và chiều rộng của nó. Ta biết chu vi của hình chữ nhật chính là tổng tất cả các cạnh của nó, vì vậy ta có thể sẽ viết func như sau:
class Regtangle {
// hàm tính chu vi hình chữ nhật
class func perimeter(width: Int, height: Int) -> Int {
(width + height) * 2
}
}
Để viết bắt đầu viết test case chúng ta sẽ cần tạo file test như sau:
Chuột phải vào thư mục cần tạo file -> chọn new file -> Unit Test Case Class -> Next
Đặt tên cho file test, trong trường hợp này mình đang cần test class Regtangle nên mình đặt tên như hình -> Next
Sau khi tạo file chúng ta XCode sẽ tạo sẵn cho chúng ta một số đoạn code cơ bản như sau:
import XCTest
// thêm target cần test
@testable import UTXcode14
final class RegtangleTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}
func testPerformanceExample() đây là hàm để kiểm tra hiệu suất của đoạn code, nếu bạn không cần kiểm tra thì bỏ nó đi giúp mỗi lần chạy test của bạn sẽ nhanh hơn đáng kể.
Bây giờ chúng ta đã có thể viết test case để kiêm tra class Regtangle. Đối với func tính chu vi như vậy thì ta sẽ cần dựa vào yêu cầu và phân tích tích bài toán một chút.
Chúng ta hiểu rằng chiều cao và chiều rộng của hình chữ nhật phải là số lớn hơn 0, chu vi của hình chữ nhật thì bằng tổng chiều dài bốn cạnh, vậy nên chúng ta sẽ cần viết các test case với trường hợp như sau:
width và height đều lớn hơn 0: Đầu ra là (width + height) * 2
width <= 0, height > 0: đầu ra cần phải là một thông báo lỗi
width > 0, height <= 0: Đầu ra cần phải là một thông báo lỗi
width <= 0, height <= 0: Đầu ra cần phải là một thông báo lỗi
Đối với case số 1 yêu cầu dữ liệu đầu vào cả width và height đều phải là một số > 0 thì ta viết như sau:
// case width > 0 and height > 0
func test_perimeter_case1() {
// input
let width: Int = 3
let height: Int = 2
// expectation
let expectation = 10
// run code
let result = Regtangle.perimeter(width: width, height: height)
// verify
XCTAssertEqual(result, expectation)
}
Chạy test ta thu đươc kết quả như hình, dấu tích xanh thể hiện kết quả với expectation là bằng nhau, có nghĩa là trong trường hợp này hàm Regtangle.perimeter() đã chạy đúng.
Tiếp theo chúng ta sẽ viết tiếp test case số 2, width <=0 và height > 0, đây là trường hợp chiều rộng nhỏ hơn 0 vì vậy nó là một trường hợp lỗi, mình sẽ mong đợi một thông báo lỗi “Width must be greater than zero”. Vậy nên mình viết code như hình dưới
Lúc này Xcode sẽ báo lỗi như hình trên là do chúng ta đang so sánh 2 kiểu dữ liệu khác nhau. Quay lại hàm tính chu vi hình chữ nhật thì chúng ta thấy không có logic kiểm tra width và height điều này khiến cho func này không đảm bảo tính đúng đắn của nó.
Vậy viết func tính chu vi như nào mới đúng? các bạn có thể tham khảo một số cách viết của mình như sau:
Cách 1: Sử dụng Result để trả về kết quả
// equatable để tiện cho việc so sánh khi viết Unit test
struct MyError: Error, Equatable {
let message: String
}
class Regtangle {
// hàm tính chu vi hình chữ nhật
class func perimeter(width: Int, height: Int) -> Result<Int, MyError> {
if width <= 0 && height <= 0 {
return .failure(MyError(message: "Width and height must be greater than zero"))
} else if width <= 0 {
return .failure(MyError(message: "Width must be greater than zero"))
} else if height <= 0 {
return .failure(MyError(message: "Height must be greater than zero"))
} else {
let perimeter = (width + height) * 2
return .success(perimeter)
}
}
}
Cách 2: Sử dụng throw để đẩy ra lỗi
struct MyError: Error, Equatable {
let message: String
}
class Regtangle {
// hàm tính chu vi hình chữ nhật
class func perimeter(width: Int, height: Int) throws -> Int {
if width <= 0 && height <= 0 {
throw MyError(message: "Width and height must be greater than zero")
} else if width <= 0 {
throw MyError(message: "Width must be greater than zero")
} else if height <= 0 {
throw MyError(message: "Height must be greater than zero")
} else {
return (width + height) * 2
}
}
}
Trong bài viết này mình sẽ hướng dẫn các bạn viết test case khi sử dụng throw, mình sẽ viết tổng cộng 7 test cases để thực hiện test func này, trong đó bao gồm 4 test cases để test logic chính và 3 cases để test giá trị biên. Cụ thể mình sẽ thực hiện như sau:
import XCTest
@testable import UTXcode14
final class RegtangleTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
// case width > 0 and height > 0
func test_perimeter_case1() throws {
// precontidtion
let width: Int = 3
let height: Int = 2
// expectation
let expectation = 10
// run code
XCTAssertNoThrow(try Regtangle.perimeter(width: width, height: height))
let result = try Regtangle.perimeter(width: width, height: height)
// verify
XCTAssertEqual(result, expectation)
}
// case width < 0 and height > 0
func test_perimeter_case2() throws {
// precontidtion
let width: Int = -3
let height: Int = 2
// expectation
let expectation = MyError(message: "Width must be greater than zero")
// run code
XCTAssertThrowsError(try Regtangle.perimeter(width: width, height: height)) { error in
XCTAssertEqual(error as? MyError, expectation)
}
}
// case width > 0 and height < 0
func test_perimeter_case3() throws {
// precontidtion
let width: Int = 3
let height: Int = -2
// expectation
let expectation = MyError(message: "Height must be greater than zero")
// run code
XCTAssertThrowsError(try Regtangle.perimeter(width: width, height: height)) { error in
XCTAssertEqual(error as? MyError, expectation)
}
}
// case width < 0 and height < 0
func test_perimeter_case4() throws {
// precontidtion
let width: Int = -3
let height: Int = -2
// expectation
let expectation = MyError(message: "Width and height must be greater than zero")
// run code
XCTAssertThrowsError(try Regtangle.perimeter(width: width, height: height)) { error in
XCTAssertEqual(error as? MyError, expectation)
}
}
// case width = 0 and height > 0, test giá trị biên của width
func test_perimeter_case5() throws {
// precontidtion
let width: Int = 0
let height: Int = 2
// expectation
let expectation = MyError(message: "Width must be greater than zero")
// run code
XCTAssertThrowsError(try Regtangle.perimeter(width: width, height: height)) { error in
XCTAssertEqual(error as? MyError, expectation)
}
}
// case width = 0 and height > 0, test giá trị biên của height
func test_perimeter_case6() throws {
// precontidtion
let width: Int = 2
let height: Int = 0
// expectation
let expectation = MyError(message: "Height must be greater than zero")
// run code
XCTAssertThrowsError(try Regtangle.perimeter(width: width, height: height)) { error in
XCTAssertEqual(error as? MyError, expectation)
}
}
// case width = 0 and height = 0, test giá trị biên của cả width và height
func test_perimeter_case7() throws {
// precontidtion
let width: Int = 0
let height: Int = 0
// expectation
let expectation = MyError(message: "Width and height must be greater than zero")
// run code
XCTAssertThrowsError(try Regtangle.perimeter(width: width, height: height)) { error in
XCTAssertEqual(error as? MyError, expectation)
}
}
}
Chạy test (Command U) chúng ta được kết quả như sau:
Tất cả các test case của chúng ta đều passed, điều này chứng minh func của bạn đã đáp ứng hết tất cả yêu cầu mà bạn đặt ra
Hi vọng bài viết sẽ giúp cho các bạn có thêm kiến thức để nâng cao năng lực của bản thân.