Làm thế nào để bạn kiểm tra mã đơn vị bằng cách sử dụng các cấu trúc đồ thị?


18

Tôi đang viết mã (đệ quy) đang điều hướng một biểu đồ phụ thuộc tìm kiếm các chu kỳ hoặc mâu thuẫn trong các phụ thuộc. Tuy nhiên, tôi không chắc làm thế nào để tiếp cận đơn vị thử nghiệm này. Vấn đề là một trong những mối quan tâm chính của chúng tôi là mã sẽ xử lý trên tất cả các cấu trúc biểu đồ thú vị có thể phát sinh và đảm bảo rằng tất cả các nút sẽ được xử lý thích hợp.

Mặc dù thường thì phạm vi bảo hiểm 100% dòng hoặc chi nhánh là đủ để tự tin rằng một số mã hoạt động, có vẻ như ngay cả với phạm vi bảo hiểm 100% đường dẫn bạn vẫn còn nghi ngờ.

Vì vậy, làm thế nào để đi về việc chọn cấu trúc biểu đồ cho các trường hợp thử nghiệm để có niềm tin rằng mã của họ có thể xử lý tất cả các hoán vị có thể hiểu được mà bạn sẽ tìm thấy trong dữ liệu trong thế giới thực.


PS- Nếu có vấn đề, tất cả các cạnh trong biểu đồ của tôi được gắn nhãn "phải có" xor "không thể có" và không có chu kỳ nhỏ và chỉ có một cạnh giữa hai nút bất kỳ.


PPS- Tuyên bố vấn đề bổ sung này ban đầu được đăng bởi tác giả của câu hỏi trong một bình luận dưới đây:

For all vertices N in forest F, for all vertices M, in F, such that if there are any walks between N and M they all must either use only edges labelled 'conflict' or 'requires'.


13
Cùng một cách bạn đơn vị kiểm tra bất kỳ phương pháp khác. Bạn xác định tất cả các trường hợp kiểm tra "thú vị" cho từng phương pháp và viết các bài kiểm tra đơn vị cho chúng. Trong trường hợp của bạn, bạn sẽ phải tạo các biểu đồ phụ thuộc đóng hộp cho từng cấu trúc biểu đồ "thú vị".
Dunk

@Dunk Chúng tôi tiếp tục nghĩ rằng chúng tôi có tất cả những cái khó được bảo hiểm và sau đó chúng tôi nhận ra rằng một cấu trúc nhất định gây ra các vấn đề mà chúng tôi chưa từng xem xét trước đây. Kiểm tra mọi mánh khóe mà chúng ta có thể nghĩ là những gì chúng ta đang làm, những gì tôi hy vọng tìm thấy là một số hướng dẫn / quy trình để tạo ra các ví dụ rắc rối có thể sử dụng tính giảm thiểu của các hình thức cơ bản, v.v.
Sled

