[multithreading] 스레드 코드를 어떻게 단위 테스트해야합니까?

멀티 스레드 코드를 테스트하는 악몽을 피할 수 없었습니다. 사람들이 성공적인 실행을 위해 스레드를 사용하는 코드를 테스트하는 방법에 대해 어떻게 물어 보거나 두 스레드가 주어진 방식으로 상호 작용할 때 나타나는 종류의 문제를 테스트하는 방법에 대해 묻고 싶습니다.

이것은 오늘날 프로그래머에게 정말로 중요한 문제인 것 같습니다.이 지식에 대한 지식을 모으는 것이 유용 할 것입니다.



답변

이걸하는 쉬운 방법은 없어 나는 본질적으로 멀티 스레드 프로젝트에서 일하고 있습니다. 운영 체제에서 이벤트가 발생하며 동시에 처리해야합니다.

복잡한 멀티 스레드 응용 프로그램 코드 테스트를 처리하는 가장 간단한 방법은 다음과 같습니다. 테스트하기에 너무 복잡하면 잘못하고 있습니다. 여러 개의 스레드가있는 단일 인스턴스가 있고 이러한 스레드가 서로 단계별로 진행되는 상황을 테스트 할 수없는 경우 설계를 다시 작성해야합니다. 이것만큼 간단하고 복잡합니다.

스레드가 동시에 인스턴스를 실행하는 것을 방지하는 멀티 스레딩 프로그래밍 방법에는 여러 가지가 있습니다. 가장 간단한 방법은 모든 객체를 불변으로 만드는 것입니다. 물론 일반적으로는 불가능합니다. 따라서 디자인에서 스레드가 동일한 인스턴스와 상호 작용하는 장소를 식별하고 해당 장소의 수를 줄여야합니다. 이렇게하면 멀티 스레딩이 실제로 발생하는 몇 가지 클래스를 격리하여 시스템 테스트의 전체적인 복잡성을 줄일 수 있습니다.

그러나이 작업을 수행해도 두 스레드가 서로 밟는 모든 상황을 테스트 할 수는 없다는 것을 알아야합니다. 이를 위해서는 동일한 테스트에서 두 개의 스레드를 동시에 실행 한 다음 주어진 순간에 어떤 행을 실행하는지 정확하게 제어해야합니다. 가장 좋은 방법은이 상황을 시뮬레이션하는 것입니다. 그러나 테스트를 위해 특별히 코드를 작성해야 할 수도 있으며 이는 진정한 솔루션을 향한 반 단계에 불과합니다.

스레딩 문제에 대한 코드를 테스트하는 가장 좋은 방법은 코드를 정적으로 분석하는 것입니다. 스레드 코드가 유한 스레드 안전 패턴 세트를 따르지 않으면 문제가있을 수 있습니다. VS의 Code Analysis에는 스레딩에 대한 지식이 포함되어 있다고 생각하지만 많지는 않습니다.

현재 상황이 다가오고 있으며 앞으로 다가올 좋은시기가 될 것이므로 멀티 스레드 앱을 테스트하는 가장 좋은 방법은 스레드 코드의 복잡성을 최대한 줄이는 것입니다. 스레드가 상호 작용하는 영역을 최소화하고 최대한 테스트하고 코드 분석을 사용하여 위험 영역을 식별하십시오.


답변

이 질문이 게시 된 지 오래되었지만 아직 답변되지 않았습니다 …

kleolb02 의 답변은 좋은 것입니다. 자세한 내용을 살펴 보겠습니다.

C # 코드를 연습하는 방법이 있습니다. 단위 테스트의 경우 재현 가능한 테스트 를 프로그래밍 할 수 있어야합니다 . 이는 다중 스레드 코드에서 가장 큰 문제입니다. 작동 테스트 장치로 비동기 코드를 강제 대한 내 대답의 목표 그래서 기적 .

Gerard Meszardos의 저서 ” xUnit Test Patterns ” 에서 발췌 한 아이디어 이며 “Humble Object”(p. 695)라고합니다. 핵심 로직 코드와 비동기 코드처럼 냄새가 나는 것은 분리해야합니다. 결과적으로 핵심 로직의 클래스가 생겨 동기식 으로 작동 합니다. .

이를 통해 핵심 논리 코드를 동기식 으로 테스트 수 있습니다. 코어 로직에서 수행하는 통화 타이밍을 완전히 제어 할 수 있으므로 재현 가능한 테스트를 수행 할 수 있습니다 . 그리고 이것이 핵심 논리와 비동기 논리를 분리함으로써 얻는 이점입니다.

