Làm thế nào tôi nên đơn vị kiểm tra mã luồng?


704

Vì vậy, tôi đã tránh được cơn ác mộng đang thử nghiệm mã đa luồng vì nó dường như quá giống với một bãi mìn. Tôi muốn hỏi làm thế nào mọi người đã kiểm tra mã dựa trên các luồng để thực thi thành công hay chỉ là cách mọi người tiến hành kiểm tra các loại vấn đề chỉ xuất hiện khi hai luồng tương tác theo một cách nhất định?

Điều này có vẻ như là một vấn đề thực sự quan trọng đối với các lập trình viên ngày nay, nó sẽ hữu ích để tập hợp kiến ​​thức của chúng tôi về điều này.


2
Tôi đã nghĩ đến việc đăng một câu hỏi về vấn đề chính xác này. Trong khi Will làm cho nhiều điểm tốt dưới đây, tôi nghĩ chúng ta có thể làm tốt hơn. Tôi đồng ý không có "cách tiếp cận" duy nhất nào để giải quyết vấn đề này một cách sạch sẽ. Tuy nhiên, "kiểm tra tốt nhất có thể" đang đặt thanh rất thấp. Tôi sẽ trở lại với những phát hiện của tôi.
Zach Burlingame

Trong Java: Gói java.util.conc hiện chứa một số Lớp xấu được biết đến, có thể giúp viết JUnit-Tests xác định. Hãy xem - CountDownLatch - Semaphore - Trao đổi
Synox

Bạn có thể cung cấp một liên kết đến câu hỏi liên quan đến thử nghiệm đơn vị trước đây của bạn xin vui lòng?
Andrew Grimm


7
Tôi nghĩ điều quan trọng cần lưu ý là câu hỏi này đã 8 tuổi và các thư viện ứng dụng đã đi một chặng đường dài trong thời gian này. Trong "kỷ nguyên hiện đại" (2016), sự phát triển đa luồng xuất hiện chủ yếu trong các hệ thống nhúng. Nhưng nếu bạn đang làm việc trên một ứng dụng máy tính để bàn hoặc điện thoại, trước tiên hãy khám phá các lựa chọn thay thế. Các môi trường ứng dụng như .NET hiện bao gồm các công cụ để quản lý hoặc đơn giản hóa rất nhiều có thể 90% các tình huống đa luồng phổ biến. (asnync / await, PLinq, IObservable, TPL ...). Mã đa luồng là khó. Nếu bạn không phát minh lại bánh xe, bạn không phải kiểm tra lại.
Paul Williams

Câu trả lời:


245

Hãy nhìn xem, không có cách nào dễ dàng để làm điều này. Tôi đang làm việc trên một dự án vốn đã đa luồng. Các sự kiện đến từ hệ điều hành và tôi phải xử lý chúng đồng thời.

Cách đơn giản nhất để đối phó với việc kiểm tra mã ứng dụng đa luồng phức tạp là: Nếu quá phức tạp để kiểm tra, bạn đã làm sai. Nếu bạn có một cá thể duy nhất có nhiều luồng hoạt động theo nó và bạn không thể kiểm tra các tình huống trong đó các luồng này nối với nhau, thì thiết kế của bạn cần được làm lại. Nó vừa đơn giản vừa phức tạp như thế này.

Có nhiều cách để lập trình cho đa luồng mà tránh các luồng chạy qua các thể hiện cùng một lúc. Đơn giản nhất là làm cho tất cả các đối tượng của bạn bất biến. Tất nhiên, điều đó thường không thể. Vì vậy, bạn phải xác định những vị trí đó trong thiết kế của bạn nơi các luồng tương tác với cùng một thể hiện và giảm số lượng các vị trí đó. Bằng cách này, bạn cô lập một vài lớp trong đó thực sự đa luồng xảy ra, làm giảm sự phức tạp chung của việc kiểm tra hệ thống của bạn.

Nhưng bạn phải nhận ra rằng ngay cả khi làm điều này, bạn vẫn không thể kiểm tra mọi tình huống trong đó hai luồng tác động lên nhau. Để làm điều đó, bạn phải chạy đồng thời hai luồng trong cùng một bài kiểm tra, sau đó kiểm soát chính xác những dòng họ đang thực hiện tại bất kỳ thời điểm nào. Điều tốt nhất bạn có thể làm là mô phỏng tình huống này. Nhưng điều này có thể yêu cầu bạn viết mã cụ thể để thử nghiệm và đó là một nửa bước để hướng tới một giải pháp thực sự.

Có lẽ cách tốt nhất để kiểm tra mã cho các vấn đề luồng là thông qua phân tích tĩnh của mã. Nếu mã luồng của bạn không tuân theo một tập hợp hữu hạn các mẫu an toàn của luồng, thì bạn có thể gặp vấn đề. Tôi tin rằng Phân tích mã trong VS có chứa một số kiến ​​thức về luồng, nhưng có lẽ không nhiều.

Hãy nhìn xem, vì mọi thứ hiện tại (và có lẽ sẽ có thời điểm tốt), cách tốt nhất để kiểm tra các ứng dụng đa luồng là giảm độ phức tạp của mã luồng càng nhiều càng tốt. Giảm thiểu các khu vực nơi các chủ đề tương tác, kiểm tra tốt nhất có thể và sử dụng phân tích mã để xác định các khu vực nguy hiểm.


1
Phân tích mã là tuyệt vời nếu bạn đối phó với một ngôn ngữ / khung cho phép nó. EG: Findbugs sẽ tìm thấy các vấn đề tương tranh được chia sẻ rất đơn giản và dễ dàng với các biến tĩnh. Những gì nó không thể tìm thấy là các mẫu thiết kế đơn lẻ, nó giả định rằng tất cả các đối tượng có thể được tạo nhiều lần. Plugin này không đủ cho các khung như Spring.
Zombie

3
thực sự có một cách chữa trị: các đối tượng hoạt động. drdobbs.com/abul/prefer-USE-active-objects-instead-of-n/NH
Dill

6
Mặc dù đây là lời khuyên tốt, tôi vẫn không ngừng hỏi, "làm thế nào để kiểm tra những khu vực tối thiểu đó, nơi cần nhiều chủ đề?"
Bryan Rayner

5
"Nếu quá phức tạp để kiểm tra, bạn đã làm sai" - tất cả chúng ta phải đi sâu vào mã kế thừa mà chúng tôi đã không viết. Làm thế nào để quan sát này giúp bất cứ ai chính xác?
Ronna

2
Phân tích tĩnh có thể là một ý tưởng tốt, nhưng nó không phải là thử nghiệm. Bài đăng này thực sự không trả lời câu hỏi, đó là về cách kiểm tra.
Warren Dew

96

Đã được một lúc khi câu hỏi này được đăng, nhưng nó vẫn chưa được trả lời ...

