Bài kiểm tra đơn vị và cơ sở dữ liệu: Tại thời điểm nào tôi thực sự kết nối với cơ sở dữ liệu?


37

Có câu trả lời cho câu hỏi về cách các lớp kiểm tra kết nối với cơ sở dữ liệu, ví dụ: "Các lớp kiểm thử dịch vụ có nên kết nối ...""Kiểm tra đơn vị - Ứng dụng kết hợp cơ sở dữ liệu" .

Vì vậy, trong ngắn hạn, giả sử bạn có một lớp A cần kết nối với cơ sở dữ liệu. Thay vì để A thực sự kết nối, bạn cung cấp cho A một giao diện mà A có thể sử dụng để kết nối. Để kiểm tra, bạn triển khai giao diện này với một số nội dung - tất nhiên không cần kết nối. Nếu lớp B khởi tạo A, nó phải chuyển kết nối cơ sở dữ liệu "thực" đến A. Nhưng điều đó có nghĩa là B mở kết nối cơ sở dữ liệu. Điều đó có nghĩa là để kiểm tra B bạn tiêm kết nối vào B. Nhưng B được khởi tạo trong lớp C và cứ thế.

Vì vậy, tại thời điểm nào tôi phải nói "ở đây tôi lấy dữ liệu từ cơ sở dữ liệu và tôi sẽ không viết bài kiểm tra đơn vị cho đoạn mã này"?

Nói cách khác: Ở đâu đó trong mã trong một số lớp tôi phải gọi sqlDB.connect()hoặc một cái gì đó tương tự. Làm thế nào để tôi kiểm tra lớp này?

Và nó có giống với mã phải xử lý GUI hay hệ thống tệp không?


Tôi muốn làm bài kiểm tra đơn vị. Bất kỳ loại thử nghiệm khác không liên quan đến câu hỏi của tôi. Tôi biết rằng tôi sẽ chỉ kiểm tra một lớp với nó (tôi rất đồng ý với bạn Kilian). Bây giờ, một số lớp phải kết nối với DB. Nếu tôi muốn kiểm tra lớp này và hỏi "Làm thế nào để tôi làm điều này", nhiều người nói: "Sử dụng tiêm phụ thuộc!" Nhưng điều đó chỉ chuyển vấn đề sang một lớp khác, phải không? Vì vậy, tôi hỏi, làm thế nào để tôi kiểm tra lớp thực sự, thực sự thiết lập kết nối?

Câu hỏi thưởng: Một số câu trả lời ở đây đun sôi để "Sử dụng các đối tượng giả!" Điều đó nghĩa là gì? Tôi chế nhạo các lớp mà bài kiểm tra lớp phụ thuộc vào. Bây giờ tôi có chế giễu bài kiểm tra dưới lớp và thực sự kiểm tra bản giả (gần với ý tưởng sử dụng Phương thức mẫu, xem bên dưới)?


Có phải đó là kết nối cơ sở dữ liệu mà bạn đang thử nghiệm? Việc tạo tạm thời trong cơ sở dữ liệu bộ nhớ (như derby ) có được chấp nhận không?

@MichaelT Tôi vẫn phải thay thế DB tạm thời trong bộ nhớ bằng cơ sở dữ liệu thực. Ở đâu? Khi nào? Làm thế nào là đơn vị thử nghiệm? Hoặc nó có ổn không để đơn vị kiểm tra mã này?
TobiMcNamobi

3
Không có gì để "kiểm tra đơn vị" về cơ sở dữ liệu. Nó được duy trì bởi những người khác và nếu có lỗi trong đó, bạn sẽ phải để họ sửa nó thay vì tự làm. Điều duy nhất khác nhau giữa việc sử dụng thực tế của lớp của bạn và sử dụng trong các bài kiểm tra phải là các tham số của kết nối cơ sở dữ liệu. Không chắc là mã đọc tệp thuộc tính, hoặc cơ chế tiêm Spring hoặc bất cứ thứ gì bạn sử dụng để kết nối ứng dụng của bạn bị hỏng (và nếu có, bạn không thể tự sửa nó, xem ở trên) - vì vậy tôi cho rằng nó có thể chấp nhận được không kiểm tra bit này của chức năng hệ thống ống nước.
Kilian Foth