이 코어 로직은 다른 클래스에 의해 랩핑되어야하며,이 클래스는 코어 로직에 대한 호출을 비동기 적으로 수신하고 이러한 호출을 코어 로직에 위임 합니다. 생산 코드는 해당 클래스를 통해서만 핵심 로직에 액세스합니다. 이 클래스는 호출 만 위임해야하기 때문에 논리가 많지 않은 매우 “멍청한”클래스입니다. 따라서이 비동기 작업 클래스에 대한 단위 테스트를 최소한으로 유지할 수 있습니다.

위의 모든 것 (클래스 간의 상호 작용 테스트)은 구성 요소 테스트입니다. 또한이 경우 “Humble Object”패턴을 고수하면 타이밍을 절대적으로 제어 할 수 있어야합니다.


답변

참으로 힘든 것! 내 (C ++) 단위 테스트에서 사용 된 동시성 패턴의 선을 따라 여러 범주로 나누었습니다.

  1. 단일 스레드에서 작동하고 스레드를 인식하지 못하는 클래스에 대한 단위 테스트-평소처럼 쉽게 테스트합니다.

  2. 동기화 된 공개 API를 노출시키는 모니터 객체 (호출자의 제어 스레드에서 동기화 된 메소드를 실행하는)에 대한 단위 테스트 -API를 실행하는 여러 모의 스레드를 인스턴스화합니다. 수동 객체의 내부 조건을 행사하는 시나리오를 구성하십시오. 오랜 시간 동안 여러 스레드에서 기본적으로 이길 수있는 장기 실행 테스트를 하나 포함하십시오. 이것은 내가 아는 과학적이지 않지만 자신감을 쌓아줍니다.

  3. 클래스 디자인에 따라 변형 된 위의 # 2와 유사하게 활성 객체 (자체 스레드 또는 스레드를 캡슐화하는 객체)에 대한 단위 테스트 . 퍼블릭 API는 블로킹 또는 비 블로킹 일 수 있으며, 발신자는 미래를 얻거나, 데이터가 대기열에 도착하거나 대기열에서 제외 될 수 있습니다. 여기에는 가능한 많은 조합이 있습니다. 흰색 상자. 테스트중인 객체를 호출하려면 여전히 여러 모의 스레드가 필요합니다.

여담으로:

내가하는 내부 개발자 교육에서는 동시성 필러 와 이러한 두 가지 패턴을 동시성 문제를 생각하고 분해하는 기본 프레임 워크로 가르칩니다 . 분명히 더 고급 개념이 있지만이 기본 세트는 엔지니어가 수프를 피하는 데 도움이된다는 것을 알았습니다. 또한 위에서 설명한 것처럼 더 단위 테스트가 가능한 코드로 이어집니다.


답변

최근 몇 년 동안 여러 프로젝트에 대한 스레드 처리 코드를 작성할 때이 문제에 여러 번 직면했습니다. 다른 답변의 대부분은 대안을 제공하면서 실제로 테스트에 대한 질문에 대답하지 않기 때문에 늦은 답변을 제공하고 있습니다. 내 대답은 멀티 스레드 코드에 대한 대안이없는 경우에 해결됩니다. 완전성을 위해 코드 디자인 문제를 다루고 단위 테스트에 대해서도 설명합니다.

테스트 가능한 다중 스레드 코드 작성

가장 먼저해야 할 일은 프로덕션 스레드 처리 코드를 실제 데이터 처리를 수행하는 모든 코드와 분리하는 것입니다. 이렇게하면 데이터 처리를 단일 스레드 코드로 테스트 할 수 있으며 멀티 스레드 코드가 수행하는 유일한 작업은 스레드를 조정하는 것입니다.

두 번째로 기억해야 할 것은 멀티 스레드 코드의 버그는 확률 적이라는 것입니다. 가장 빈번하게 나타나는 버그는 프로덕션 환경으로 몰래 들어가고 프로덕션 환경에서도 재현하기 어려운 버그로, 가장 큰 문제를 일으킬 것입니다. 이러한 이유로 코드를 빠르게 작성한 다음 작동 할 때까지 디버깅하는 표준 코딩 방식은 멀티 스레드 코드에는 좋지 않습니다. 쉬운 버그가 수정되고 위험한 버그가 여전히 존재하는 코드가 생성됩니다.

대신 멀티 스레드 코드를 작성할 때는 먼저 버그를 작성하지 않으려는 자세로 코드를 작성해야합니다. 데이터 처리 코드를 올바르게 제거했다면 스레드 처리 코드는 충분히 작아야합니다 (바람직하게는 몇 줄, 최악의 경우 수십 줄). 스레딩을 이해하면 시간을내어 조심하십시오.

멀티 스레드 코드에 대한 단위 테스트 작성