Câu trả lời của kleolb02 là một câu trả lời hay. Tôi sẽ cố gắng đi vào chi tiết hơn.

Có một cách, mà tôi thực hành cho mã C #. Đối với các bài kiểm tra đơn vị, bạn sẽ có thể lập trình các bài kiểm tra có thể lặp lại , đây là thách thức lớn nhất trong mã đa luồng. Vì vậy, câu trả lời của tôi nhằm mục đích buộc mã không đồng bộ vào một khai thác thử nghiệm, hoạt động đồng bộ .

Đó là một ý tưởng từ cuốn sách của Gerard Meszardos " Các mẫu thử nghiệm xUnit và được gọi là "Đối tượng khiêm tốn" (tr. 695): Bạn phải tách mã logic lõi và bất kỳ thứ gì có mùi giống như mã không đồng bộ với nhau. Điều này sẽ dẫn đến một lớp cho logic cốt lõi, hoạt động đồng bộ .

Điều này đặt bạn vào vị trí để kiểm tra mã logic cốt lõi trong một cách đồng bộ . Bạn có quyền kiểm soát tuyệt đối về thời gian của các cuộc gọi bạn đang thực hiện trên logic cốt lõi và do đó có thể thực hiện các bài kiểm tra có thể lặp lại . Và đây là lợi ích của bạn từ việc tách biệt logic cốt lõi và logic không đồng bộ.

Nhu cầu logic cốt lõi này được bao bọc bởi một lớp khác, chịu trách nhiệm nhận các cuộc gọi đến logic lõi không đồng bộ và các đại biểu các cuộc gọi này cho logic lõi. Mã sản xuất sẽ chỉ truy cập logic cốt lõi thông qua lớp đó. Bởi vì lớp này chỉ nên ủy thác các cuộc gọi, nên đây là một lớp rất "ngu ngốc" mà không có nhiều logic. Vì vậy, bạn có thể giữ các bài kiểm tra đơn vị của mình cho lớp làm việc không đồng bộ này ở mức tối thiểu.

Bất cứ điều gì ở trên (kiểm tra tương tác giữa các lớp) là kiểm tra thành phần. Cũng trong trường hợp này, bạn sẽ có thể kiểm soát tuyệt đối thời gian, nếu bạn tuân theo mẫu "Đối tượng khiêm tốn".


1
Nhưng đôi khi nếu các chủ đề hợp tác tốt với nhau cũng là điều cần được kiểm tra, phải không? Chắc chắn tôi sẽ tách logic cốt lõi khỏi phần async sau khi đọc câu trả lời của bạn. Nhưng tôi vẫn sẽ kiểm tra logic thông qua các giao diện không đồng bộ với một cuộc gọi lại làm việc trên tất cả các chủ đề đã được thực hiện.
CopperCash

Còn hệ thống đa xử lý thì sao?
Technophile

65

Thực sự khó khăn! Trong các thử nghiệm đơn vị (C ++) của tôi, tôi đã chia nó thành nhiều loại dọc theo dòng của mẫu đồng thời được sử dụng:

  1. Kiểm thử đơn vị cho các lớp hoạt động trong một luồng và không nhận biết luồng - dễ dàng, kiểm tra như bình thường.

  2. Kiểm tra đơn vị cho các đối tượng Giám sát (những đối tượng thực thi các phương thức được đồng bộ hóa trong luồng điều khiển của người gọi) để lộ API công khai được đồng bộ hóa - khởi tạo nhiều luồng giả lập thực hiện API. Xây dựng các kịch bản thực hiện các điều kiện bên trong của đối tượng thụ động. Bao gồm một bài kiểm tra chạy dài hơn về cơ bản đánh bại cái quái đó từ nhiều luồng trong một khoảng thời gian dài. Điều này là không khoa học tôi biết nhưng nó xây dựng sự tự tin.

  3. Kiểm tra đơn vị cho các đối tượng Hoạt động (những đối tượng đóng gói luồng hoặc luồng điều khiển của riêng chúng) - tương tự như # 2 ở trên với các biến thể tùy thuộc vào thiết kế lớp. API công khai có thể bị chặn hoặc không chặn, người gọi có thể có được tương lai, dữ liệu có thể đến hàng đợi hoặc cần phải được xử lý. Có nhiều kết hợp có thể ở đây; hộp trắng đi. Vẫn yêu cầu nhiều luồng giả để thực hiện cuộc gọi đến đối tượng được thử nghiệm.

Như một bên:

Trong đào tạo nhà phát triển nội bộ mà tôi làm, tôi dạy các Trụ cột đồng thời và hai mô hình này làm khuôn khổ chính để suy nghĩ và giải mã các vấn đề tương tranh. Rõ ràng có những khái niệm tiên tiến hơn ngoài kia nhưng tôi đã thấy rằng bộ cơ bản này giúp các kỹ sư tránh xa món súp. Nó cũng dẫn đến mã có thể kiểm tra được nhiều đơn vị hơn, như được mô tả ở trên.


51

Tôi đã phải đối mặt với vấn đề này nhiều lần trong những năm gần đây khi viết mã xử lý luồng cho một số dự án. Tôi đang cung cấp một câu trả lời muộn vì hầu hết các câu trả lời khác, trong khi cung cấp giải pháp thay thế, không thực sự trả lời câu hỏi về kiểm tra. Câu trả lời của tôi được đề cập đến các trường hợp không có sự thay thế cho mã đa luồng; Tôi bao gồm các vấn đề thiết kế mã cho đầy đủ, nhưng cũng thảo luận về thử nghiệm đơn vị.

Viết mã đa luồng có thể kiểm tra

Điều đầu tiên cần làm là tách mã xử lý luồng sản xuất của bạn khỏi tất cả các mã thực hiện xử lý dữ liệu thực tế. Bằng cách đó, việc xử lý dữ liệu có thể được kiểm tra dưới dạng mã đơn luồng và điều duy nhất mà mã đa luồng thực hiện là phối hợp các luồng.

Điều thứ hai cần nhớ là các lỗi trong mã đa luồng là xác suất; các lỗi xuất hiện ít thường xuyên nhất là các lỗi sẽ lén lút vào sản xuất, sẽ khó sinh sản ngay cả trong sản xuất và do đó sẽ gây ra những vấn đề lớn nhất. Vì lý do này, cách tiếp cận mã hóa tiêu chuẩn của việc viết mã nhanh chóng và sau đó gỡ lỗi cho đến khi nó hoạt động là một ý tưởng tồi cho mã đa luồng; nó sẽ dẫn đến mã nơi sửa các lỗi dễ dàng và các lỗi nguy hiểm vẫn còn đó.