2
@KilianFoth hoàn toàn liên quan đến môi trường làm việc và vai trò của nhân viên. Nó thực sự không có gì để làm với câu hỏi. Nếu không có ai chịu trách nhiệm về cơ sở dữ liệu thì sao?
Phản ứng

Một số khung mô phỏng cho phép bạn đưa các đối tượng giả vào khá nhiều thứ, thậm chí vào các thành viên riêng và tĩnh. Điều này làm cho việc kiểm tra với những thứ như mock db kết nối dễ dàng hơn nhiều. Mockito + Powermock là những gì hoạt động với tôi những ngày này (chúng là Java, không chắc chắn những gì bạn đang làm việc).
Thất vọngWithFormsDesigner

Câu trả lời:


21

Điểm của một bài kiểm tra đơn vị là kiểm tra một lớp (trên thực tế, nó thường nên kiểm tra một phương thức ).

Điều này có nghĩa là khi bạn kiểm tra lớp A, bạn đưa cơ sở dữ liệu kiểm tra vào đó - một cái gì đó tự viết hoặc cơ sở dữ liệu trong bộ nhớ nhanh như chớp, bất cứ điều gì sẽ hoàn thành công việc.

Tuy nhiên, nếu bạn kiểm tra lớp B, là khách hàng của A, thì thông thường bạn sẽ chế nhạo toàn bộ Ađối tượng bằng một thứ khác, có lẽ là thứ gì đó thực hiện công việc của nó theo cách nguyên thủy, được lập trình sẵn - mà không sử dụng một Ađối tượng thực tế và chắc chắn không sử dụng dữ liệu cơ sở (trừ khi Achuyển toàn bộ kết nối cơ sở dữ liệu trở lại cho người gọi của nó - nhưng điều đó thật kinh khủng tôi không muốn nghĩ về nó). Tương tự như vậy, khi bạn viết một bài kiểm tra đơn vị cho lớp C, là khách hàng của B, bạn sẽ chế giễu một cái gì đó đóng vai trò Bvà quên Ahoàn toàn.

Nếu bạn không làm điều đó, nó không còn là bài kiểm tra đơn vị nữa, mà là bài kiểm tra hệ thống hoặc tích hợp. Chúng cũng rất quan trọng, nhưng một ấm cá hoàn toàn khác. Để bắt đầu, họ thường nỗ lực hơn để thiết lập và chạy, không thể yêu cầu vượt qua họ như một điều kiện tiên quyết để đăng ký, v.v.


11

Thực hiện kiểm tra đơn vị đối với kết nối cơ sở dữ liệu là hoàn toàn bình thường và là một thông lệ. Đơn giản là không thể tạo ra một puristcách tiếp cận trong đó mọi thứ trong hệ thống của bạn đều phụ thuộc vào nhau.

Chìa khóa ở đây là kiểm tra dựa trên cơ sở dữ liệu tạm thời hoặc chỉ kiểm tra và có quy trình khởi động nhẹ nhất có thể để xây dựng cơ sở dữ liệu kiểm tra đó.

Đối với thử nghiệm đơn vị trong CakePHP, có những thứ được gọi là fixtures. Lịch thi đấu là các bảng cơ sở dữ liệu tạm thời được tạo ra khi đang thử nghiệm đơn vị. Các vật cố có phương pháp thuận tiện để tạo ra chúng. Họ có thể tạo lại một lược đồ từ cơ sở dữ liệu sản xuất bên trong cơ sở dữ liệu thử nghiệm hoặc bạn có thể xác định lược đồ bằng cách sử dụng một ký hiệu đơn giản.

Chìa khóa để thành công với điều này là không triển khai cơ sở dữ liệu kinh doanh, mà chỉ tập trung vào khía cạnh của mã bạn đang kiểm tra. Nếu bạn có một bài kiểm tra đơn vị xác minh rằng mô hình dữ liệu chỉ đọc các tài liệu được xuất bản, thì lược đồ bảng cho bài kiểm tra đó chỉ nên có các trường được yêu cầu bởi mã đó. Bạn không phải triển khai lại toàn bộ cơ sở dữ liệu quản lý nội dung chỉ để kiểm tra mã đó.

Một số tài liệu tham khảo bổ sung.