멀티 스레드 코드를 최대한 신중하게 작성하면 해당 코드에 대한 테스트를 작성하는 것이 좋습니다. 테스트의 주요 목적은 타이밍에 의존하는 경쟁 조건 버그를 테스트하기위한 것이 아니라 그러한 경쟁 조건을 반복적으로 테스트하는 것이 불가능합니다. 대신 이러한 버그를 방지하기위한 잠금 전략이 여러 스레드가 의도 한대로 상호 작용할 수 있는지 테스트하는 것입니다. .

올바른 잠금 동작을 올바르게 테스트하려면 테스트에서 여러 스레드를 시작해야합니다. 테스트를 반복 가능하게하기 위해 스레드 간의 상호 작용이 예측 가능한 순서로 발생하기를 원합니다. 우리는 테스트에서 스레드를 외부에서 동기화하고 싶지 않습니다. 스레드가 외부에서 동기화되지 않은 프로덕션에서 발생할 수있는 버그를 가리기 때문입니다. 스레드 동기화에 타이밍 지연을 사용하는 것은 멀티 스레드 코드의 테스트를 작성해야 할 때마다 성공적으로 사용한 기술입니다.

지연 시간이 너무 짧으면 테스트가 실행될 수있는 다른 기계 간의 작은 타이밍 차이로 인해 타이밍이 꺼지고 테스트가 실패 할 수 있으므로 테스트가 취약 해집니다. 일반적으로 수행 한 작업은 테스트 실패를 유발하는 지연으로 시작하고 지연을 증가시켜 테스트가 내 개발 시스템에서 안정적으로 통과 한 다음 지연을 두 배로하여 테스트가 다른 시스템을 통과 할 가능성을 높입니다. 이것은 테스트에 거시적 시간이 걸리는 것을 의미하지만, 내 경험상 신중한 테스트 디자인은 그 시간을 12 초 이하로 제한 할 수 있습니다. 애플리케이션에 스레드 조정 코드가 필요한 장소가 많지 않아야하므로 테스트 스위트에 적합해야합니다.

마지막으로 테스트에서 잡은 버그 수를 추적하십시오. 테스트에 80 %의 코드 적용 범위가있는 경우 버그의 약 80 %가 발견 될 수 있습니다. 테스트가 잘 설계되었지만 버그가 발견되지 않으면 프로덕션에만 표시되는 추가 버그가 없을 가능성이 높습니다. 테스트에서 하나 또는 두 개의 버그가 발견되면 여전히 운이 좋을 수 있습니다. 그 외에도 스레드 처리 코드를주의 깊게 검토하거나 완전히 다시 작성하는 것이 좋습니다. 코드에는 여전히 코드가 생산 될 때까지 찾기가 매우 어려운 숨겨진 버그가 포함되어 있기 때문입니다. 그때 고치기 어렵다.


답변

또한 다중 스레드 코드를 테스트하는 데 심각한 문제가있었습니다. 그런 다음 Gerard Meszaros의 “xUnit Test Patterns”에서 정말 멋진 솔루션을 찾았습니다. 그가 묘사 한 패턴을 Humble object 라고 합니다 .

기본적으로 로직을 환경에서 분리 된 별도의 테스트하기 쉬운 구성 요소로 논리를 추출하는 방법에 대해 설명합니다. 이 로직을 테스트 한 후 복잡한 동작 (멀티 스레딩, 비동기 실행 등)을 테스트 할 수 있습니다.


답변

주위에 몇 가지 도구가 있습니다. 다음은 일부 Java에 대한 요약입니다.

좋은 정적 분석 도구로는 FindBugs (유용한 힌트 제공), JLint , Java Pathfinder (JPF & JPF2) 및 Bogor가 있습니다.

MultithreadedTC 는 자체 테스트 사례를 설정해야하는 훌륭한 동적 분석 도구 (JUnit에 통합)입니다.

IBM Research의 ConTest 는 흥미 롭습니다. 모든 종류의 스레드 수정 동작 (예 : 절전 및 수율)을 삽입하여 버그를 무작위로 찾아내어 코드를 계측합니다.

SPIN 은 Java (및 기타) 구성 요소를 모델링하는 데 유용한 도구이지만 유용한 프레임 워크가 필요합니다. 그대로 사용하기는 어렵지만 사용 방법을 알고 있으면 매우 강력합니다. 꽤 많은 도구가 후드 아래에 SPIN을 사용합니다.

MultithreadedTC가 아마도 가장 주류이지만, 위에 나열된 정적 분석 도구 중 일부는 확실히 볼 가치가 있습니다.


답변

결정 성은 단위 결정 테스트를 작성하는 데 도움이 될 수 있습니다. 시스템 어딘가의 상태가 업데이트 될 때까지 기다릴 수 있습니다. 예를 들면 다음과 같습니다.

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

또는

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

스칼라와 그루비도 지원합니다.

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