Thay vào đó, khi viết mã đa luồng, bạn phải viết mã với thái độ rằng bạn sẽ tránh viết lỗi ngay từ đầu. Nếu bạn đã loại bỏ mã xử lý dữ liệu đúng cách, mã xử lý luồng phải đủ nhỏ - tốt nhất là một vài dòng, tệ nhất là vài chục dòng - rằng bạn có cơ hội viết mã mà không viết lỗi và chắc chắn không viết nhiều lỗi , nếu bạn hiểu luồng, hãy dành thời gian và cẩn thận.

Viết bài kiểm tra đơn vị cho mã đa luồng

Khi mã đa luồng được viết càng cẩn thận càng tốt, vẫn đáng để viết các bài kiểm tra cho mã đó. Mục đích chính của các bài kiểm tra không phải là quá nhiều để kiểm tra các lỗi điều kiện cuộc đua phụ thuộc thời gian cao - không thể kiểm tra lặp lại các điều kiện cuộc đua như vậy - mà là để kiểm tra rằng chiến lược khóa của bạn để ngăn chặn các lỗi đó cho phép nhiều luồng tương tác như dự định .

Để kiểm tra đúng hành vi khóa chính xác, kiểm tra phải bắt đầu nhiều luồng. Để làm cho bài kiểm tra có thể lặp lại, chúng tôi muốn các tương tác giữa các luồng xảy ra theo thứ tự dự đoán. Chúng tôi không muốn đồng bộ hóa bên ngoài các luồng trong thử nghiệm, vì điều đó sẽ che giấu các lỗi có thể xảy ra trong sản xuất khi các luồng không được đồng bộ hóa bên ngoài. Điều đó khiến cho việc sử dụng độ trễ thời gian để đồng bộ hóa luồng, đây là kỹ thuật mà tôi đã sử dụng thành công bất cứ khi nào tôi phải viết các bài kiểm tra mã đa luồng.

Nếu độ trễ quá ngắn, thì thử nghiệm trở nên mong manh, bởi vì sự khác biệt nhỏ về thời gian - nói giữa các máy khác nhau mà thử nghiệm có thể chạy - có thể khiến thời gian bị tắt và thử nghiệm thất bại. Những gì tôi thường làm là bắt đầu với độ trễ gây ra lỗi thử nghiệm, tăng độ trễ để thử nghiệm vượt qua đáng tin cậy trên máy phát triển của tôi và sau đó tăng gấp đôi độ trễ vượt quá để thử nghiệm có cơ hội vượt qua các máy khác. Điều này không có nghĩa là thử nghiệm sẽ mất một lượng thời gian vĩ mô, mặc dù theo kinh nghiệm của tôi, thiết kế thử nghiệm cẩn thận có thể giới hạn thời gian đó không quá một chục giây. Vì bạn không nên có nhiều nơi yêu cầu mã phối hợp luồng trong ứng dụng của mình, điều đó có thể chấp nhận được đối với bộ thử nghiệm của bạn.

Cuối cùng, theo dõi số lượng lỗi bắt gặp trong bài kiểm tra của bạn. Nếu thử nghiệm của bạn có phạm vi bảo hiểm mã 80%, có thể dự kiến ​​sẽ bắt được khoảng 80% lỗi của bạn. Nếu thử nghiệm của bạn được thiết kế tốt nhưng không tìm thấy lỗi, có khả năng hợp lý là bạn không có thêm lỗi sẽ chỉ xuất hiện trong sản xuất. Nếu thử nghiệm bắt được một hoặc hai lỗi, bạn vẫn có thể gặp may. Ngoài ra, và bạn có thể muốn xem xét đánh giá cẩn thận hoặc thậm chí viết lại hoàn toàn mã xử lý luồng của mình, vì có khả năng mã đó vẫn chứa các lỗi ẩn sẽ rất khó tìm thấy cho đến khi mã được sản xuất và rất Khó sửa rồi.


3
Kiểm tra chỉ có thể tiết lộ sự hiện diện của lỗi, không phải sự vắng mặt của họ. Câu hỏi ban đầu hỏi về một vấn đề 2 luồng, trong trường hợp đó có thể kiểm tra toàn diện, nhưng thường thì không. Đối với bất cứ điều gì ngoài các kịch bản đơn giản nhất, bạn có thể phải cắn viên đạn và sử dụng các phương pháp chính thức - nhưng đừng bỏ qua các bài kiểm tra đơn vị! Viết mã đa luồng chính xác là khó ngay từ đầu, nhưng một vấn đề khó không kém là chứng minh tương lai chống lại hồi quy.
Paul Williams

4
Tóm tắt tuyệt vời của một trong những cách hiểu ít nhất. Câu trả lời của bạn là đập vào sự phân biệt thực sự mà ppl thường bỏ qua.
prash

1
Một chục giây là một khoảng thời gian khá dài, ngay cả khi bạn chỉ có vài trăm bài kiểm tra với độ dài đó ...
Toby Speight

1
@TobySpeight Các bài kiểm tra dài so với các bài kiểm tra đơn vị bình thường. Tôi đã thấy rằng một nửa tá các thử nghiệm là quá đủ nếu mã luồng được thiết kế đúng cách đơn giản nhất có thể, mặc dù vậy - cần vài trăm thử nghiệm đa luồng sẽ gần như chắc chắn chỉ ra sự sắp xếp luồng quá phức tạp.
Warren Dew

2
Đó là một lý lẽ tốt để giữ logic luồng của bạn tách rời khỏi chức năng như bạn có thể (tôi biết, nói dễ hơn nhiều so với thực hiện). Và, nếu có thể, việc chia bộ thử nghiệm thành các bộ "mọi thay đổi" và "cam kết trước" (để các thử nghiệm từng phút của bạn không bị ảnh hưởng quá nhiều).
Toby Speight

22

Tôi cũng gặp vấn đề nghiêm trọng khi kiểm tra mã đa luồng. Sau đó, tôi tìm thấy một giải pháp thực sự tuyệt vời trong "Các mẫu thử nghiệm xUnit" của Gerard Meszaros. Mẫu anh mô tả được gọi là Đối tượng khiêm tốn .

Về cơ bản, nó mô tả cách bạn có thể trích xuất logic thành một thành phần riêng biệt, dễ kiểm tra được tách rời khỏi môi trường của nó. Sau khi bạn kiểm tra logic này, bạn có thể kiểm tra hành vi phức tạp (đa luồng, thực thi không đồng bộ, v.v ...)


20

Có một vài công cụ xung quanh khá tốt. Dưới đây là một bản tóm tắt của một số trong những người Java.

Một số công cụ phân tích tĩnh tốt bao gồm FindBugs (đưa ra một số gợi ý hữu ích), JLint , Java Pathfinder (JPF & JPF2) và Bogor .

MultithreadedTC là một công cụ phân tích động khá tốt (được tích hợp vào JUnit) nơi bạn phải thiết lập các trường hợp thử nghiệm của riêng mình.