6
Đó là vấn đề với bất kỳ hình thức kiểm tra. Tất cả những gì bạn biết là các bài kiểm tra mà bạn nghĩ về công việc. Điều đó không có nghĩa là sw của bạn không có lỗi chỉ vì các bài kiểm tra của bạn vượt qua. Mọi dự án đều có cùng một vấn đề. Tôi đang trong giai đoạn cuối cùng để phân phối dự án hiện tại của mình để chúng tôi có thể bắt đầu sản xuất. Các loại lỗi chúng ta gặp phải bây giờ có xu hướng khá mơ hồ. Chẳng hạn như, nơi phần cứng vẫn hoạt động theo thông số kỹ thuật nhưng chỉ vừa đủ và khi kết hợp với phần cứng khác đồng thời với cùng một vấn đề thì vấn đề xảy ra; nhưng chỉ đôi khi :( Sw được thử nghiệm tốt nhưng chúng tôi đã không nghĩ về mọi thứ
Dunk

Những gì bạn mô tả nghe có vẻ giống như thử nghiệm tích hợp và không giống như thử nghiệm đơn vị. Các thử nghiệm đơn vị sẽ đảm bảo rằng một phương thức có thể tìm thấy các vòng tròn trong biểu đồ. Các bài kiểm tra đơn vị khác sẽ đảm bảo rằng một vòng tròn cụ thể của đồ thị cụ thể được xử lý bởi lớp đang kiểm tra.
SpaceTrucker

Phát hiện chu kỳ là một chủ đề được đề cập đầy đủ (xem Knuth, và một số câu trả lời bên dưới) và các giải pháp không liên quan đến một số lượng lớn các trường hợp đặc biệt, vì vậy trước tiên bạn nên xác định điều gì làm cho vấn đề của bạn như thế này. Có phải do những mâu thuẫn mà bạn đề cập? Nếu vậy, chúng tôi cần thêm thông tin về họ. Nếu đó là kết quả của các lựa chọn thực hiện, bạn có thể phải cấu trúc lại, có thể theo một cách lớn. Về cơ bản, đây là một vấn đề thiết kế mà bạn sẽ phải nghĩ theo cách của mình, TDD là phương pháp sai lầm có thể đưa bạn vào sâu trong mê cung trước khi bạn đi vào ngõ cụt.
sdenham

Câu trả lời:


5

Chúng tôi tiếp tục nghĩ rằng chúng tôi có tất cả những cái khó khăn được bảo hiểm và sau đó chúng tôi nhận ra rằng một cấu trúc nhất định gây ra các vấn đề mà chúng tôi đã không xem xét trước đây. Kiểm tra mọi mánh khóe mà chúng ta có thể nghĩ là những gì chúng ta đang làm.

Nghe có vẻ như một khởi đầu tốt. Tôi đoán bạn đã thử áp dụng một số kỹ thuật cổ điển như phân tích giá trị biên hoặc phân vùng tương đương , như bạn đã đề cập đến thử nghiệm dựa trên phạm vi bảo hiểm. Sau khi bạn đầu tư nhiều thời gian vào việc xây dựng các trường hợp thử nghiệm tốt, bạn sẽ đến một điểm mà bạn, nhóm của bạn và cả những người thử nghiệm của bạn (nếu bạn có) sẽ hết ý tưởng. Và đó là điểm mà bạn nên rời khỏi con đường thử nghiệm đơn vị và bắt đầu thử nghiệm với càng nhiều dữ liệu trong thế giới thực càng tốt.

Rõ ràng là bạn nên cố gắng chọn một sự đa dạng lớn của đồ thị từ dữ liệu sản xuất của bạn. Có lẽ bạn phải viết một số công cụ hoặc chương trình bổ sung chỉ cho phần đó của quy trình. Phần khó ở đây có lẽ là để xác minh tính chính xác của đầu ra chương trình của bạn, khi bạn đặt mười nghìn biểu đồ thế giới thực khác nhau vào chương trình của mình, làm thế nào để biết chương trình của bạn luôn tạo ra đầu ra chính xác? Rõ ràng bạn không thể kiểm tra hơn bằng tay. Vì vậy, nếu bạn may mắn, bạn có thể thực hiện lần kiểm tra phụ thuộc thứ hai rất đơn giản, có thể không đáp ứng mong đợi về hiệu suất của bạn, nhưng dễ xác minh hơn thuật toán ban đầu của bạn. Bạn cũng nên cố gắng tích hợp nhiều kiểm tra tính hợp lý trực tiếp vào chương trình của mình (ví dụ:

Cuối cùng, học cách chấp nhận rằng mọi thử nghiệm chỉ có thể chứng minh sự tồn tại của lỗi chứ không thể thiếu lỗi.


5

1. Tạo thử nghiệm ngẫu nhiên

Viết một thuật toán tạo ra các biểu đồ, để nó tạo ra một vài trăm (hoặc nhiều hơn) các biểu đồ ngẫu nhiên và ném từng biểu đồ vào thuật toán của bạn.

Giữ hạt giống ngẫu nhiên của các biểu đồ gây ra thất bại thú vị và thêm chúng dưới dạng thử nghiệm đơn vị.

2. Phần khó mã

Một số cấu trúc biểu đồ mà bạn biết là khó khăn, bạn có thể viết mã ngay lập tức hoặc viết một số mã kết hợp chúng và đẩy chúng vào thuật toán của bạn.

3. Tạo danh sách đầy đủ

Nhưng, nếu bạn muốn chắc chắn "mã có thể xử lý tất cả các hoán vị có thể hiểu được bạn sẽ tìm thấy trong dữ liệu trong thế giới thực.", Bạn cần tạo dữ liệu này không phải từ hạt giống ngẫu nhiên, mà bằng cách đi bộ qua tất cả các hoán vị. (Điều này được thực hiện khi kiểm tra hệ thống tín hiệu đường sắt tàu điện ngầm và cung cấp cho bạn số lượng lớn các trường hợp phải mất nhiều thời gian để kiểm tra. Đối với tàu điện ngầm, hệ thống bị giới hạn, do đó, có giới hạn cao hơn về số lượng hoán vị. áp dụng)


Người hỏi đã viết rằng họ không thể biết liệu họ đã xem xét tất cả các trường hợp hay chưa, điều này ngụ ý rằng không có cách nào để liệt kê chúng. Cho đến khi họ hiểu rõ vấn đề miền đủ để làm điều đó, làm thế nào để kiểm tra là một câu hỏi tranh luận.
sdenham

@sdenham Làm thế nào bạn sẽ liệt kê một cái gì đó một cách ngẫu nhiên có vô số kết hợp hợp lệ có thể? Tôi đã hy vọng tìm thấy một cái gì đó dọc theo dòng chữ "đây là những cấu trúc đồ thị khó nhất sẽ thường gặp lỗi trong quá trình thực hiện của bạn". Tôi hiểu tên miền đủ tốt vì nó đơn giản: For all vertices N in forest F, for all vertices M, in F, such that if there are any walks between N and M they all must either use only edges labelled 'conflict' or 'requires'.Tên miền không phải là vấn đề.
Sled

@ArtB: Cảm ơn bạn đã làm rõ vấn đề. Như bạn đã nói, không có nhiều hơn một cạnh giữa hai đỉnh bất kỳ và dường như loại trừ các đường đi có chu kỳ (hoặc ít nhất là nhiều hơn một lần đi qua bất kỳ chu kỳ nào), thì ít nhất chúng ta biết rằng không có nghĩa đen là vô số kết hợp hợp lệ có thể, đó là sự tiến bộ. Lưu ý rằng việc biết cách liệt kê tất cả các khả năng không giống như việc bạn phải làm điều đó, vì nó có thể là điểm khởi đầu để đưa ra một lập luận cho tính chính xác, từ đó có thể hướng dẫn kiểm tra. Tôi sẽ suy nghĩ nhiều hơn ...
sdenham

@ArtB: Bạn nên sửa đổi câu hỏi để bao gồm bản cập nhật cho báo cáo vấn đề bạn đã đưa ra ở đây. Ngoài ra, nó có thể giúp tuyên bố rằng đây là các cạnh được định hướng (nếu đó là trường hợp) và liệu một chu kỳ có được coi là một lỗi trong biểu đồ hay không, thay vì chỉ là một tình huống mà thuật toán cần xử lý.
sdenham

4

Không có thử nghiệm nào sẽ có thể đủ trong trường hợp này, thậm chí không có hàng tấn dữ liệu trong thế giới thực hoặc mờ. Phạm vi bảo hiểm mã 100% hoặc thậm chí bảo hiểm đường dẫn 100% là không đủ để kiểm tra các hàm đệ quy.

Hàm đệ quy đứng trước một bằng chứng chính thức (không nên khó khăn trong trường hợp này), hoặc không. Nếu mã quá đan xen với mã cụ thể của ứng dụng để loại trừ tác dụng phụ, thì đó là nơi bắt đầu.

Thuật toán nghe có vẻ giống như một thuật toán chống ngập đơn giản, tương tự như tìm kiếm đầu tiên đơn giản, với việc thêm một danh sách đen không được giao với danh sách các nút được truy cập, chạy từ tất cả các nút.

foreach nodes as node
    foreach nodes as tmp
        tmp.status = unmarked

    tovisit = []
    tovisit.push(node)
    node.status = required

    while |tovisit| > 0 do
        next = tovisit.pop()
        foreach next.requires as requirement
            if requirement.status = unmarked
                tovisit.push(requirement)
                requirement.status = required
            else if requirement.status = blacklisted
                return false
        foreach next.collides as collision
            if collision.status = unmarked
                requirement.status = blacklisted
            else if requirement.status = required
                return false
return true

Thuật toán lặp này đáp ứng điều kiện là không yêu cầu phụ thuộc và đưa vào danh sách đen cùng một lúc, đối với các biểu đồ có cấu trúc tùy ý, bắt đầu từ bất kỳ tạo tác tùy ý nào theo đó luôn luôn bắt buộc tạo tác.

Mặc dù có thể nhanh hoặc không nhanh như việc bạn thực hiện, nhưng có thể chứng minh rằng nó chấm dứt cho tất cả các trường hợp (đối với mỗi lần lặp của vòng lặp bên ngoài, mỗi phần tử chỉ có thể được đẩy một lần vào tovisithàng đợi), nó tràn ngập toàn bộ có thể tiếp cận đồ thị (bằng chứng quy nạp) và nó phát hiện tất cả các trường hợp bắt buộc phải tạo một vật phẩm và đưa vào danh sách đen đồng thời, bắt đầu từ mỗi nút.

Nếu bạn có thể chỉ ra rằng việc triển khai của riêng bạn có cùng đặc điểm, bạn có thể chứng minh tính đúng đắn mà không dẫn đến thử nghiệm đơn vị. Chỉ các phương pháp cơ bản để đẩy và bật từ hàng đợi, đếm chiều dài hàng đợi, lặp lại các thuộc tính và tương tự cần phải được kiểm tra và hiển thị không có tác dụng phụ.

EDIT: Điều mà thuật toán này không chứng minh được là đồ thị của bạn không có chu kỳ. Các đồ thị theo chu kỳ được định hướng là một chủ đề được nghiên cứu kỹ lưỡng, do đó, việc tìm kiếm một thuật toán đã sẵn sàng để chứng minh tính chất này cũng rất dễ dàng.

Như bạn có thể thấy, không cần phải phát minh lại bánh xe, tất cả.


3

Bạn đang sử dụng các cụm từ như "tất cả các cấu trúc biểu đồ thú vị" và "được xử lý phù hợp". Trừ khi bạn có cách để kiểm tra mã của mình với tất cả các cấu trúc đó và xác định xem mã có xử lý biểu đồ phù hợp hay không, bạn chỉ có thể sử dụng các công cụ như phân tích phạm vi kiểm tra.

Tôi đề nghị bạn bắt đầu bằng cách tìm và thử nghiệm với một số cấu trúc biểu đồ thú vị và xác định cách xử lý phù hợp sẽ là gì và xem mã đó làm điều đó. Sau đó, bạn có thể bắt đầu xáo trộn các biểu đồ đó thành a) các biểu đồ bị hỏng vi phạm quy tắc hoặc b) các biểu đồ không thú vị có vấn đề; xem nếu mã của bạn đúng cách không xử lý chúng.