http://en.wikipedia.org/wiki/Test_fixture

http://phastait.de/manual/3.7/en/database.html

http://book.cakephp.org/2.0/en/development/testing.html#fixenses


28
Tôi không đồng ý. Một thử nghiệm yêu cầu kết nối cơ sở dữ liệu không phải là thử nghiệm đơn vị, bởi vì thử nghiệm theo bản chất của nó sẽ có tác dụng phụ. Điều đó không có nghĩa là bạn không thể viết một bài kiểm tra tự động, nhưng một bài kiểm tra như vậy theo định nghĩa là một bài kiểm tra tích hợp, thực hiện các lĩnh vực trong hệ thống của bạn ngoài cơ sở mã của bạn.
KeithS

5
Gọi tôi là người theo chủ nghĩa thuần túy, nhưng tôi giữ nguyên lý rằng một bài kiểm tra đơn vị không nên thực hiện bất kỳ hành động nào rời khỏi "hộp cát" của môi trường thời gian chạy thử nghiệm. Họ không nên chạm vào cơ sở dữ liệu, hệ thống tệp, ổ cắm mạng, v.v ... Điều này là vì nhiều lý do, không phải ít nhất là sự phụ thuộc của thử nghiệm vào trạng thái bên ngoài. Khác là hiệu suất; bộ kiểm thử đơn vị của bạn sẽ chạy nhanh và giao tiếp với các kho dữ liệu ngoài này lưu trữ các bài kiểm tra chậm theo các đơn đặt hàng cường độ. Trong sự phát triển của riêng tôi, tôi sử dụng các mô phỏng một phần để kiểm tra những thứ như kho lưu trữ của tôi và tôi cảm thấy thoải mái khi xác định "cạnh" cho hộp cát của mình.
KeithS

2
@gbjbaanb - Thoạt nghe có vẻ ổn, nhưng theo kinh nghiệm của tôi thì nó rất nguy hiểm. Ngay cả trong các bộ và khung kiểm tra được kiến ​​trúc tốt nhất, mã để phục hồi giao dịch này có thể không được thực thi. Nếu người chạy thử gặp sự cố hoặc bị hủy bỏ trong bài kiểm tra hoặc bài kiểm tra ném ra SOE hoặc OOME, trường hợp tốt nhất là bạn có kết nối treo và giao dịch trong DB sẽ khóa các bảng bạn chạm cho đến khi kết nối bị tắt. Các cách bạn ngăn chặn điều này gây ra sự cố, chẳng hạn như sử dụng SQLite làm DB thử nghiệm, có những nhược điểm riêng, ví dụ thực tế là bạn không thực sự sử dụng DB thực .
KeithS

5
@KeithS Tôi nghĩ chúng ta đang tranh luận về ngữ nghĩa. Đây không phải là định nghĩa của bài kiểm tra đơn vị hoặc bài kiểm tra tích hợp là gì. Tôi sử dụng đồ đạc để kiểm tra mã phụ thuộc vào kết nối cơ sở dữ liệu. Nếu đó là một bài kiểm tra tích hợp, thì tôi ổn với điều đó. Tôi cần biết rằng bài kiểm tra đã qua. Tôi không thể quan tâm đến sự phụ thuộc, hiệu suất hoặc rủi ro. Tôi sẽ không biết nếu mã đó hoạt động trừ khi thử nghiệm đó vượt qua. Đối với phần lớn các bài kiểm tra không có sự phụ thuộc, nhưng đối với những bài kiểm tra có, thì những phần phụ thuộc đó không thể được tách rời. Thật dễ dàng để nói rằng họ nên như vậy, nhưng họ chỉ đơn giản là không thể.
Phản ứng

4
Tôi nghĩ rằng chúng tôi là tốt. Tôi cũng sử dụng "khung kiểm tra đơn vị" (NUnit) cho các thử nghiệm tích hợp của mình, nhưng tôi đảm bảo tách riêng hai loại thử nghiệm này (thường trong các thư viện riêng biệt). Điểm tôi đang cố gắng thực hiện là bộ kiểm tra đơn vị của bạn, bộ kiểm tra đơn vị bạn chạy nhiều lần một ngày trước mỗi lần đăng ký khi bạn thực hiện theo phương pháp lặp lại màu đỏ-xanh-lục-lặp, nên hoàn toàn tách biệt, để bạn có thể chạy những bài kiểm tra này nhiều lần trong ngày mà không giẫm lên ngón chân của đồng nghiệp.
KeithS