ConTest từ IBM Research rất thú vị. Nó ghi mã của bạn bằng cách chèn tất cả các loại hành vi sửa đổi luồng (ví dụ: ngủ & năng suất) để cố gắng phát hiện ra các lỗi một cách ngẫu nhiên.

SPIN là một công cụ thực sự tuyệt vời để mô hình hóa các thành phần Java (và các thành phần khác) của bạn, nhưng bạn cần phải có một số khung hữu ích. Thật khó để sử dụng, nhưng vô cùng mạnh mẽ nếu bạn biết cách sử dụng nó. Khá nhiều công cụ sử dụng SPIN bên dưới mui xe.

MultithreadedTC có lẽ là chính thống nhất, nhưng một số công cụ phân tích tĩnh được liệt kê ở trên chắc chắn đáng xem xét.


16

Awaitility cũng có thể hữu ích để giúp bạn viết bài kiểm tra đơn vị xác định. Nó cho phép bạn đợi cho đến khi một số trạng thái ở đâu đó trong hệ thống của bạn được cập nhật. Ví dụ:

await().untilCall( to(myService).myMethod(), greaterThan(3) );

hoặc là

await().atMost(5,SECONDS).until(fieldIn(myObject).ofType(int.class), equalTo(1));

Nó cũng có hỗ trợ Scala và Groovy.

await until { something() > 4 } // Scala example

1
Sự chờ đợi là tuyệt vời - chính xác những gì tôi đang tìm kiếm!
Forge_7

14

Một cách khác để (kiểm tra) mã luồng, và các hệ thống rất phức tạp nói chung là thông qua Kiểm tra Fuzz . Nó không tuyệt vời, và nó sẽ không tìm thấy mọi thứ, nhưng nó có thể hữu ích và đơn giản để làm.

Trích dẫn:

Kiểm tra Fuzz hoặc fuzzing là một kỹ thuật kiểm thử phần mềm cung cấp dữ liệu ngẫu nhiên ("fuzz") cho các đầu vào của chương trình. Nếu chương trình không thành công (ví dụ: do sự cố hoặc do lỗi xác nhận mã tích hợp), các lỗi có thể được ghi nhận. Ưu điểm lớn của thử nghiệm fuzz là thiết kế thử nghiệm cực kỳ đơn giản và không có định kiến ​​về hành vi hệ thống.

...

Kiểm thử Fuzz thường được sử dụng trong các dự án phát triển phần mềm lớn sử dụng thử nghiệm hộp đen. Các dự án này thường có ngân sách để phát triển các công cụ kiểm tra và kiểm tra fuzz là một trong những kỹ thuật mang lại lợi ích cao cho tỷ lệ chi phí.

...

Tuy nhiên, kiểm tra fuzz không thay thế cho kiểm tra toàn diện hoặc các phương thức chính thức: nó chỉ có thể cung cấp một mẫu ngẫu nhiên về hành vi của hệ thống và trong nhiều trường hợp vượt qua kiểm tra fuzz chỉ có thể chứng minh rằng một phần mềm xử lý các ngoại lệ mà không gặp sự cố, thay vì cư xử đúng đắn. Do đó, kiểm tra fuzz chỉ có thể được coi là một công cụ tìm lỗi chứ không phải là một sự đảm bảo về chất lượng.


13

Tôi đã làm rất nhiều điều này, và vâng nó rất tệ.

Một số lời khuyên:

  • GroboUtils để chạy nhiều luồng thử nghiệm
  • alphaWorks ConTest đến các lớp nhạc cụ để tạo ra sự xen kẽ khác nhau giữa các lần lặp
  • Tạo một throwabletrường và kiểm tra nó trong tearDown(xem Liệt kê 1). Nếu bạn bắt gặp một ngoại lệ xấu trong một luồng khác, chỉ cần gán nó cho có thể ném được.
  • Tôi đã tạo lớp utils trong Liệt kê 2 và đã thấy nó là vô giá, đặc biệt là WaitForVerify và WaitForCondition, điều này sẽ làm tăng đáng kể hiệu năng của các bài kiểm tra của bạn.
  • Sử dụng tốt AtomicBooleantrong các bài kiểm tra của bạn. Đây là luồng an toàn và bạn thường sẽ cần một loại tham chiếu cuối cùng để lưu trữ các giá trị từ các lớp gọi lại và tương tự. Xem ví dụ trong Liệt kê 3.
  • Đảm bảo luôn luôn kiểm tra thời gian chờ của bạn (ví dụ @Test(timeout=60*1000):), vì các kiểm tra đồng thời đôi khi có thể bị treo vĩnh viễn khi chúng bị hỏng.

Liệt kê 1:

@After
public void tearDown() {
    if ( throwable != null )
        throw throwable;
}

Liệt kê 2:

import static org.junit.Assert.fail;
import java.io.File;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Random;
import org.apache.commons.collections.Closure;
import org.apache.commons.collections.Predicate;
import org.apache.commons.lang.time.StopWatch;
import org.easymock.EasyMock;
import org.easymock.classextension.internal.ClassExtensionHelper;
import static org.easymock.classextension.EasyMock.*;

import ca.digitalrapids.io.DRFileUtils;

/**
 * Various utilities for testing
 */