Mặc dù đây là một cách tiếp cận tốt để kiểm tra, nhưng nó không giải quyết được vấn đề chính của câu hỏi: làm thế nào để đảm bảo tất cả các trường hợp được bảo hiểm. Điều đó, tôi tin rằng, sẽ yêu cầu phân tích nhiều hơn và có thể tái cấu trúc - xem câu hỏi của tôi ở trên.
sdenham


2

Khi nói đến loại thuật toán khó kiểm tra này, tôi sẽ tìm đến TDD, nơi bạn xây dựng thuật toán dựa trên các bài kiểm tra,

Tóm lại, TDD

  • viết bài kiểm tra
  • thấy nó thất bại
  • sửa đổi mã
  • đảm bảo tất cả các bài kiểm tra đều được thông qua
  • cấu trúc lại

và lặp lại chu trình,

Trong tình huống đặc biệt này,

  1. Thử nghiệm đầu tiên sẽ là, đồ thị nút đơn trong đó thuật toán không nên trả về bất kỳ chu kỳ nào
  2. Thứ hai sẽ là ba biểu đồ nút không có chu kỳ trong đó thuật toán không nên trả về bất kỳ chu kỳ nào
  3. Tiếp theo sẽ là sử dụng biểu đồ ba nút với một chu kỳ trong đó thuật toán không nên trả về bất kỳ chu kỳ nào
  4. Bây giờ bạn có thể kiểm tra nó theo chu kỳ phức tạp hơn một chút tùy thuộc vào khả năng