4

Có, ở đâu đó trong cơ sở mã của bạn, một dòng mã thực hiện hành động thực tế là kết nối với DB từ xa. Dòng mã này, 9 lần trong 10, gọi một phương thức "tích hợp" được cung cấp bởi các thư viện thời gian chạy dành riêng cho ngôn ngữ và môi trường của bạn. Như vậy, đó không phải là mã "của bạn" và vì vậy bạn không cần phải kiểm tra nó; cho các mục đích của kiểm tra đơn vị, bạn có thể tin tưởng rằng cuộc gọi phương thức này sẽ thực hiện chính xác. Những gì bạn có thể, và nên, vẫn kiểm tra trong bộ kiểm thử đơn vị của bạn là những thứ như đảm bảo các tham số sẽ được sử dụng cho cuộc gọi này là những gì bạn mong đợi, như đảm bảo chuỗi kết nối là chính xác, hoặc câu lệnh SQL hoặc tên thủ tục lưu trữ.

Đây là một trong những mục đích đằng sau hạn chế rằng các bài kiểm tra đơn vị không nên rời khỏi "hộp cát" thời gian chạy của chúng và phụ thuộc vào trạng thái bên ngoài. Nó thực sự khá thực tế; Mục đích của kiểm tra đơn vị là để xác minh rằng mã bạn đã viết (hoặc sắp viết, trong TDD) hành xử theo cách bạn nghĩ. Mã mà bạn không viết, chẳng hạn như thư viện bạn đang sử dụng để thực hiện các hoạt động cơ sở dữ liệu của mình, không nên là một phần trong phạm vi của bất kỳ bài kiểm tra đơn vị nào, vì lý do rất đơn giản là bạn không viết nó.

Trong bộ kiểm tra tích hợp của bạn , những hạn chế này được nới lỏng. Bây giờ bạn có thểkiểm tra thiết kế chạm vào cơ sở dữ liệu, để đảm bảo rằng mã bạn đã viết chơi độc đáo với mã bạn đã không làm. Tuy nhiên, hai bộ kiểm thử này nên được tách riêng, vì bộ kiểm thử đơn vị của bạn hiệu quả hơn khi chạy nhanh hơn (vì vậy bạn có thể nhanh chóng xác minh rằng tất cả các xác nhận được đưa ra bởi các nhà phát triển về mã của họ vẫn giữ) và gần như theo định nghĩa, kiểm tra tích hợp chậm hơn bởi các đơn đặt hàng cường độ vì sự phụ thuộc vào nguồn lực bên ngoài. Hãy để bộ xử lý xây dựng chạy bộ tích hợp đầy đủ của bạn cứ sau vài giờ, thực hiện các kiểm tra khóa tài nguyên bên ngoài, để các nhà phát triển không giẫm lên các ngón chân của nhau bằng cách chạy các thử nghiệm tương tự cục bộ này. Và nếu xây dựng phá vỡ, vì vậy những gì? Rất nhiều tầm quan trọng được đặt vào việc đảm bảo build-bot không bao giờ thất bại trong quá trình xây dựng hơn có lẽ nên có.