public abstract class DRTestUtils
{
    static private Random random = new Random();

/** Calls {@link #waitForCondition(Integer, Integer, Predicate, String)} with
 * default max wait and check period values.
 */
static public void waitForCondition(Predicate predicate, String errorMessage) 
    throws Throwable
{
    waitForCondition(null, null, predicate, errorMessage);
}

/** Blocks until a condition is true, throwing an {@link AssertionError} if
 * it does not become true during a given max time.
 * @param maxWait_ms max time to wait for true condition. Optional; defaults
 * to 30 * 1000 ms (30 seconds).
 * @param checkPeriod_ms period at which to try the condition. Optional; defaults
 * to 100 ms.
 * @param predicate the condition
 * @param errorMessage message use in the {@link AssertionError}
 * @throws Throwable on {@link AssertionError} or any other exception/error
 */
static public void waitForCondition(Integer maxWait_ms, Integer checkPeriod_ms, 
    Predicate predicate, String errorMessage) throws Throwable 
{
    waitForCondition(maxWait_ms, checkPeriod_ms, predicate, new Closure() {
        public void execute(Object errorMessage)
        {
            fail((String)errorMessage);
        }
    }, errorMessage);
}

/** Blocks until a condition is true, running a closure if
 * it does not become true during a given max time.
 * @param maxWait_ms max time to wait for true condition. Optional; defaults
 * to 30 * 1000 ms (30 seconds).
 * @param checkPeriod_ms period at which to try the condition. Optional; defaults
 * to 100 ms.
 * @param predicate the condition
 * @param closure closure to run
 * @param argument argument for closure
 * @throws Throwable on {@link AssertionError} or any other exception/error
 */
static public void waitForCondition(Integer maxWait_ms, Integer checkPeriod_ms, 
    Predicate predicate, Closure closure, Object argument) throws Throwable 
{
    if ( maxWait_ms == null )
        maxWait_ms = 30 * 1000;
    if ( checkPeriod_ms == null )
        checkPeriod_ms = 100;
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    while ( !predicate.evaluate(null) ) {
        Thread.sleep(checkPeriod_ms);
        if ( stopWatch.getTime() > maxWait_ms ) {
            closure.execute(argument);
        }
    }
}

/** Calls {@link #waitForVerify(Integer, Object)} with <code>null</code>
 * for {@code maxWait_ms}
 */
static public void waitForVerify(Object easyMockProxy)
    throws Throwable
{
    waitForVerify(null, easyMockProxy);
}

/** Repeatedly calls {@link EasyMock#verify(Object[])} until it succeeds, or a
 * max wait time has elapsed.
 * @param maxWait_ms Max wait time. <code>null</code> defaults to 30s.
 * @param easyMockProxy Proxy to call verify on
 * @throws Throwable
 */
static public void waitForVerify(Integer maxWait_ms, Object easyMockProxy)
    throws Throwable
{
    if ( maxWait_ms == null )
        maxWait_ms = 30 * 1000;
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    for(;;) {
        try
        {
            verify(easyMockProxy);
            break;
        }
        catch (AssertionError e)
        {
            if ( stopWatch.getTime() > maxWait_ms )
                throw e;
            Thread.sleep(100);
        }
    }
}

/** Returns a path to a directory in the temp dir with the name of the given
 * class. This is useful for temporary test files.
 * @param aClass test class for which to create dir
 * @return the path
 */
static public String getTestDirPathForTestClass(Object object) 
{

    String filename = object instanceof Class ? 
        ((Class)object).getName() :
        object.getClass().getName();
    return DRFileUtils.getTempDir() + File.separator + 
        filename;
}

static public byte[] createRandomByteArray(int bytesLength)
{
    byte[] sourceBytes = new byte[bytesLength];
    random.nextBytes(sourceBytes);
    return sourceBytes;
}

/** Returns <code>true</code> if the given object is an EasyMock mock object 
 */
static public boolean isEasyMockMock(Object object) {
    try {
        InvocationHandler invocationHandler = Proxy
                .getInvocationHandler(object);
        return invocationHandler.getClass().getName().contains("easymock");
    } catch (IllegalArgumentException e) {
        return false;
    }
}
}

Liệt kê 3:

@Test
public void testSomething() {
    final AtomicBoolean called = new AtomicBoolean(false);
    subject.setCallback(new SomeCallback() {
        public void callback(Object arg) {
            // check arg here
            called.set(true);
        }
    });
    subject.run();
    assertTrue(called.get());
}

2
Thời gian chờ là một ý tưởng tốt, nhưng nếu một bài kiểm tra hết thời gian, bất kỳ kết quả nào sau đó trong lần chạy đó đều bị nghi ngờ. Bài kiểm tra đã hết thời gian có thể vẫn còn một số luồng đang chạy có thể làm bạn bối rối.
Don Kirkby

12

Kiểm tra mã MT cho chính xác, như đã nêu, là một vấn đề khá khó khăn. Cuối cùng, mọi thứ sẽ sôi sục để đảm bảo rằng không có cuộc đua dữ liệu được đồng bộ hóa không chính xác trong mã của bạn. Vấn đề ở đây là có vô cùng nhiều khả năng thực hiện chủ đề (interleavings) trên mà bạn không có nhiều điều khiển (hãy chắc chắn để đọc này bài viết, mặc dù). Trong các kịch bản đơn giản, có thể thực sự có thể chứng minh tính đúng đắn bằng lý luận nhưng điều này thường không phải là trường hợp. Đặc biệt nếu bạn muốn tránh / giảm thiểu đồng bộ hóa và không đi đến tùy chọn đồng bộ hóa rõ ràng / dễ dàng nhất.

Một cách tiếp cận mà tôi tuân theo là viết mã kiểm tra đồng thời cao để làm cho các cuộc đua dữ liệu không bị phát hiện có khả năng xảy ra. Và sau đó tôi chạy các thử nghiệm đó một thời gian :) Tôi đã tình cờ bắt gặp một cuộc nói chuyện trong đó một nhà khoa học máy tính đã trình diễn một công cụ làm việc này (thử nghiệm ngẫu nhiên từ thông số kỹ thuật và sau đó chạy chúng một cách điên cuồng, đồng thời, kiểm tra các bất biến đã xác định bị phá vỡ).