Một khía cạnh quan trọng trong phương pháp này là bạn cần luôn luôn thêm một thử nghiệm cho bước có thể (trong đó bạn chia các kịch bản có thể thành các thử nghiệm đơn giản) và khi bạn bao quát tất cả các kịch bản có thể, thuật toán thường được phát triển tự động.

Cuối cùng, bạn cần thêm một hoặc nhiều kiểm tra Tích hợp phức tạp để xem liệu có bất kỳ sự cố không lường trước nào không (chẳng hạn như lỗi tràn ngăn xếp / lỗi hiệu suất khi biểu đồ của bạn rất lớn và khi bạn sử dụng đệ quy)


2

Sự hiểu biết của tôi về vấn đề, như đã nêu ban đầu và sau đó được cập nhật bởi các bình luận dưới câu trả lời của Macke, bao gồm: 1) cả hai loại cạnh (phụ thuộc và xung đột) đều được định hướng; 2) nếu hai nút được kết nối bởi một cạnh, chúng không được kết nối bởi một nút khác, ngay cả khi nó thuộc loại khác hoặc ngược lại; 3) nếu một đường dẫn giữa hai nút có thể được xây dựng bằng cách trộn các cạnh của các loại khác nhau, thì đó là một lỗi, thay vì một tình huống bị bỏ qua; 4) Nếu có một đường dẫn giữa hai nút sử dụng các cạnh của một loại, thì có thể không có đường dẫn khác giữa chúng sử dụng các cạnh của loại khác; 5) các chu kỳ của một loại cạnh đơn hoặc loại cạnh hỗn hợp không được phép (từ một phỏng đoán tại ứng dụng, tôi không chắc chắn rằng các chu trình chỉ xung đột là một lỗi, nhưng nếu không thì có thể loại bỏ điều kiện này.)

