tl; dr
Tại Pivotal, chúng tôi đã viết bài Cedar vì chúng tôi sử dụng và yêu thích Rspec trong các dự án Ruby của chúng tôi. Cedar không có nghĩa là để thay thế hoặc cạnh tranh với OCUnit; điều này có nghĩa là mang khả năng thử nghiệm kiểu BDD cho Objective C, giống như Rspec đã đi tiên phong trong thử nghiệm kiểu BDD trong Ruby, nhưng đã không loại bỏ Test :: Unit. Chọn cái này hay cái kia phần lớn là vấn đề sở thích phong cách.
Trong một số trường hợp, chúng tôi đã thiết kế Cedar để khắc phục một số thiếu sót trong cách OCUnit hoạt động cho chúng tôi. Cụ thể, chúng tôi muốn có thể sử dụng trình gỡ lỗi trong các thử nghiệm, để chạy thử nghiệm từ dòng lệnh và trong các bản dựng CI và nhận được kết quả văn bản hữu ích của kết quả thử nghiệm. Những điều này có thể ít nhiều hữu ích cho bạn.
Câu trả lời dài
Quyết định giữa hai khung thử nghiệm như Cedar và OCUnit (ví dụ) có hai điều: phong cách ưa thích và dễ sử dụng. Tôi sẽ bắt đầu với phong cách, bởi vì đó đơn giản chỉ là vấn đề quan điểm và sở thích; dễ sử dụng có xu hướng là một tập hợp của sự đánh đổi.
Cân nhắc về phong cách vượt qua những gì công nghệ hoặc ngôn ngữ bạn sử dụng. Thử nghiệm đơn vị theo kiểu xUnit đã xuất hiện từ lâu hơn so với thử nghiệm theo kiểu BDD, nhưng sau đó đã nhanh chóng trở nên phổ biến, chủ yếu là do Rspec.
Ưu điểm chính của thử nghiệm kiểu xUnit là tính đơn giản và áp dụng rộng rãi (trong số các nhà phát triển viết thử nghiệm đơn vị); gần như bất kỳ ngôn ngữ nào bạn có thể xem xét viết mã bằng đều có khung kiểu xUnit.
Các khung kiểu BDD có xu hướng có hai điểm khác biệt chính khi so sánh với kiểu xUnit: cách bạn cấu trúc bài kiểm tra (hoặc thông số kỹ thuật) và cú pháp để viết các xác nhận của bạn. Đối với tôi, sự khác biệt về cấu trúc là sự khác biệt chính. Các kiểm tra xUnit là một chiều, với một phương thức setUp cho tất cả các kiểm tra trong một lớp kiểm tra nhất định. Các lớp mà chúng tôi kiểm tra, tuy nhiên, không phải là một chiều; chúng ta thường cần kiểm tra các hành động trong một số bối cảnh khác nhau, có khả năng xung đột. Ví dụ, hãy xem xét một lớp ShoppingCart đơn giản, với phương thức addItem: (cho mục đích của câu trả lời này, tôi sẽ sử dụng cú pháp Objective C). Hành vi của phương pháp này có thể khác nhau khi giỏ hàng trống so với khi giỏ hàng chứa các mặt hàng khác; nó có thể khác nếu người dùng đã nhập mã giảm giá; nó có thể khác nếu mục được chỉ định có thể ' t được vận chuyển theo phương thức vận chuyển đã chọn; v.v ... Vì những điều kiện có thể này giao nhau với nhau, bạn kết thúc với số lượng bối cảnh có thể tăng lên về mặt hình học; trong thử nghiệm kiểu xUnit, điều này thường dẫn đến rất nhiều phương thức với các tên như testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies. Cấu trúc của các khung kiểu BDD cho phép bạn tổ chức các điều kiện này một cách riêng lẻ, điều mà tôi thấy giúp dễ dàng hơn để đảm bảo tôi bao quát mọi trường hợp, cũng như dễ dàng tìm kiếm, thay đổi hoặc thêm các điều kiện riêng lẻ. Ví dụ, sử dụng cú pháp Cedar, phương thức trên sẽ như thế này: trong thử nghiệm kiểu xUnit, điều này thường dẫn đến rất nhiều phương thức với các tên như testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies. Cấu trúc của các khung kiểu BDD cho phép bạn tổ chức các điều kiện này một cách riêng lẻ, điều mà tôi thấy giúp dễ dàng hơn để đảm bảo tôi bao quát mọi trường hợp, cũng như dễ dàng tìm kiếm, thay đổi hoặc thêm các điều kiện riêng lẻ. Ví dụ, sử dụng cú pháp Cedar, phương thức trên sẽ như thế này: trong thử nghiệm kiểu xUnit, điều này thường dẫn đến rất nhiều phương thức với các tên như testAddItemWhenCartIsEmptyAndNoDiscountCodeAndShippingMethodApplies. Cấu trúc của các khung kiểu BDD cho phép bạn tổ chức các điều kiện này một cách riêng lẻ, điều mà tôi thấy giúp dễ dàng hơn để đảm bảo tôi bao quát mọi trường hợp, cũng như dễ dàng tìm kiếm, thay đổi hoặc thêm các điều kiện riêng lẻ. Ví dụ, sử dụng cú pháp Cedar, phương thức trên sẽ như thế này:
describe(@"ShoppingCart", ^{
describe(@"addItem:", ^{
describe(@"when the cart is empty", ^{
describe(@"with no discount code", ^{
describe(@"when the shipping method applies to the item", ^{
it(@"should add the item to the cart", ^{
...
});
it(@"should add the full price of the item to the overall price", ^{
...
});
});
describe(@"when the shipping method does not apply to the item", ^{
...
});
});
describe(@"with a discount code", ^{
...
});
});
describe(@"when the cart contains other items, ^{
...
});
});
});
Trong một số trường hợp, bạn sẽ tìm thấy các bối cảnh trong đó có cùng một bộ các xác nhận mà bạn có thể DRY up bằng cách sử dụng các bối cảnh ví dụ được chia sẻ.
Sự khác biệt chính thứ hai giữa các khung kiểu BDD và khung kiểu xUnit, cú pháp khẳng định (hoặc "so khớp"), chỉ đơn giản làm cho kiểu của thông số kỹ thuật có phần đẹp hơn; một số người thực sự thích nó, những người khác thì không.
Điều đó dẫn đến câu hỏi về việc sử dụng dễ dàng. Trong trường hợp này, mỗi khung có ưu và nhược điểm:
OCUnit đã tồn tại lâu hơn nhiều so với Cedar và được tích hợp trực tiếp vào Xcode. Điều này có nghĩa là thật đơn giản để thực hiện một mục tiêu thử nghiệm mới và, hầu hết thời gian, để kiểm tra và chạy "chỉ hoạt động". Mặt khác, chúng tôi thấy rằng trong một số trường hợp, chẳng hạn như chạy trên thiết bị iOS, việc kiểm tra OCUnit hoạt động là không thể. Thiết lập thông số kỹ thuật của Cedar mất nhiều công sức hơn so với các bài kiểm tra OCUnit, vì bạn đã có được thư viện và tự liên kết với nó (không bao giờ là một nhiệm vụ tầm thường trong Xcode). Chúng tôi đang làm việc để thiết lập dễ dàng hơn và mọi đề xuất đều được chào đón.
OCUnit chạy thử nghiệm như là một phần của bản dựng. Điều này có nghĩa là bạn không cần phải chạy một tệp thực thi để chạy thử nghiệm; nếu bất kỳ thử nghiệm thất bại, xây dựng của bạn thất bại. Điều này làm cho quá trình chạy thử nghiệm đơn giản hơn một bước và đầu ra thử nghiệm đi thẳng vào cửa sổ đầu ra xây dựng của bạn, giúp dễ dàng nhìn thấy. Chúng tôi đã chọn để các thông số kỹ thuật của tuyết được xây dựng thành một tệp thực thi mà bạn chạy riêng vì một vài lý do:
- Chúng tôi muốn có thể sử dụng trình gỡ lỗi. Bạn chạy các thông số kỹ thuật giống như bạn sẽ chạy bất kỳ chương trình thực thi nào khác, vì vậy bạn có thể sử dụng trình gỡ lỗi theo cách tương tự.
- Chúng tôi muốn đăng nhập giao diện điều khiển dễ dàng trong các bài kiểm tra. Bạn có thể sử dụng NSLog () trong các bài kiểm tra OCUnit, nhưng đầu ra đi vào cửa sổ xây dựng nơi bạn phải mở ra bước xây dựng để đọc nó.
- Chúng tôi muốn dễ dàng đọc báo cáo thử nghiệm, cả trên dòng lệnh và trong Xcode. Kết quả OCUnit xuất hiện độc đáo trong cửa sổ xây dựng trong Xcode, nhưng xây dựng từ dòng lệnh (hoặc là một phần của quy trình CI) dẫn đến kết quả đầu ra thử nghiệm xen kẽ với rất nhiều đầu ra của bản dựng khác. Với các giai đoạn xây dựng và chạy riêng biệt, Cedar tách biệt đầu ra để dễ dàng tìm ra đầu ra thử nghiệm. Trình chạy thử mặc định của Cedar sao chép kiểu in tiêu chuẩn "." đối với mỗi thông số kỹ thuật vượt qua, "F" cho thông số kỹ thuật không thành công, v.v., Cedar cũng có khả năng sử dụng các đối tượng phóng viên tùy chỉnh, do đó bạn có thể đưa ra kết quả đầu ra theo bất kỳ cách nào bạn muốn, với một chút nỗ lực.
OCUnit là khung thử nghiệm đơn vị chính thức cho Objective C và được Apple hỗ trợ. Apple về cơ bản có vô số tài nguyên, vì vậy nếu họ muốn một cái gì đó được thực hiện thì nó sẽ được thực hiện. Và, sau tất cả, đây là hộp cát của Apple mà chúng tôi đang chơi. Tuy nhiên, mặt trái của đồng tiền đó là Apple nhận được theo yêu cầu hỗ trợ bajillion và báo cáo lỗi mỗi ngày. Họ rất giỏi trong việc xử lý tất cả, nhưng họ có thể không thể xử lý các vấn đề bạn báo cáo ngay lập tức hoặc cả. Cedar mới hơn nhiều và ít nướng hơn OCUnit, nhưng nếu bạn có thắc mắc hoặc vấn đề hoặc đề xuất nào đó hãy gửi tin nhắn đến danh sách gửi thư của Cedar (cedar-discuss@googlegroups.com) và chúng tôi sẽ làm những gì chúng tôi có thể giúp bạn. Ngoài ra, vui lòng rẽ nhánh mã từ Github (github.com/pivotal/cedar) và thêm bất cứ điều gì bạn nghĩ là thiếu.
Chạy thử nghiệm OCUnit trên thiết bị iOS có thể khó khăn. Thành thật mà nói, tôi đã không thử điều này trong một thời gian khá lâu, vì vậy nó có thể đã trở nên dễ dàng hơn, nhưng lần cuối cùng tôi thử tôi chỉ đơn giản là không thể kiểm tra OCUnit cho bất kỳ chức năng UIKit nào hoạt động. Khi chúng tôi viết bài, chúng tôi đã đảm bảo rằng chúng tôi có thể kiểm tra mã phụ thuộc vào UIKit cả trên trình giả lập và trên thiết bị.
Cuối cùng, chúng tôi đã viết bài kiểm tra đơn vị, có nghĩa là nó không thực sự so sánh với các dự án như UISpec. Đã khá lâu kể từ khi tôi thử sử dụng UISpec, nhưng tôi hiểu rằng nó tập trung chủ yếu vào việc điều khiển UI theo chương trình trên thiết bị iOS. Chúng tôi đặc biệt quyết định không cố gắng để hỗ trợ cho các loại thông số kỹ thuật này, vì Apple (vào thời điểm đó) sắp công bố UIAutomation.