Nhân tiện, tôi nghĩ rằng khía cạnh kiểm tra mã MT này đã không được đề cập ở đây: xác định các bất biến của mã mà bạn có thể kiểm tra ngẫu nhiên. Thật không may, việc tìm ra những bất biến đó cũng là một vấn đề khá khó khăn. Ngoài ra, họ có thể không giữ toàn bộ thời gian trong khi thực hiện, vì vậy bạn phải tìm / thực thi các điểm thực thi mà bạn có thể mong đợi chúng là đúng. Đưa việc thực thi mã đến trạng thái như vậy cũng là một vấn đề khó khăn (và có thể chính nó phải chịu các vấn đề tương tranh. Whew, thật khó khăn!

Một số liên kết thú vị để đọc:

  • Sự xen kẽ xác định : Một khung cho phép buộc xen kẽ một số luồng nhất định và sau đó kiểm tra các bất biến
  • jMock Blitzer : Đồng bộ hóa kiểm tra căng thẳng
  • assertConcản : Phiên bản JUnit của đồng bộ hóa kiểm tra căng thẳng
  • Kiểm tra mã đồng thời : Tổng quan ngắn về hai phương pháp chính của lực lượng vũ phu (kiểm tra căng thẳng) hoặc xác định (đi tìm bất biến)

tác giả đề cập đến ngẫu nhiên trong thử nghiệm. Nó có thể là QuickCheck , được chuyển sang nhiều ngôn ngữ. Bạn có thể xem cuộc nói chuyện về thử nghiệm như vậy cho hệ thống đồng thời tại đây
Tối đa

6

Pete Goodliffe có một loạt bài kiểm tra đơn vịluồng .

Nó khó. Tôi lấy cách dễ dàng hơn và cố gắng giữ cho mã luồng được trừu tượng hóa từ thử nghiệm thực tế. Pete có đề cập rằng cách tôi làm là sai nhưng tôi đã có sự chia ly đúng hoặc tôi đã may mắn.


6
Tôi đã đọc hai bài báo được xuất bản cho đến nay và tôi không thấy chúng rất hữu ích. Ông chỉ nói về những khó khăn mà không đưa ra nhiều lời khuyên cụ thể. Có thể các bài viết trong tương lai sẽ được cải thiện.
Don Kirkby

6

Đối với Java, hãy xem chương 12 của JCIP . Có một số ví dụ cụ thể về việc viết các bài kiểm tra đơn vị xác định, đa luồng để ít nhất kiểm tra tính chính xác và bất biến của mã đồng thời.

"Chứng minh" an toàn luồng với các bài kiểm tra đơn vị khó khăn hơn nhiều. Tôi tin rằng điều này được phục vụ tốt hơn bằng thử nghiệm tích hợp tự động trên nhiều nền tảng / cấu hình khác nhau.


6

Tôi thích viết hai hoặc nhiều phương thức kiểm tra để thực thi trên các luồng song song và mỗi phương thức thực hiện các cuộc gọi vào đối tượng được kiểm tra. Tôi đã sử dụng các cuộc gọi Ngủ () để phối hợp thứ tự các cuộc gọi từ các luồng khác nhau, nhưng điều đó không thực sự đáng tin cậy. Nó cũng chậm hơn rất nhiều vì bạn phải ngủ đủ lâu để thời gian thường hoạt động.

Tôi tìm thấy thư viện TC Java đa luồng từ cùng một nhóm đã viết FindBugs. Nó cho phép bạn chỉ định thứ tự các sự kiện mà không cần sử dụng chế độ Ngủ () và đáng tin cậy. Tôi chưa thử.

Hạn chế lớn nhất đối với phương pháp này là nó chỉ cho phép bạn kiểm tra các kịch bản mà bạn nghi ngờ sẽ gây rắc rối. Như những người khác đã nói, bạn thực sự cần cách ly mã đa luồng của mình thành một số lượng nhỏ các lớp đơn giản để có bất kỳ hy vọng kiểm tra kỹ lưỡng chúng.

Khi bạn đã kiểm tra cẩn thận các kịch bản mà bạn dự kiến ​​sẽ gây rắc rối, một bài kiểm tra không khoa học, ném một loạt các yêu cầu đồng thời trong lớp trong một thời gian là một cách tốt để tìm kiếm sự cố không mong muốn.

Cập nhật: Tôi đã chơi một chút với thư viện TC Java đa luồng và nó hoạt động tốt. Tôi cũng đã chuyển một số tính năng của nó sang phiên bản .NET mà tôi gọi là TickingTest .


5

Tôi xử lý các thử nghiệm đơn vị của các thành phần luồng giống như cách tôi xử lý bất kỳ thử nghiệm đơn vị nào, đó là đảo ngược các khung điều khiển và khung cách ly. Tôi phát triển trong lĩnh vực .Net và ngoài luồng, việc phân luồng (trong số những thứ khác) rất khó (tôi nói gần như không thể) để cô lập hoàn toàn.

Do đó, tôi đã viết các trình bao bọc trông giống như thế này (đơn giản hóa):

public interface IThread
{
    void Start();
    ...
}

public class ThreadWrapper : IThread
{
    private readonly Thread _thread;

    public ThreadWrapper(ThreadStart threadStart)
    {
        _thread = new Thread(threadStart);
    }

    public Start()
    {
        _thread.Start();
    }
}

public interface IThreadingManager
{
    IThread CreateThread(ThreadStart threadStart);
}

public class ThreadingManager : IThreadingManager
{
    public IThread CreateThread(ThreadStart threadStart)
    {
         return new ThreadWrapper(threadStart)
    }
}

Từ đó tôi có thể dễ dàng đưa IThreadingManager vào các thành phần của mình và sử dụng khung lựa chọn riêng biệt của mình để làm cho luồng hoạt động như tôi mong đợi trong quá trình thử nghiệm.

Điều đó đã làm việc rất tốt cho tôi và tôi sử dụng cùng một cách tiếp cận cho nhóm luồng, mọi thứ trong System.En Môi trường, Ngủ, v.v.


5

Hãy xem câu trả lời liên quan của tôi tại

Thiết kế lớp Kiểm tra cho Rào chắn tùy chỉnh

Nó thiên về Java nhưng có một bản tóm tắt hợp lý về các tùy chọn.

Tóm lại, mặc dù (IMO) nó không sử dụng một số khung ưa thích sẽ đảm bảo tính chính xác mà là cách bạn thiết kế mã đa luồng. Chia tách các mối quan tâm (đồng thời và chức năng) đi một cách rất lớn để nâng cao sự tự tin. Phát triển phần mềm hướng đối tượng được hướng dẫn bởi các thử nghiệm giải thích một số tùy chọn tốt hơn tôi có thể.

Phân tích tĩnh và các phương thức chính thức (xem, Đồng thời: Mô hình trạng thái và Chương trình Java ) là một tùy chọn nhưng tôi thấy chúng bị hạn chế sử dụng trong phát triển thương mại.

Đừng quên rằng bất kỳ thử nghiệm kiểu tải / ngâm nào hiếm khi được đảm bảo để làm nổi bật các vấn đề.

Chúc may mắn!


Bạn cũng nên đề cập đến tempus-fugitthư viện của mình ở đây, trong đó helps write and test concurrent code;)
Idolon

4

Tôi mới phát hiện ra (đối với Java) một công cụ có tên là Themesafe. Nó là một công cụ phân tích tĩnh giống như findbugs nhưng đặc biệt để phát hiện các vấn đề đa luồng. Nó không phải là sự thay thế để thử nghiệm nhưng tôi có thể đề xuất nó như là một phần của việc viết Java đa luồng đáng tin cậy.

Nó thậm chí còn nắm bắt được một số vấn đề tiềm ẩn rất tinh vi xung quanh những thứ như sụt giảm lớp, truy cập các đối tượng không an toàn thông qua các lớp đồng thời và phát hiện các bộ sửa đổi dễ bay hơi bị thiếu khi sử dụng mô hình khóa được kiểm tra kép.

Nếu bạn viết Java đa luồng, hãy thử.


3

Bài viết sau đây gợi ý 2 giải pháp. Bao bọc một semaphore (CountDownLatch) và thêm chức năng như dữ liệu bên ngoài từ luồng nội bộ. Một cách khác để đạt được mục đích này là sử dụng Thread Pool (xem Điểm ưa thích).

Sprinkler - Đối tượng đồng bộ hóa nâng cao


3
Vui lòng giải thích các cách tiếp cận ở đây, các liên kết bên ngoài có thể sẽ chết trong tương lai.
Uooo

2