Hơn nữa, tôi sẽ giả sử cấu trúc dữ liệu được sử dụng không ngăn chặn vi phạm các yêu cầu này được thể hiện (ví dụ: không thể biểu thị điều kiện vi phạm 2 trong ánh xạ từ cặp nút sang (loại, hướng) nếu luôn luôn ghép cặp nút có nút được đánh số ít nhất trước.) Nếu một số lỗi nhất định không thể được biểu thị, nó sẽ giảm số lượng các trường hợp được xem xét.

Thực tế, có ba biểu đồ có thể được xem xét ở đây: hai biểu đồ chỉ có một cạnh và biểu đồ hỗn hợp được hình thành bởi sự kết hợp của một trong hai loại. Bạn có thể sử dụng điều này để tạo ra một cách có hệ thống tất cả các biểu đồ lên đến một số nút. Trước tiên, tạo tất cả các đồ thị có thể có của các nút N có không quá một cạnh giữa hai cặp nút được đặt hàng (các cặp theo thứ tự vì đây là các đồ thị có hướng.) hình thành liên minh của mỗi cặp.

Nếu cấu trúc dữ liệu của bạn không thể biểu thị vi phạm điều kiện 2, bạn có thể giảm đáng kể các trường hợp được xem xét bằng cách chỉ xây dựng tất cả các biểu đồ xung đột có thể phù hợp trong không gian của biểu đồ phụ thuộc hoặc ngược lại. Nếu không, bạn có thể phát hiện vi phạm điều kiện 2 trong khi hình thành liên minh.

