Nên lưu đơn vị ()
Vâng, nó nên. Nhưng hãy cố gắng viết các điều kiện kiểm tra của bạn theo cách độc lập với việc thực hiện. Ví dụ: biến ví dụ sử dụng của bạn thành một bài kiểm tra đơn vị:
function testSavePeople() {
myDataStore = new Store('some connection string', 'password');
myPeople = ['Joe', 'Maggie', 'John'];
savePeople(myDataStore, myPeople);
assert(myDataStore.containsPerson('Joe'));
assert(myDataStore.containsPerson('Maggie'));
assert(myDataStore.containsPerson('John'));
}
Bài kiểm tra này thực hiện nhiều việc:
- nó xác minh hợp đồng của chức năng
savePeople()
- nó không quan tâm đến việc thực hiện
savePeople()
- nó ghi lại cách sử dụng ví dụ của
savePeople()
Hãy lưu ý rằng bạn vẫn có thể giả định / sơ khai / giả mạo lưu trữ dữ liệu. Trong trường hợp này tôi sẽ không kiểm tra các cuộc gọi chức năng rõ ràng, nhưng cho kết quả của hoạt động. Bằng cách này, bài kiểm tra của tôi được chuẩn bị cho những thay đổi / nhà tái cấu trúc trong tương lai.
Ví dụ: việc triển khai cửa hàng dữ liệu của bạn có thể cung cấp một saveBulkPerson()
phương thức trong tương lai - bây giờ thay đổi đối với việc triển khai savePeople()
sử dụng saveBulkPerson()
sẽ không phá vỡ thử nghiệm đơn vị miễn là saveBulkPerson()
hoạt động như mong đợi. Và nếu saveBulkPerson()
bằng cách nào đó không hoạt động như mong đợi, bài kiểm tra đơn vị của bạn sẽ nắm bắt được điều đó.
hoặc các bài kiểm tra như vậy có đủ để kiểm tra cấu trúc ngôn ngữ forEach tích hợp không?
Như đã nói, hãy thử kiểm tra các kết quả mong đợi và giao diện chức năng, không phải để triển khai (trừ khi bạn đang thực hiện kiểm tra tích hợp - sau đó có thể sử dụng các lệnh gọi chức năng cụ thể). Nếu có nhiều cách để thực hiện một chức năng, tất cả chúng sẽ hoạt động với bài kiểm tra đơn vị của bạn.
Về cập nhật câu hỏi của bạn:
Kiểm tra thay đổi trạng thái! Ví dụ, một số bột sẽ được sử dụng. Theo triển khai của bạn, khẳng định rằng lượng sử dụng dough
phù hợp pan
hoặc khẳng định rằng đã dough
sử dụng hết. Khẳng định rằng pan
có chứa cookie sau khi gọi hàm. Khẳng định rằng oven
trống / ở trạng thái như trước.
Đối với các xét nghiệm bổ sung, xác minh trường hợp cạnh: Điều gì sẽ xảy ra nếu oven
không có sản phẩm nào trước khi cuộc gọi? Điều gì xảy ra nếu không đủ dough
? Nếu pan
đã đầy?
Bạn sẽ có thể suy ra tất cả các dữ liệu cần thiết cho các thử nghiệm này từ chính các đối tượng bột, chảo và lò nướng. Không cần phải nắm bắt các cuộc gọi chức năng. Đối xử với các chức năng như thể thực hiện nó sẽ không có sẵn cho bạn!
Trên thực tế, hầu hết người dùng TDD viết bài kiểm tra của họ trước khi họ viết hàm để họ không phụ thuộc vào việc triển khai thực tế.
Đối với bổ sung mới nhất của bạn:
Khi người dùng tạo tài khoản mới, một số điều cần phải xảy ra: 1) hồ sơ người dùng mới cần được tạo trong cơ sở dữ liệu 2) một email chào mừng cần được gửi 3) địa chỉ IP của người dùng cần được ghi lại để gian lận mục đích.
Vì vậy, chúng tôi muốn tạo một phương thức liên kết tất cả các bước "người dùng mới":
function createNewUser(validatedUserData, emailService, dataStore) {
userId = dataStore.insertUserRecord(validateduserData);
emailService.sendWelcomeEmail(validatedUserData);
dataStore.recordIpAddress(userId, validatedUserData.ip);
}
Đối với một chức năng như thế này, tôi sẽ giả / stub / fake (bất cứ điều gì có vẻ tổng quát hơn) dataStore
và emailService
các tham số. Hàm này không tự thực hiện bất kỳ chuyển đổi trạng thái nào trên bất kỳ tham số nào, nó ủy thác chúng cho các phương thức của một số trong số chúng. Tôi sẽ cố gắng xác minh rằng lệnh gọi hàm đã thực hiện 4 điều:
- nó chèn một người dùng vào kho lưu trữ dữ liệu
- nó đã gửi (hoặc ít nhất được gọi là phương thức tương ứng) một email chào mừng
- nó đã ghi lại IP của người dùng vào kho lưu trữ dữ liệu
- nó ủy thác bất kỳ ngoại lệ / lỗi nào nó gặp phải (nếu có)
3 kiểm tra đầu tiên có thể được thực hiện với giả, sơ khai hoặc giả dataStore
và emailService
(bạn thực sự không muốn gửi email khi kiểm tra). Vì tôi phải tìm kiếm một số ý kiến, đây là những điểm khác biệt:
- Giả là một đối tượng hành xử giống như ban đầu và ở một mức độ nhất định không thể phân biệt. Mã của nó thường có thể được sử dụng lại qua các bài kiểm tra. Ví dụ, đây có thể là một cơ sở dữ liệu trong bộ nhớ đơn giản cho trình bao bọc cơ sở dữ liệu.
- Một sơ khai chỉ thực hiện càng nhiều càng cần thiết để thực hiện các hoạt động cần thiết của thử nghiệm này. Trong hầu hết các trường hợp, sơ khai chỉ dành riêng cho một bài kiểm tra hoặc một nhóm các bài kiểm tra chỉ cần một bộ nhỏ các phương pháp của bản gốc. Trong ví dụ này, nó có thể là một
dataStore
phiên bản phù hợp insertUserRecord()
và recordIpAddress()
.
- Giả là một đối tượng cho phép bạn xác minh cách sử dụng nó (thường xuyên nhất bằng cách cho phép bạn đánh giá các cuộc gọi đến các phương thức của nó). Tôi sẽ cố gắng sử dụng chúng một cách tiết kiệm trong các bài kiểm tra đơn vị vì bằng cách sử dụng chúng, bạn thực sự cố gắng kiểm tra việc thực hiện chức năng và không tuân thủ giao diện của nó, nhưng chúng vẫn có những công dụng của chúng. Nhiều khung mô phỏng tồn tại để giúp bạn tạo ra chỉ giả mà bạn cần.
Lưu ý rằng nếu bất kỳ phương pháp nào trong số các phương thức này gây ra lỗi, chúng tôi muốn lỗi này nổi lên theo mã cuộc gọi, để nó có thể xử lý lỗi khi nó thấy phù hợp. Nếu nó được gọi bởi mã API, nó có thể dịch lỗi thành mã phản hồi HTTP thích hợp. Nếu nó được gọi bởi một giao diện web, nó có thể dịch lỗi thành một thông báo phù hợp sẽ được hiển thị cho người dùng, v.v. Vấn đề là chức năng này không biết cách xử lý các lỗi có thể bị ném.
Các trường hợp ngoại lệ / lỗi dự kiến là các trường hợp kiểm tra hợp lệ: Bạn xác nhận rằng, trong trường hợp sự kiện như vậy xảy ra, hàm sẽ hoạt động theo cách bạn mong đợi. Điều này có thể đạt được bằng cách cho phép đối tượng giả / giả / gốc tương ứng ném khi muốn.
Bản chất của sự nhầm lẫn của tôi là để kiểm tra đơn vị một chức năng như vậy, có vẻ như cần phải lặp lại việc thực hiện chính xác trong chính thử nghiệm (bằng cách chỉ định rằng các phương thức được gọi trên các giả theo một thứ tự nhất định) và điều đó có vẻ sai.
Đôi khi điều này phải được thực hiện (mặc dù bạn chủ yếu quan tâm đến điều này trong các bài kiểm tra tích hợp). Thường xuyên hơn, có nhiều cách khác để xác minh các tác dụng phụ / thay đổi trạng thái dự kiến.
Xác minh các chức năng gọi chính xác làm cho các bài kiểm tra đơn vị khá dễ vỡ: Chỉ những thay đổi nhỏ đối với chức năng ban đầu khiến chúng bị lỗi. Điều này có thể được mong muốn hoặc không, nhưng nó yêu cầu thay đổi (các) thử nghiệm đơn vị tương ứng bất cứ khi nào bạn thay đổi một chức năng (có thể tái cấu trúc, tối ưu hóa, sửa lỗi, ...).
Đáng buồn thay, trong trường hợp đó, bài kiểm tra đơn vị mất một số độ tin cậy: vì nó đã được thay đổi, nó không xác nhận chức năng sau khi thay đổi hoạt động giống như trước đây.
Ví dụ: xem xét ai đó thêm một cuộc gọi đến oven.preheat()
(tối ưu hóa!) Trong ví dụ nướng bánh quy của bạn:
- Nếu bạn chế nhạo đối tượng lò nướng, nó sẽ không mong đợi cuộc gọi đó và thất bại trong thử nghiệm, mặc dù hành vi có thể quan sát được của phương pháp không thay đổi (hy vọng bạn vẫn có một chảo bánh quy).
- Một sơ khai có thể hoặc có thể không thất bại, tùy thuộc vào việc bạn chỉ thêm các phương thức được kiểm tra hay toàn bộ giao diện với một số phương thức giả.
- Giả mạo không nên thất bại, vì nó nên thực hiện phương thức (theo giao diện)
Trong các thử nghiệm đơn vị của tôi, tôi cố gắng chung chung nhất có thể: Nếu việc triển khai thay đổi, nhưng hành vi có thể nhìn thấy (từ góc nhìn của người gọi) vẫn giống nhau, các thử nghiệm của tôi sẽ vượt qua. Lý tưởng nhất, trường hợp duy nhất tôi cần thay đổi một bài kiểm tra đơn vị hiện tại phải là một sửa lỗi (của bài kiểm tra, không phải là chức năng được kiểm tra).