Tôi đã dành hầu hết tuần trước tại một thư viện đại học để nghiên cứu gỡ lỗi mã đồng thời. Vấn đề trung tâm là mã đồng thời là không xác định. Thông thường, gỡ lỗi học tập đã rơi vào một trong ba trại ở đây:

  1. Theo dõi sự kiện / phát lại. Điều này đòi hỏi một người theo dõi sự kiện và sau đó xem xét các sự kiện đã được gửi. Trong khung UT, điều này sẽ liên quan đến việc gửi các sự kiện theo cách thủ công như một phần của bài kiểm tra, và sau đó thực hiện đánh giá sau khi chết.
  2. Kịch bản. Đây là nơi bạn tương tác với mã đang chạy với một bộ kích hoạt. "Trên x> foo, baz ()". Điều này có thể được hiểu thành một khung UT trong đó bạn có một hệ thống thời gian chạy kích hoạt một thử nghiệm nhất định trong một điều kiện nhất định.
  3. Tương tác. Điều này rõ ràng sẽ không hoạt động trong một tình huống thử nghiệm tự động. ;)

Bây giờ, như các nhà bình luận ở trên đã nhận thấy, bạn có thể thiết kế hệ thống đồng thời của mình thành một trạng thái xác định hơn. Tuy nhiên, nếu bạn không làm điều đó đúng cách, bạn sẽ quay lại thiết kế một hệ thống tuần tự một lần nữa.

Đề nghị của tôi sẽ tập trung vào việc có một giao thức thiết kế rất nghiêm ngặt về những gì được xâu chuỗi và những gì không được xâu chuỗi. Nếu bạn hạn chế giao diện của mình để có sự phụ thuộc tối thiểu giữa các yếu tố, nó sẽ dễ dàng hơn nhiều.

Chúc may mắn, và tiếp tục giải quyết vấn đề.


2

Tôi đã có một nhiệm vụ đáng tiếc là kiểm tra mã luồng và chúng chắc chắn là những bài kiểm tra khó nhất tôi từng viết.

Khi viết bài kiểm tra của tôi, tôi đã sử dụng kết hợp các đại biểu và sự kiện. Về cơ bản, tất cả là về việc sử dụng PropertyNotifyChangedcác sự kiện với một WaitCallbackhoặc một số loạiConditionalWaiter thăm dò ý kiến.

Tôi không chắc đây có phải là cách tiếp cận tốt nhất không, nhưng nó đã có kết quả với tôi.


1

Giả sử theo mã "đa luồng" có nghĩa là một cái gì đó là

  • trạng thái và đột biến
  • VÀ truy cập / sửa đổi bởi nhiều luồng đồng thời

Nói cách khác, chúng ta đang nói về việc thử nghiệm lớp / phương thức / đơn vị an toàn chủ đề tùy chỉnh trạng thái - vốn là một con thú rất hiếm hiện nay.

Bởi vì con thú này rất hiếm, trước hết chúng ta cần đảm bảo rằng có tất cả các lý do hợp lệ để viết nó.

Bước 1. Xem xét sửa đổi trạng thái trong cùng bối cảnh đồng bộ hóa.

Ngày nay, thật dễ dàng để viết mã đồng thời và không đồng bộ có thể soạn thảo trong đó IO hoặc các hoạt động chậm khác được tải xuống nền nhưng trạng thái chia sẻ được cập nhật và truy vấn trong một ngữ cảnh đồng bộ hóa. ví dụ async / await task và Rx in .NET, v.v. - tất cả chúng đều có thể kiểm tra được bằng thiết kế, các tác vụ và bộ lập lịch "thực" có thể được thay thế để kiểm tra xác định (tuy nhiên điều này nằm ngoài phạm vi của câu hỏi).

Nghe có vẻ rất hạn chế nhưng phương pháp này hoạt động tốt đáng ngạc nhiên. Có thể viết toàn bộ ứng dụng theo phong cách này mà không cần làm cho bất kỳ chủ đề trạng thái nào an toàn (tôi làm).

Bước 2. Nếu thao tác trạng thái chia sẻ trên bối cảnh đồng bộ hóa đơn lẻ là hoàn toàn không thể.

Hãy chắc chắn rằng bánh xe không được phát minh lại / chắc chắn không có sự thay thế tiêu chuẩn nào có thể phù hợp với công việc. Có thể là mã rất gắn kết và được chứa trong một đơn vị, ví dụ như rất có thể đó là trường hợp đặc biệt của một số cấu trúc dữ liệu an toàn luồng tiêu chuẩn như bản đồ băm hoặc bộ sưu tập hoặc bất cứ điều gì.

Lưu ý: nếu mã lớn / kéo dài trên nhiều lớp VÀ cần thao tác trạng thái đa luồng thì khả năng rất cao là thiết kế không tốt, hãy xem xét lại Bước 1

Bước 3. Nếu đạt được bước này thì chúng ta cần kiểm tra lớp / phương thức / đơn vị an toàn luồng tùy chỉnh của chính chúng ta .

Tôi sẽ thành thật chết người: Tôi chưa bao giờ phải viết các bài kiểm tra thích hợp cho mã như vậy. Hầu hết thời gian tôi rời khỏi Bước 1, đôi khi ở Bước 2. Lần trước tôi phải viết mã an toàn luồng tùy chỉnh từ nhiều năm trước, đó là trước khi tôi chấp nhận thử nghiệm đơn vị / có lẽ tôi sẽ không phải viết nó với kiến ​​thức hiện tại nào.

Nếu tôi thực sự phải kiểm tra mã như vậy ( cuối cùng, câu trả lời thực tế ) thì tôi sẽ thử vài điều dưới đây

  1. Kiểm tra căng thẳng không xác định. ví dụ chạy đồng thời 100 luồng và kiểm tra xem kết quả cuối cùng có nhất quán không. Điều này là điển hình hơn cho thử nghiệm tích hợp / mức độ cao hơn của nhiều kịch bản người dùng nhưng cũng có thể được sử dụng ở cấp độ đơn vị.

  2. Đưa ra một số 'hook' test trong đó test có thể đưa ra một số mã để giúp tạo các kịch bản xác định trong đó một luồng phải thực hiện thao tác trước chuỗi khác. Xấu xí như nó, tôi không thể nghĩ gì tốt hơn.

  3. Kiểm tra định hướng trễ để làm cho các luồng chạy và thực hiện các hoạt động theo thứ tự cụ thể. Nói một cách chính xác, các thử nghiệm như vậy cũng không mang tính quyết định (có khả năng hệ thống đóng băng / bộ sưu tập GC thế giới có thể làm biến dạng sự chậm trễ được phối hợp khác), nó cũng xấu nhưng cho phép tránh được các móc nối.


0