Trên đường ngang đầu tiên của đồ thị kết hợp từ nút đầu tiên, bạn có thể xây dựng tập hợp tất cả các đường dẫn đến mọi nút có thể tiếp cận và khi bạn làm như vậy, bạn có thể kiểm tra vi phạm tất cả các điều kiện (để phát hiện chu kỳ, bạn có thể sử dụng thuật toán của Tarjan .)

Bạn chỉ phải xem xét các đường dẫn từ nút đầu tiên, ngay cả khi biểu đồ bị ngắt kết nối, bởi vì các đường dẫn từ bất kỳ nút nào khác sẽ xuất hiện dưới dạng các đường dẫn từ nút đầu tiên trong một số trường hợp khác.

Nếu các đường dẫn hỗn hợp đơn giản có thể bị bỏ qua, thay vì là lỗi (điều kiện 3), thì đủ để xem xét các biểu đồ phụ thuộc và xung đột một cách độc lập và kiểm tra xem nếu một nút có thể truy cập được ở một nút thì nó không phải là nút khác.

Nếu bạn nhớ các đường dẫn được tìm thấy trong việc kiểm tra biểu đồ của các nút N-1, bạn có thể sử dụng chúng làm điểm bắt đầu để tạo và đánh giá biểu đồ của các nút N.

Điều này không tạo ra nhiều cạnh cùng loại giữa các nút, nhưng nó có thể được mở rộng để làm như vậy. Tuy nhiên, điều này sẽ làm tăng đáng kể số lượng các trường hợp, vì vậy sẽ tốt hơn nếu mã được kiểm tra khiến điều này không thể đại diện hoặc không thể thực hiện được, đã lọc ra tất cả các trường hợp như vậy trước đó.

Chìa khóa để viết một lời sấm truyền như thế này là giữ cho nó đơn giản nhất có thể, ngay cả khi điều đó có nghĩa là không hiệu quả, để bạn có thể thiết lập niềm tin vào nó (lý tưởng thông qua các lập luận cho tính chính xác của nó, được hỗ trợ bằng thử nghiệm.)

Khi bạn có phương tiện để tạo các trường hợp thử nghiệm và bạn tin tưởng vào lời tiên tri mà bạn đã tạo để phân tách chính xác hàng hóa với cái xấu, bạn có thể sử dụng phương pháp này để lái thử mã tự động của mã đích. Nếu điều đó là không khả thi, lựa chọn tốt nhất tiếp theo của bạn là tìm hiểu các kết quả cho các trường hợp đặc biệt. Nhà tiên tri có thể phân loại các lỗi mà nó tìm thấy và cung cấp cho bạn một số thông tin về các trường hợp được chấp nhận, chẳng hạn như số lượng và độ dài đường dẫn của từng loại và liệu có bất kỳ nút nào ở đầu của cả hai loại đường dẫn này không có thể giúp bạn tìm kiếm những trường hợp bạn chưa từng thấy trước đây.

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.