Bây giờ, mức độ nghiêm trọng của bạn có thể tuân thủ điều này phụ thuộc vào chiến lược chính xác của bạn để kết nối và truy vấn cơ sở dữ liệu. Trong nhiều trường hợp bạn phải sử dụng khung truy cập dữ liệu "xương sống", chẳng hạn như các đối tượng SqlConnection và SqlStatement của ADO.NET, toàn bộ phương thức được phát triển bởi bạn có thể bao gồm các lệnh gọi phương thức tích hợp và mã khác phụ thuộc vào việc có kết nối cơ sở dữ liệu, và do đó, điều tốt nhất bạn có thể làm trong tình huống này là chế nhạo toàn bộ chức năng và tin tưởng vào bộ kiểm thử tích hợp của bạn. Nó cũng phụ thuộc vào mức độ bạn sẵn sàng thiết kế các lớp của mình để cho phép các dòng mã cụ thể được thay thế cho mục đích thử nghiệm (chẳng hạn như đề xuất của Tobi về mẫu Phương thức mẫu, đây là một mẫu tốt vì nó cho phép "giả một phần"

Nếu mô hình lưu giữ dữ liệu của bạn dựa vào mã trong lớp dữ liệu của bạn (chẳng hạn như kích hoạt, procs được lưu trữ, v.v.) thì đơn giản là không có cách nào khác để thực thi mã mà bạn đang viết ngoài việc phát triển các thử nghiệm sống bên trong lớp dữ liệu hoặc vượt qua ranh giới giữa thời gian chạy ứng dụng của bạn và DBMS. Một người theo chủ nghĩa thuần túy sẽ nói rằng mô hình này, vì lý do này, sẽ được tránh để ủng hộ một cái gì đó như ORM. Tôi không nghĩ rằng tôi đã đi khá xa; ngay cả trong thời đại của các truy vấn tích hợp ngôn ngữ và các hoạt động kiên trì phụ thuộc vào miền được kiểm tra bởi trình biên dịch, tôi thấy giá trị trong việc khóa cơ sở dữ liệu xuống chỉ các hoạt động được hiển thị thông qua thủ tục được lưu trữ và tất nhiên các quy trình được lưu trữ đó phải được xác minh bằng cách sử dụng tự động xét nghiệm. Nhưng, các bài kiểm tra như vậy không phải là bài kiểm tra đơn vị . Họ đang hội nhập xét nghiệm.

Nếu bạn gặp vấn đề với sự khác biệt này, thì thường dựa trên mức độ quan trọng cao được đặt vào "phạm vi bảo hiểm mã" hoàn chỉnh hay còn gọi là "phạm vi kiểm tra đơn vị". Bạn muốn đảm bảo mọi dòng mã của bạn được bao phủ bởi một bài kiểm tra đơn vị. Một mục tiêu cao cả trên khuôn mặt của nó, nhưng tôi nói hogwash; tâm lý đó cho vay để chống lại các mô hình kéo dài vượt xa trường hợp cụ thể này, chẳng hạn như viết các bài kiểm tra khẳng định thực thi nhưng không thực hiệnma cua ban. Những loại kết thúc này chỉ vì mục đích số lượng bảo hiểm có hại hơn là thư giãn phạm vi bảo hiểm tối thiểu của bạn. Nếu bạn muốn đảm bảo rằng mọi dòng mã cơ sở của bạn được thực thi bằng một số thử nghiệm tự động, thì điều đó thật dễ dàng; khi tính toán số liệu bảo hiểm mã, bao gồm các bài kiểm tra tích hợp. Bạn thậm chí có thể tiến thêm một bước và cô lập các bài kiểm tra "Itino" đang tranh chấp này ("Chỉ tích hợp tên") và giữa bộ kiểm tra đơn vị của bạn và danh mục kiểm tra tích hợp phụ này (vẫn sẽ chạy nhanh một cách hợp lý) gần với phạm vi bảo hiểm đầy đủ.


2

Kiểm tra đơn vị không bao giờ nên kết nối với cơ sở dữ liệu. Theo định nghĩa, họ nên kiểm tra một đơn vị mã mỗi (một phương thức) trong sự cô lập hoàn toàn với phần còn lại của hệ thống của bạn. Nếu họ không, thì họ không phải là một bài kiểm tra đơn vị.

Bỏ qua ngữ nghĩa, có vô số lý do tại sao điều này có lợi:

  • Các thử nghiệm chạy các đơn đặt hàng có cường độ nhanh hơn
  • Vòng phản hồi trở thành tức thì (ví dụ phản hồi <1s cho TDD)
  • Các thử nghiệm có thể được chạy song song cho các hệ thống xây dựng / triển khai
  • Các thử nghiệm không cần cơ sở dữ liệu để chạy (giúp việc xây dựng dễ dàng hơn hoặc ít nhất là nhanh hơn)

Kiểm tra đơn vị là một cách để kiểm tra công việc của bạn. Họ nên phác thảo tất cả các kịch bản cho một phương thức nhất định, thường có nghĩa là tất cả các đường dẫn khác nhau thông qua một phương thức. Đó là đặc điểm kỹ thuật của bạn mà bạn đang xây dựng, tương tự như sổ sách kế toán kép.

Những gì bạn đang mô tả là một loại kiểm tra tự động khác: kiểm tra tích hợp. Trong khi chúng cũng rất quan trọng, lý tưởng là bạn sẽ có ít hơn nhiều. Họ nên xác minh rằng một nhóm các đơn vị tích hợp với nhau đúng cách.

Vậy làm thế nào để bạn kiểm tra mọi thứ với truy cập cơ sở dữ liệu? Tất cả mã truy cập dữ liệu của bạn phải ở trong một lớp cụ thể, vì vậy mã ứng dụng của bạn có thể tương tác với các dịch vụ có thể giả định thay vì cơ sở dữ liệu thực tế. Không nên quan tâm liệu các dịch vụ đó có được hỗ trợ bởi bất kỳ loại cơ sở dữ liệu SQL, dữ liệu kiểm tra trong bộ nhớ hoặc thậm chí dữ liệu dịch vụ web từ xa không. Đó không phải là mối quan tâm của họ.

Lý tưởng nhất (và điều này là rất chủ quan), bạn muốn phần lớn mã của bạn được bao phủ bởi các bài kiểm tra đơn vị. Điều này giúp bạn tự tin rằng mỗi phần hoạt động độc lập. Một khi các mảnh được xây dựng, bạn cần phải đặt chúng lại với nhau. Ví dụ - khi tôi băm mật khẩu của người dùng, tôi sẽ nhận được đầu ra chính xác này.

Giả sử mỗi thành phần được tạo thành từ khoảng 5 lớp - bạn muốn kiểm tra tất cả các điểm thất bại trong chúng. Điều này tương đương với rất ít thử nghiệm chỉ để đảm bảo mọi thứ đều được kết nối đúng cách. Ví dụ - kiểm tra bạn có thể tìm thấy người dùng từ cơ sở dữ liệu được cung cấp tên người dùng / mật khẩu.

Cuối cùng, bạn muốn một số thử nghiệm chấp nhận để thực sự đảm bảo bạn đang đáp ứng các mục tiêu kinh doanh. Thậm chí còn ít hơn trong số này; họ có thể đảm bảo ứng dụng đang chạy và thực hiện những gì nó được xây dựng để làm. Ví dụ - đưa ra dữ liệu thử nghiệm này, tôi sẽ có thể đăng nhập.

Hãy nghĩ về ba loại thử nghiệm này như một kim tự tháp. Bạn cần rất nhiều bài kiểm tra đơn vị để hỗ trợ tất cả mọi thứ, và sau đó bạn làm việc theo cách của mình từ đó.


1

Các mẫu Template Method có thể giúp đỡ.

Bạn gói các cuộc gọi đến cơ sở dữ liệu trong protectedcác phương thức. Để kiểm tra lớp này, bạn thực sự kiểm tra một đối tượng giả kế thừa từ lớp kết nối cơ sở dữ liệu thực và ghi đè các phương thức được bảo vệ.

Bằng cách này, các cuộc gọi thực tế đến cơ sở dữ liệu không bao giờ được kiểm tra theo đơn vị, điều đó đúng. Nhưng đó chỉ là một vài dòng mã. Và đó là chấp nhận được.


1
Trong trường hợp bạn tự hỏi tại sao tôi trả lời câu hỏi của riêng tôi: Có, đây có thể là một câu trả lời nhưng tôi khá không chắc chắn nếu đó là câu hỏi đúng.
TobiMcNamobi

-1

Kiểm tra với dữ liệu ngoài là kiểm tra tích hợp. Kiểm tra đơn vị có nghĩa là bạn chỉ kiểm tra đơn vị. Nó chủ yếu được thực hiện với logic kinh doanh của bạn. Để làm cho đơn vị mã của bạn có thể kiểm tra được, bạn phải tuân theo một số nguyên tắc, giống như bạn phải làm cho đơn vị của mình độc lập với phần khác của mã. Trong quá trình kiểm tra đơn vị nếu bạn cần dữ liệu thì bạn cần phải ép dữ liệu đó bằng cách tiêm phụ thuộc. Có một số khuôn khổ chế giễu và stubbing ra khỏi đó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.