Đối với mã J2E, tôi đã sử dụng SilkPerformer, LoadRunner và JMeter để kiểm tra đồng thời các luồng. Tất cả đều làm điều tương tự. Về cơ bản, họ cung cấp cho bạn một giao diện tương đối đơn giản để quản trị phiên bản máy chủ proxy của họ, được yêu cầu, để phân tích luồng dữ liệu TCP / IP và mô phỏng nhiều người dùng thực hiện đồng thời yêu cầu đến máy chủ ứng dụng của bạn. Máy chủ proxy có thể cung cấp cho bạn khả năng thực hiện những việc như phân tích các yêu cầu được thực hiện, bằng cách hiển thị toàn bộ trang và URL được gửi đến máy chủ, cũng như phản hồi từ máy chủ, sau khi xử lý yêu cầu.

Bạn có thể tìm thấy một số lỗi trong chế độ http không an toàn, nơi bạn ít nhất có thể phân tích dữ liệu biểu mẫu đang được gửi và thay đổi một cách có hệ thống dữ liệu đó cho mỗi người dùng. Nhưng các thử nghiệm thực sự là khi bạn chạy trong https (Lớp ổ cắm bảo mật). Sau đó, bạn cũng phải đấu tranh với việc thay đổi một cách có hệ thống dữ liệu phiên và cookie, có thể phức tạp hơn một chút.

Lỗi tốt nhất tôi từng thấy, trong khi kiểm tra đồng thời, là khi tôi phát hiện ra rằng nhà phát triển đã dựa vào bộ sưu tập rác Java để đóng yêu cầu kết nối được thiết lập khi đăng nhập, đến máy chủ LDAP, khi đăng nhập. Điều này dẫn đến việc người dùng bị lộ đến các phiên của người dùng khác và kết quả rất khó hiểu, khi cố gắng phân tích những gì đã xảy ra khi máy chủ được đưa đến đầu gối, hầu như không thể hoàn thành một giao dịch, cứ sau vài giây.

Cuối cùng, bạn hoặc ai đó có thể sẽ phải khóa xuống và phân tích mã cho những sai lầm giống như tôi vừa đề cập. Và một cuộc thảo luận mở giữa các phòng ban, giống như cuộc thảo luận đã xảy ra, khi chúng tôi mở ra vấn đề được mô tả ở trên, là hữu ích nhất. Nhưng những công cụ này là giải pháp tốt nhất để kiểm tra mã đa luồng. JMeter là nguồn mở. SilkPerformer và LoadRunner là độc quyền. Nếu bạn thực sự muốn biết liệu ứng dụng của bạn có an toàn cho chủ đề hay không, đó là cách các ông lớn làm điều đó. Tôi đã làm điều này cho các công ty lớn một cách chuyên nghiệp, vì vậy tôi không đoán được. Tôi đang nói từ kinh nghiệm cá nhân.

Một lời cảnh báo: phải mất một thời gian để hiểu các công cụ này. Sẽ không phải là vấn đề đơn giản là cài đặt phần mềm và kích hoạt GUI, trừ khi bạn đã tiếp xúc với lập trình đa luồng. Tôi đã cố gắng xác định 3 loại lĩnh vực quan trọng cần hiểu (biểu mẫu, dữ liệu phiên và cookie), với hy vọng rằng ít nhất bắt đầu bằng việc hiểu các chủ đề này sẽ giúp bạn tập trung vào kết quả nhanh chóng, thay vì phải đọc qua toàn bộ tài liệu.


0

Đồng thời là một tương tác phức tạp giữa mô hình bộ nhớ, phần cứng, bộ nhớ cache và mã của chúng tôi. Trong trường hợp Java, ít nhất các thử nghiệm như vậy đã được giải quyết một phần chủ yếu bởi jcstress . Những người tạo ra thư viện đó được biết đến là tác giả của nhiều tính năng tương tranh JVM, GC và Java.

Nhưng ngay cả thư viện này cũng cần kiến ​​thức tốt về đặc tả Mô hình bộ nhớ Java để chúng tôi biết chính xác những gì chúng tôi đang thử nghiệm. Nhưng tôi nghĩ trọng tâm của nỗ lực này là mircobenchmark. Không phải ứng dụng kinh doanh lớn.


0

Có một bài viết về chủ đề này, sử dụng Rust làm ngôn ngữ trong mã ví dụ:

https://medium.com/@polyglot_factotum/rust-concurrency-five-easy- Pieces-871f1c62906a

Tóm lại, mẹo là viết logic đồng thời của bạn sao cho mạnh mẽ đối với tính không xác định liên quan đến nhiều luồng thực thi, sử dụng các công cụ như kênh và kênh.

Sau đó, nếu đó là cách bạn cấu trúc các "thành phần" của mình, cách dễ nhất để kiểm tra chúng là sử dụng các kênh để gửi tin nhắn cho chúng, sau đó chặn trên các kênh khác để xác nhận rằng thành phần đó gửi một số tin nhắn dự kiến.

Bài viết được liên kết đến được viết hoàn toàn bằng cách sử dụng các bài kiểm tra đơn vị.


-1

Nếu bạn đang kiểm tra Thread mới đơn giản (runnable) .run () Bạn có thể giả định Thread để chạy tuần tự runnable

Chẳng hạn, nếu mã của đối tượng được kiểm tra gọi một luồng mới như thế này

Class TestedClass {
    public void doAsychOp() {
       new Thread(new myRunnable()).start();
    }
}

Sau đó, chế nhạo Chủ đề mới và chạy đối số có thể chạy tuần tự có thể giúp

@Mock
private Thread threadMock;

@Test
public void myTest() throws Exception {
    PowerMockito.mockStatic(Thread.class);
    //when new thread is created execute runnable immediately 
    PowerMockito.whenNew(Thread.class).withAnyArguments().then(new Answer<Thread>() {
        @Override
        public Thread answer(InvocationOnMock invocation) throws Throwable {
            // immediately run the runnable
            Runnable runnable = invocation.getArgumentAt(0, Runnable.class);
            if(runnable != null) {
                runnable.run();
            }
            return threadMock;//return a mock so Thread.start() will do nothing         
        }
    }); 
    TestedClass testcls = new TestedClass()
    testcls.doAsychOp(); //will invoke myRunnable.run in current thread
    //.... check expected 
}

-3

(nếu có thể) không sử dụng chủ đề, sử dụng các tác nhân / đối tượng hoạt động. Dễ kiểm tra.


2
@OMTheEternity có thể nhưng nó vẫn là câu trả lời hay nhất.
Dill

-5

Bạn có thể sử dụng EasyMock.makeThreadSafe để kiểm tra luồng xử lý cá thể


Đây hoàn toàn không phải là một cách có thể để kiểm tra mã đa luồng. Vấn đề không phải là mã kiểm tra chạy đa luồng mà là bạn kiểm tra mã thường chạy đa luồng. Và bạn không thể đồng bộ hóa mọi thứ đi vì sau đó bạn thực sự không kiểm tra các cuộc đua dữ liệu nữa.
bennidi
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.