문서의 선택한 두 판 사이의 차이를 보여줍니다.
양쪽 이전 판 이전 판 다음 판 | 이전 판 | ||
wiki:java:junit:junit_a_cook_s_tour [2020/09/01 13:05] dhan |
wiki:java:junit:junit_a_cook_s_tour [2023/01/13 18:44] (현재) |
||
---|---|---|---|
줄 1: | 줄 1: | ||
====== JUnit A Cook's Tour ====== | ====== JUnit A Cook's Tour ====== | ||
- | 참고 :이 기사는 JUnit 3.8.x를 기반으로합니다. | + | 참고 :이 기사는 JUnit 3.8.x를 기반으로 합니다. |
1. 소개 | 1. 소개 | ||
이전 기사 (Test Infected : Programmers Love Writing Tests, Java Report, July 1998, Volume 3, Number 7 참조)에서 | 이전 기사 (Test Infected : Programmers Love Writing Tests, Java Report, July 1998, Volume 3, Number 7 참조)에서 | ||
- | 간단한 프레임 워크를 사용하여 | + | |
- | 이 기사에서는 내부를 들여다보고 프레임워크 자체가 어떻게 구성되는지 보여줄 것입니다. | + | 이번 기사에서는 내부를 들여다보고 프레임워크 자체가 어떻게 구성되는지 보여줄 것입니다. |
| | ||
우리는 JUnit 프레임워크를 주의 깊게 연구하고 어떻게 구성했는지를 반영했습니다. | 우리는 JUnit 프레임워크를 주의 깊게 연구하고 어떻게 구성했는지를 반영했습니다. | ||
우리는 다양한 수준에서 수업을 찾았습니다. | 우리는 다양한 수준에서 수업을 찾았습니다. | ||
이 기사에서 우리는 그것들을 한꺼번에 전달하려고 시도할 것입니다. | 이 기사에서 우리는 그것들을 한꺼번에 전달하려고 시도할 것입니다. | ||
- | | + | |
| | ||
- | 우리는 프레임 워크의 목표에 대한 토론으로 시작합니다. | + | 우리는 프레임워크의 목표에 대한 토론으로 시작합니다. |
- | 목표는 프레임 워크 자체를 표시하는 동안 많은 작은 세부 사항으로 다시 나타납니다. | + | 목표는 프레임워크 자체를 표시하는 동안 많은 작은 세부 사항으로 다시 나타납니다. |
- | 다음으로 프레임 워크의 설계 및 구현에 | + | 다음으로 프레임 워크의 설계 및 구현에 |
- | 디자인은 문해력있는 프로그램으로 구현되는 패턴 (놀라움, 놀라움)의 관점에서 설명 될 것입니다. | + | 디자인은 문해력 있는 프로그램으로 구현되는 패턴 (놀람, 놀라움)의 관점에서 설명될 것입니다. |
- | 프레임 워크 개발에 대한 몇 가지 선택 사항으로 결론을 내립니다. | + | 프레임워크 개발에 대한 몇 가지 선택 사항으로 결론을 내릴 것입니다. |
2. 목표 | 2. 목표 | ||
JUnit의 목표는 무엇입니까? | JUnit의 목표는 무엇입니까? | ||
| | ||
- | 먼저, 우리는 개발의 가정으로 돌아 가야합니다. | + | 먼저, 우리는 개발의 가정으로 돌아가야 합니다. |
- | 프로그램 기능에 자동화 된 테스트가없는 경우 작동하지 않는다고 가정합니다. | + | 프로그램 기능에 자동화된 테스트가 없는 경우 작동하지 않는다고 가정합니다. |
- | 이것은 개발자가 프로그램 기능이 작동한다고 확신하면 지금은 영원히 작동한다는 일반적인 가정보다 훨씬 안전 해 보입니다. | + | 이것은 개발자가 프로그램 기능이 작동한다고 확신하면 |
+ | | ||
| | ||
- | 이러한 관점에서 개발자는 코드를 작성하고 디버그 할 때 완료되지 않으며 프로그램이 작동 함을 보여주는 테스트도 작성해야합니다. | + | 이러한 관점에서 개발자는 코드를 작성하고 디버그할 때 테스트가 없으면 |
- | 하지만 모두가 너무 바쁘고, 할 일이 너무 많고, 테스트를 할 시간이 없습니다. 이미 작성할 코드가 너무 많습니다. | + | |
- | 테스트 코드도 어떻게 작성해야합니까? | + | 하지만 모두가 너무 바쁘고, 할 일이 너무 많고, 테스트할 시간이 없습니다. |
+ | | ||
+ | 테스트 코드도 어떻게 작성해야 합니까? | ||
+ | | ||
| | ||
- | 따라서 가장 중요한 목표는 개발자가 실제로 테스트를 작성할 것이라는 희망이있는 프레임워크를 | + | 따라서 가장 중요한 목표는 개발자가 실제로 테스트를 작성할 것이라는 희망이 있는 프레임워크를 |
- | 프레임 워크는 익숙한 도구를 사용해야 하므로 새로 배울 내용이 거의 없습니다. | + | 프레임워크는 익숙한 도구를 사용해야 하므로 새로 배울 내용이 거의 없습니다. |
- | 새로운 테스트를 작성하는 데 절대적으로 필요한 것보다 | + | 새로운 테스트를 작성하는데 절대적으로 필요한 것 이상의 |
- | 중복된 노력을 제거해야합니다. | + | 중복된 노력을 제거해야 합니다. |
| | ||
- | 이 모든 테스트가 수행해야한다면 디버거에서 표현식을 작성하는 것만으로 완료됩니다. | + | 이 모든 테스트가 수행해야 한다면 디버거에서 표현식을 작성하는 것만으로 완료됩니다. |
그러나 이것은 테스트에 충분하지 않습니다. | 그러나 이것은 테스트에 충분하지 않습니다. | ||
- | | + | |
- | 통합 후 1 분 후에 | + | 통합 후 1분 후에 |
- | 당신이 오래 전에 떠났을 때. | + | 당신이 오래 자리를 비웠을 때 당신의 프로그램이 5년 후에도 계속 작동할 것이라는 보장도 없기 때문입니다. |
| | ||
따라서 테스트의 두 번째 목표는 시간이 지남에 따라 가치를 유지하는 테스트를 만드는 것입니다. | 따라서 테스트의 두 번째 목표는 시간이 지남에 따라 가치를 유지하는 테스트를 만드는 것입니다. | ||
- | 원래 작성자가 아닌 다른 사람이 테스트를 실행하고 결과를 해석 할 수 있어야합니다. | + | 원래 작성자가 아닌 다른 사람이 테스트를 실행하고 결과를 해석할 수 있어야 합니다. |
- | 다양한 | + | 다양한 |
| | ||
- | 마지막으로 기존 테스트를 활용하여 새 테스트를 생성 할 수 있어야합니다. | + | 마지막으로 기존 테스트를 활용하여 새 테스트를 생성할 수 있어야 합니다. |
- | | + | |
+ | 이게 전부입니다. | ||
3. JUnit의 디자인 | 3. JUnit의 디자인 | ||
- | JUnit의 디자인은 | + | JUnit의 디자인은 (" |
- | 아이디어는 시스템의 아키텍처를 가질 때까지 아무것도없이 | + | 아이디어는 시스템의 아키텍처를 가질 때까지 아무것도 시작하지 않고 패턴을 차례로 적용하여 시스템의 |
- | 해결해야 할 아키텍처 문제를 제시하고이를 해결하는 패턴을 요약 한 다음 패턴이 JUnit에 어떻게 적용되었는지 보여줍니다. | + | |
- | + | ||
- | 3.1 시작하기-TestCase | + | 3.1 시작하기 - TestCase |
- | 먼저 기본 개념 인 TestCase를 나타내는 개체를 만들어야합니다. | + | 먼저 기본 개념인 TestCase를 나타내는 개체를 만들어야 합니다. |
- | 개발자는 종종 테스트 사례를 염두에두고 있지만 다양한 방법으로 이를 실현합니다. | + | 개발자는 종종 테스트 사례를 염두에 두고 있지만 다양한 방법으로 이를 실현합니다. |
| | ||
- | | + | |
- | 디버거 표현식, | + | |
- | 테스트 스크립트. | + | |
| | ||
- | 테스트를 쉽게 조작하려면 객체를 만들어야합니다. | + | 테스트를 쉽게 조작하려면 객체를 만들어야 합니다. |
- | 이것은 개발자의 마음 속에 내재 | + | 이것은 개발자의 마음속에 내재한 테스트를 거쳐 구체적으로 만들어 |
- | 동시에 개체 개발자는 | + | |
- | | + | 동시에 개체 개발자는 |
- | 의도에서 인용하면 " | + | |
+ | | ||
+ | 의도에서 인용하면 " | ||
다음은 TestCase의 클래스 정의에 대한 코드입니다. | 다음은 TestCase의 클래스 정의에 대한 코드입니다. | ||
+ | | ||
<code java> | <code java> | ||
public abstract class TestCase implements Test | public abstract class TestCase implements Test | ||
줄 73: | 줄 80: | ||
} | } | ||
</ | </ | ||
- | + | | |
- | 이 클래스는 상속을 통해 재사용 될 것으로 예상하기 때문에 "공개 추상"으로 선언합니다. | + | 이 클래스는 상속을 통해 재사용 될 것으로 예상하기 때문에 "public abstract"로 선언합니다. |
- | 지금은 | + | 지금은 |
- | 현재 | + | 현재 |
- | 모든 TestCase는 이름으로 생성되므로 테스트가 실패하면 실패한 테스트를 식별 할 수 있습니다. | + | 모든 TestCase는 이름으로 생성되므로 테스트가 실패하면 실패한 테스트를 식별할 수 있습니다. |
+ | |||
<code java> | <code java> | ||
public abstract class TestCase implements Test | public abstract class TestCase implements Test | ||
{ | { | ||
private final String fName; | private final String fName; | ||
+ | | ||
public TestCase (String name) { | public TestCase (String name) { | ||
fName = name; | fName = name; | ||
줄 91: | 줄 99: | ||
} | } | ||
</ | </ | ||
- | JUnit의 | + | |
+ | | ||
우리가 사용하는 표기법은 간단합니다. | 우리가 사용하는 표기법은 간단합니다. | ||
- | 연관된 패턴을 포함하는 음영 처리 된 상자로 클래스에 주석을 답니다. | + | 연관된 패턴을 포함하는 음영 처리된 상자로 클래스에 주석을 답니다. |
- | 패턴에서 클래스의 역할이 분명하면 패턴 이름 만 표시됩니다. | + | 패턴에서 클래스의 역할이 분명하면 패턴 이름만 표시됩니다. |
- | 역할이 명확하지 않으면 음영 처리 된 상자가이 클래스에 해당하는 참가자의 이름으로 확대됩니다. | + | 역할이 명확하지 않으면 음영 처리된 상자가 이 클래스에 해당하는 참가자의 이름으로 확대됩니다. |
- | 이 표기법은 다이어그램의 혼란을 최소화하고 처음에 표시되었습니다 | + | 이 표기법은 다이어그램의 혼란을 최소화하기 위해 |
- | 그림 1은 TestCase에 적용된이 표기법을 보여줍니다. 단일 클래스를 다루고 있고 모호성이 없을 수 있기 때문에 패턴 이름 만 표시됩니다. | + | |
- | + | | |
- | + | 그림 1은 TestCase에 적용된 이 표기법을 보여줍니다. | |
- | 그림 1 TestCase 적용 명령 | + | |
- | + | ||
- | 3.2 in-run ()을 채우기 위한 공백 | + | {{: |
- | 다음으로 해결해야 할 문제는 개발자에게 | + | |
- | TestCase를 | + | 3.2 Blanks to fill in - run() |
- | 그러나 우리가 할 수있는 모든 것이 하나의 변수와 동작이없는 | + | 다음으로 해결해야 할 문제는 개발자에게 |
- | 첫 번째 목표를 충족시키기 위해 많은 일을하지 않아서 테스트를 더 쉽게 작성할 수 있습니다. | + | TestCase를 |
+ | 그러나 우리가 할 수 있는 모든 것이 하나의 변수와 동작이 없는 | ||
+ | 첫 번째 목표를 충족시키기 위해 많은 일을 하지 않아서 테스트를 더 쉽게 작성할 수 있습니다. | ||
| | ||
다행히 모든 테스트에는 공통 구조가 있습니다. | 다행히 모든 테스트에는 공통 구조가 있습니다. | ||
- | | + | |
- | 이는 각 테스트가 새로운 | + | 이는 각 테스트가 새로운 |
이것은 테스트의 가치를 극대화하려는 목표를 지원합니다. | 이것은 테스트의 가치를 극대화하려는 목표를 지원합니다. | ||
| | ||
템플릿 방법은 우리의 문제를 아주 잘 해결합니다. | 템플릿 방법은 우리의 문제를 아주 잘 해결합니다. | ||
" | " | ||
- | | + | 템플릿 방법을 사용하면 하위 클래스가 알고리즘의 구조를 변경하지 않고도 알고리즘의 특정 단계를 재정의할 수 있습니다." |
- | 우리는 개발자가 | + | 우리는 개발자가 |
- | 그러나 이 시퀀스의 실행은 | + | 그러나 이 시퀀스의 실행은 |
| | ||
다음은 템플릿 방법입니다. | 다음은 템플릿 방법입니다. | ||
줄 124: | 줄 135: | ||
public void run() | public void run() | ||
{ | { | ||
- | setUp (); | + | setUp(); |
- | runTest (); | + | runTest(); |
- | tearDown (); | + | tearDown(); |
} | } | ||
</ | </ | ||
줄 132: | 줄 143: | ||
| | ||
<code java> | <code java> | ||
- | protected void runTest () | + | protected void runTest() |
{ | { | ||
... | ... | ||
줄 139: | 줄 150: | ||
<code java> | <code java> | ||
- | protected void setUp () | + | protected void setUp() |
{ | { | ||
... | ... | ||
줄 146: | 줄 157: | ||
<code java> | <code java> | ||
- | protected void tearDown () | + | protected void tearDown() |
{ | { | ||
... | ... | ||
줄 152: | 줄 163: | ||
</ | </ | ||
- | setUp 및 tearDown은 재정의 될 예정이지만 프레임워크에서 호출되므로 보호 된 것으로 선언합니다. | + | setUp 및 tearDown은 재정의될 예정이지만 프레임워크에서 호출되므로 보호된 것으로 선언합니다. |
- | 투어의 두 번째 스냅 샷은 그림 2에 나와 있습니다. | + | 투어의 두 번째 스냅샷은 그림 2에 나와 있습니다. |
+ | {{: | ||
- | 그림 2 TestCase.run ()은 템플릿 메서드를 적용합니다. | ||
- | 3.3 결과보고-테스트 결과 | + | 3.3 Reporting results |
- | TestCase가 | + | TestCase가 |
테스트를 실행하여 실행되는지 확인합니다. | 테스트를 실행하여 실행되는지 확인합니다. | ||
- | 테스트가 실행 된 후 작동 한 작업과 실행되지 않은 작업에 대한 요약을 기록해야합니다. | + | 테스트가 실행된 후 작동한 작업과 실행되지 않은 작업에 대한 요약을 기록해야 합니다. |
| | ||
- | 테스트가 성공하거나 실패 할 확률이 같거나 테스트를 한 번만 실행했다면 TestCase 객체에 | + | 테스트가 성공하거나 실패할 확률이 같거나 테스트를 한 번만 실행했다면 TestCase 객체에 |
- | 테스트가 완료되면 | + | 테스트가 완료되면 |
- | 따라서 실패와 성공에 대한 매우 | + | |
+ | 따라서 실패와 성공에 대한 매우 | ||
| | ||
Smalltalk 모범 사례 패턴 (Beck, K. Smalltalk 모범 사례 패턴, Prentice Hall, 1996 참조)에는 적용 가능한 패턴이 있습니다. | Smalltalk 모범 사례 패턴 (Beck, K. Smalltalk 모범 사례 패턴, Prentice Hall, 1996 참조)에는 적용 가능한 패턴이 있습니다. | ||
- | | + | |
- | 테스트 실행 결과를 수집하기 위해 새 개체 인 TestResult를 만듭니다. | + | |
+ | 테스트 실행 결과를 수집하기 위해 새로운 객체인 TestResult를 만듭니다. | ||
<code java> | <code java> | ||
줄 175: | 줄 188: | ||
{ | { | ||
protected int fRunTests; | protected int fRunTests; | ||
- | public TestResult () { | + | public TestResult () |
+ | | ||
| | ||
} | } | ||
} | } | ||
</ | </ | ||
- | + | | |
- | | + | 이를 사용하려면 TestCase.run() 메서드에 매개 변수를 추가하고 TestResult에 테스트가 실행 중임을 알려야 합니다. |
- | 이를 사용하려면 TestCase.run () 메서드에 매개 변수를 추가하고 TestResult에 테스트가 실행 중임을 알려야합니다. | + | |
<code java> | <code java> | ||
public void run (TestResult 결과) { | public void run (TestResult 결과) { | ||
줄 191: | 줄 204: | ||
} | } | ||
</ | </ | ||
- | 그리고 TestResult는 실행 된 테스트의 수를 추적해야합니다. | + | 그리고 TestResult는 실행된 테스트의 수를 추적해야 합니다. |
<code java> | <code java> | ||
- | 공개 동기화 무효 | + | public synchronized void startTest (Test test) |
+ | { | ||
fRunTests ++; | fRunTests ++; | ||
} | } | ||
</ | </ | ||
- | + | | |
- | 테스트가 다른 스레드에서 실행될 때 단일 TestResult가 결과를 안전하게 수집 할 수 있도록 | + | 테스트가 다른 스레드에서 실행될 때 단일 TestResult가 결과를 안전하게 수집할 수 있도록 |
- | TestResult 메서드 startTest를 | + | TestResult 메서드 startTest를 |
- | 마지막으로 TestCase의 간단한 외부 인터페이스를 유지하려고하므로 자체 TestResult를 생성하는 매개 변수없는 run () 버전을 생성합니다. | + | 마지막으로 TestCase의 간단한 외부 인터페이스를 유지하려고 하므로 자체 TestResult를 생성하는 매개 변수가 없는 run() 버전을 생성합니다. |
<code java> | <code java> | ||
public TestResult run () { | public TestResult run () { | ||
줄 213: | 줄 227: | ||
} | } | ||
</ | </ | ||
- | 그림 3은 다음 설계 스냅 샷을 보여줍니다. | + | 그림 3은 다음 설계 스냅샷을 보여줍니다. |
| | ||
- | | + | {{: |
| | ||
- | 테스트가 항상 올바르게 실행 되었다면 작성하지 않아도됩니다. | + | 테스트가 항상 올바르게 실행되었다면 작성하지 않아도 됩니다. |
- | 테스트는 실패 할 때 흥미 롭습니다. | + | 테스트는 실패할 때 흥미롭습니다. |
- | 특히 실패 할 것으로 예상하지 않은 경우 더욱 그렇습니다. | + | 특히 실패할 것으로 예상하지 않은 경우 더욱더 그렇습니다. |
- | 또한 테스트는 잘못된 결과를 계산하는 등 우리가 예상하는 방식으로 실패 할 수 있으며, | + | 또한 테스트는 잘못된 결과를 계산하는 등 우리가 예상하는 방식으로 실패할 수 있으며, |
- | 예를 들어 배열 경계를 벗어난 쓰기를 통해 더 멋진 방식으로 실패 할 수 있습니다. | + | 예를 들어 배열 경계를 벗어난 쓰기를 통해 더 멋진 방식으로 실패할 수 있습니다. |
- | 테스트가 아무리 실패하더라도 다음 테스트를 실행하려고합니다. | + | 테스트가 아무리 실패하더라도 다음 테스트를 실행하려고 합니다. |
| | ||
- | JUnit은 실패 와 오류를 구분 합니다. | + | JUnit는 실패와 오류를 구분합니다. |
- | 실패 가능성이 예상되고 | + | 실패 가능성이 예상되고 |
오류는 ArrayIndexOutOfBoundsException과 같은 예상치 못한 문제입니다. | 오류는 ArrayIndexOutOfBoundsException과 같은 예상치 못한 문제입니다. | ||
실패는 AssertionFailedError 오류로 표시됩니다. | 실패는 AssertionFailedError 오류로 표시됩니다. | ||
- | 예상치 못한 오류와 실패를 구별하기 위해 추가 catch 절 (1)에서 실패를 포착합니다. | + | 예상치 못한 오류와 실패를 구별하기 위해 추가 catch 절 (1) 에서 실패를 포착합니다. |
- | 두 번째 절 (2)은 다른 모든 예외를 포착하고 테스트 실행이 계속되도록합니다. | + | 두 번째 절 (2) 은 다른 모든 예외를 포착하고 테스트 실행이 계속되도록 합니다. |
<code java> | <code java> | ||
- | public void run (TestResult | + | public void run(TestResult |
- | result.startTest (this); | + | { |
- | | + | result.startTest(this); |
- | | + | |
- | runTest (); | + | |
+ | runTest(); | ||
} | } | ||
- | catch (AssertionFailedError e) {// 1 | + | catch(AssertionFailedError e){// 1 |
- | result.addFailure (this, e); | + | result.addFailure(this, |
} | } | ||
- | catch (Throwable e) {// 2 | + | catch(Throwable e){// 2 |
- | result.addError (this, e); | + | result.addError(this, |
} | } | ||
- | | + | |
- | tearDown (); | + | tearDown(); |
} | } | ||
} | } | ||
</ | </ | ||
- | AssertionFailedError는 TestCase에서 제공하는 assert 메서드에 의해 트리거됩니다. | + | |
- | JUnit은 다양한 목적을 위해 일련의 assert 메서드를 제공합니다. | + | |
+ | JUnit는 다양한 목적을 위해 일련의 assert 메서드를 제공합니다. | ||
다음은 가장 간단한 것입니다. | 다음은 가장 간단한 것입니다. | ||
+ | | ||
<code java> | <code java> | ||
- | protected void assertTrue (boolean condition) { | + | protected void assertTrue (boolean condition) |
- | if (! condition) | + | { |
- | throw new AssertionFailedError (); | + | if(!condition) |
+ | throw new AssertionFailedError(); | ||
} | } | ||
</ | </ | ||
- | AssertionFailedError는 클라이언트 (TestCase 내부의 테스트 메서드)가 포착하기위한 것이 아니라 템플릿 메서드 TestCase.run () 내부에 있습니다. 따라서 오류에서 AssertionFailedError를 파생시킵니다. | + | |
+ | | ||
+ | | ||
+ | | ||
<code java> | <code java> | ||
- | 공용 클래스 | + | public cliass |
+ | { | ||
public AssertionFailedError () {} | public AssertionFailedError () {} | ||
} | } | ||
- | </ | + | </ |
- | TestResult에서 오류를 수집하는 방법은 다음과 같습니다. | + | |
+ | | ||
+ | | ||
<code java> | <code java> | ||
- | 공개 동기화 무효 | + | public synchronized void addError (Test test, Throwable t) |
+ | { | ||
fErrors.addElement (new TestFailure (test, t)); | fErrors.addElement (new TestFailure (test, t)); | ||
} | } | ||
줄 273: | 줄 297: | ||
<code java> | <code java> | ||
- | 공개 동기화 무효 | + | public synchronized void addFailure (Test test, Throwable t) |
+ | { | ||
fFailures.addElement (new TestFailure (test, t)); | fFailures.addElement (new TestFailure (test, t)); | ||
} | } | ||
</ | </ | ||
- | TestFailure는 나중에보고 할 수 있도록 실패한 테스트와 | + | |
+ | | ||
+ | | ||
<code java> | <code java> | ||
- | 공용 클래스 | + | public class TestFailure |
- | protected Test fFailedTest를 확장합니다 . | + | { |
- | | + | protected Test fFailedTest; |
+ | | ||
} | } | ||
</ | </ | ||
- | 수집 매개 변수의 표준 형식은 | + | |
+ | collecting parameter의 표준 형식은 | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
<code java> | <code java> | ||
- | public void testMoneyEquals () { | + | public void testMoneyEquals() |
- | assertTrue (! f12CHF.equals (null)); | + | { |
- | assertEquals (f12CHF, f12CHF); | + | assertTrue(!f12CHF.equals(null)); |
- | assertEquals (f12CHF, new Money (12, " | + | assertEquals(f12CHF, |
- | assertTrue (! f12CHF.equals (f14CHF)); | + | assertEquals(f12CHF, |
+ | assertTrue(!f12CHF.equals(f14CHF)); | ||
} | } | ||
</ | </ | ||
- | JUnit은 TestResult의 다양한 구현과 함께 제공됩니다. | + | |
+ | | ||
기본 구현은 실패 및 오류 수를 계산하고 결과를 수집합니다. | 기본 구현은 실패 및 오류 수를 계산하고 결과를 수집합니다. | ||
TextTestResult는 결과를 수집하여 텍스트 형식으로 표시합니다. | TextTestResult는 결과를 수집하여 텍스트 형식으로 표시합니다. | ||
마지막으로 UITestResult는 그래픽 테스트 상태를 업데이트하기 위해 JUnit Test Runner의 그래픽 버전에서 사용됩니다. | 마지막으로 UITestResult는 그래픽 테스트 상태를 업데이트하기 위해 JUnit Test Runner의 그래픽 버전에서 사용됩니다. | ||
- | TestResult는 프레임 워크의 확장 점입니다. | + | TestResult는 프레임 워크의 확장 |
- | 클라이언트는 자신의 사용자 정의 TestResult 클래스를 정의 할 수 있습니다. | + | 클라이언트는 자신의 사용자 정의 TestResult 클래스를 정의할 수 있습니다. |
- | 예를 들어 HTMLTestResult는 결과를 HTML문서로 보고 합니다. | + | 예를 들어 HTMLTestResult는 결과를 HTML문서로 보고합니다. |
- | + | ||
- | + | 3.4 No stupid subclasses | |
- | 3.4 어리석은 하위 클래스 없음-TestCase | + | 테스트를 나타내기 위해 Command를 적용했습니다. |
- | 테스트를 나타 내기 위해 Command를 적용했습니다. | + | |
- | | + | |
이 간단한 인터페이스를 통해 동일한 인터페이스를 통해 명령의 다른 구현을 호출 할 수 있습니다. | 이 간단한 인터페이스를 통해 동일한 인터페이스를 통해 명령의 다른 구현을 호출 할 수 있습니다. | ||
| | ||
일반적으로 테스트를 실행하려면 인터페이스가 필요합니다. | 일반적으로 테스트를 실행하려면 인터페이스가 필요합니다. | ||
- | 그러나 모든 테스트 케이스는 동일한 클래스에서 다른 메소드로 구현됩니다. | + | 그러나 모든 테스트 케이스는 동일한 클래스에서 다른 메서드로 구현됩니다. |
이것은 클래스의 불필요한 확산을 방지합니다. | 이것은 클래스의 불필요한 확산을 방지합니다. | ||
- | 주어진 테스트 케이스 클래스는 각각 단일 테스트 케이스를 정의하는 다양한 메소드를 구현할 수 있습니다. | + | |
+ | | ||
각 테스트 케이스에는 testMoneyEquals 또는 testMoneyAdd와 같은 설명 이름이 있습니다. | 각 테스트 케이스에는 testMoneyEquals 또는 testMoneyAdd와 같은 설명 이름이 있습니다. | ||
- | 테스트 케이스는 간단한 | + | 테스트 케이스는 간단한 |
- | 동일한 Command 클래스의 다른 인스턴스는 다른 메서드를 사용하여 호출해야합니다. | + | 동일한 Command 클래스의 다른 인스턴스는 다른 메서드를 사용하여 호출해야 합니다. |
따라서 우리의 다음 문제는 테스트 호출자의 관점에서 모든 테스트 케이스를 동일하게 만드는 것입니다. | 따라서 우리의 다음 문제는 테스트 호출자의 관점에서 모든 테스트 케이스를 동일하게 만드는 것입니다. | ||
| | ||
- | 사용 가능한 디자인 패턴으로 해결 된 문제를 검토하면 | + | 사용 가능한 디자인 패턴으로 해결된 문제를 검토하면 |
- | | + | |
이것은 좋은 일치처럼 들립니다. | 이것은 좋은 일치처럼 들립니다. | ||
- | | + | |
- | 그중 하나는 인터페이스를 조정하기 위해 | + | 그중 하나는 인터페이스를 조정하기 위해 |
+ | | ||
예를 들어 testMoneyEquals를 runTest에 적용하려면 MoneyTest의 하위 클래스를 구현하고 runTest를 재정의하여 testMoneyEquals를 호출합니다. | 예를 들어 testMoneyEquals를 runTest에 적용하려면 MoneyTest의 하위 클래스를 구현하고 runTest를 재정의하여 testMoneyEquals를 호출합니다. | ||
+ | | ||
<code java> | <code java> | ||
- | 공개 클래스 | + | public class TestMoneyEquals |
- | public TestMoneyEquals () {super ( " | + | { |
- | | + | public TestMoneyEquals() {super(" |
+ | | ||
} | } | ||
</ | </ | ||
- | 서브 클래 싱을 | + | |
- | Java는 클래스 이름 지정 문제에 대한 흥미로운 Java 관련 솔루션을 제공하는 익명 내부 클래스를 제공합니다. 익명의 내부 클래스를 사용하면 클래스 이름을 만들지 않고도 어댑터를 만들 수 있습니다. | + | sub class를 |
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
+ | |||
+ | | ||
+ | | ||
+ | | ||
<code java> | <code java> | ||
- | TestCase test = new MoneyTest ( " | + | TestCase test = new MoneyTest ( " |
+ | | ||
protected void runTest () {testMoneyEquals (); } | protected void runTest () {testMoneyEquals (); } | ||
}; | }; | ||
</ | </ | ||
- | + | | |
- | 이것은 전체 | + | 이것은 전체 |
- | 가장 간단한 형태의 | + | |
- | + | smalltalk | |
- | JUnit은 클라이언트에게 | + | |
+ | |||
+ | | ||
+ | pluggable | ||
+ | | ||
+ | | ||
+ | | ||
+ | | ||
+ | 여담이지만, 우리는 일반적으로 일반 애플리케이션 코드에서 | ||
+ | | ||
+ | |||
+ | JUnit는 클라이언트에게 | ||
+ | | ||
+ | | ||
+ | reflection을 사용하여 아래와 같이 메서드를 호출합니다. | ||
+ | | ||
+ | | ||
+ | | ||
+ | |||
<code java> | <code java> | ||
- | protected void runTest () throws Throwable { | + | protected void runTest() throws Throwable |
+ | { | ||
Method runMethod = null; | Method runMethod = null; | ||
- | try { | + | try |
- | runMethod = getClass (). getMethod (fName, new Class [0]); | + | |
- | } catch (NoSuchMethodException e) { | + | runMethod = getClass().getMethod(fName, |
- | assertTrue ( " | + | |
} | } | ||
- | try { | + | |
+ | { | ||
+ | assertTrue (" | ||
+ | } | ||
+ | | ||
+ | | ||
runMethod.invoke (this, new Class [0]); | runMethod.invoke (this, new Class [0]); | ||
} | } | ||
줄 356: | 줄 428: | ||
} | } | ||
</ | </ | ||
- | JDK 1.1 리플렉션 | + | |
- | 다음은 | + | |
- | + | | |
- | + | 다음은 | |
- | + | ||
- | 그림 4 : TestCase는 익명 내부 클래스 또는 플러그 형 선택기가있는 | + | {{: |
- | + | ||
- | 3.5 하나 또는 여러 개에 신경 쓰지 마십시오-TestSuite | + | 3.5 Don’t care about one or many - TestSuite |
- | 시스템 상태에 대한 확신을 얻으려면 많은 테스트를 실행해야합니다. | + | 시스템 상태에 대한 확신을 얻으려면 많은 테스트를 실행해야 합니다. |
- | 지금까지 JUnit은 단일 테스트 케이스를 실행하고 테스트 결과에 결과를보고 할 수 있습니다. | + | 지금까지 JUnit는 단일 테스트 케이스를 실행하고 테스트 결과에 결과를 보고 할 수 있습니다. |
다음 과제는 다양한 테스트를 실행할 수 있도록 확장하는 것입니다. | 다음 과제는 다양한 테스트를 실행할 수 있도록 확장하는 것입니다. | ||
이 문제는 테스트 호출자가 하나 또는 여러 테스트 케이스를 실행하는지에 대해 신경 쓸 필요가 없을 때 쉽게 해결할 수 있습니다. | 이 문제는 테스트 호출자가 하나 또는 여러 테스트 케이스를 실행하는지에 대해 신경 쓸 필요가 없을 때 쉽게 해결할 수 있습니다. | ||
이러한 상황에서 가장 많이 사용되는 패턴은 Composite입니다. | 이러한 상황에서 가장 많이 사용되는 패턴은 Composite입니다. | ||
- | " | + | " |
- | Composite를 사용하면 클라이언트가 개별 객체와 객체 구성을 균일하게 처리 할 수 있습니다." | + | |
부분 전체 계층 구조에 대한 요점은 여기서 중요합니다. | 부분 전체 계층 구조에 대한 요점은 여기서 중요합니다. | ||
| | ||
- | | + | |
| | ||
Composite는 다음 참가자를 소개합니다. | Composite는 다음 참가자를 소개합니다. | ||
- | | + | |
- | | + | |
- | Leaf : 컴포넌트 | + | Leaf : Component |
| | ||
이 패턴은 단일 및 복합 객체에 대한 공통 인터페이스를 정의하는 추상 클래스를 도입하도록 알려줍니다. | 이 패턴은 단일 및 복합 객체에 대한 공통 인터페이스를 정의하는 추상 클래스를 도입하도록 알려줍니다. | ||
- | 클래스의 주요 목적은 인터페이스를 정의하는 것입니다. Java에서 Composite를 적용 할 때 우리는 추상 클래스가 아닌 인터페이스를 정의하는 것을 선호합니다. 인터페이스를 사용하면 테스트를 위해 JUnit을 특정 기본 클래스로 커밋하지 않아도됩니다. 필요한 것은 테스트가이 인터페이스를 준수하는 것입니다. 따라서 패턴 설명을 조정하고 테스트 인터페이스를 | + | 클래스의 주요 목적은 인터페이스를 정의하는 것입니다. |
- | public interface Test { | + | |
- | public abstract void run (TestResult result); | + | |
+ | | ||
+ | | ||
+ | |||
+ | <code java> | ||
+ | public interface Test | ||
+ | { | ||
+ | public abstract void run(TestResult result); | ||
} | } | ||
- | TestCase는 Composite의 Leaf에 해당하며 위에서 본 것처럼이 인터페이스를 구현합니다. | + | </ |
- | 다음으로 | + | |
- | + | | |
- | 공용 클래스 | + | 다음으로 |
+ | | ||
+ | | ||
+ | |||
+ | <code java> | ||
+ | public class TestSuite | ||
private Vector fTests = new Vector (); | private Vector fTests = new Vector (); | ||
} | } | ||
- | run () 메서드는 자식에게 위임합니다. | + | </ |
- | public void run (TestResult result) { | + | |
+ | <code java> | ||
+ | public void run(TestResult result) | ||
+ | { | ||
for (Enumeration e = fTests.elements (); e.hasMoreElements ();) { | for (Enumeration e = fTests.elements (); e.hasMoreElements ();) { | ||
- | Test test = (Test) e.nextElement (); | + | Test test = (Test)e.nextElement (); |
- | test.run (결과); | + | test.run (result); |
} | } | ||
} | } | ||
+ | </ | ||
- | 그림 5 : TestSuite는 | + | {{: |
- | 마지막으로 클라이언트는 테스트를 | + | |
- | public void addTest (테스트 테스트) { | + | <code java> |
+ | public void addTest (Test test) | ||
+ | { | ||
fTests.addElement (test); | fTests.addElement (test); | ||
} | } | ||
- | 위의 모든 코드가 테스트 인터페이스에만 의존하는 방식에 유의하십시오. TestCase와 TestSuite는 모두 Test 인터페이스를 따르기 때문에 | + | </ |
- | 다음은 TestSuite를 만드는 예입니다. | + | |
- | + | | |
- | 공개 정적 테스트 스위트 | + | |
- | TestSuite | + | |
- | suite.addTest (new MoneyTest ( " | + | |
- | suite.addTest (new MoneyTest ( " | + | 다음은 TestSuite를 만드는 예입니다. |
+ | |||
+ | <code java> | ||
+ | public static Test suite() | ||
+ | { | ||
+ | TestSuite | ||
+ | suite.addTest(new MoneyTest ( " | ||
+ | suite.addTest(new MoneyTest ( " | ||
} | } | ||
+ | </ | ||
+ | | ||
+ | 이것은 잘 작동하지만 모든 테스트를 스위트에 수동으로 추가해야 합니다. | ||
+ | JUnit의 얼리 어답터들은 이것이 어리석다고 말했습니다. | ||
+ | 새로운 테스트 케이스를 작성할 때마다 이를 static suite() 메서드에 추가해야합니다. | ||
+ | 그렇지 않으면 실행되지 않습니다. | ||
+ | 테스트 케이스 클래스를 인수로 취하는 편리한 생성자를 TestSuite에 추가했습니다. | ||
+ | 그 목적은 테스트 메서드를 추출하고 이를 포함하는 제품군을 만드는 것입니다. | ||
+ | 테스트 메서드는 접두사 " | ||
+ | 편의 생성자는 이 규칙을 사용하여 테스트 방법을 찾기 위해 reflection을 사용하여 테스트 객체를 구성합니다. | ||
+ | 이 생성자를 사용하면 위의 코드가 다음과 같이 단순화됩니다. | ||
- | 이것은 잘 작동하지만 모든 테스트를 스위트에 수동으로 추가해야합니다. JUnit의 얼리 어답터들은 이것이 어리 석다고 말했습니다. 새로운 테스트 케이스를 작성할 때마다이를 static suite () 메소드에 추가해야합니다. 그렇지 않으면 실행되지 않습니다. 테스트 케이스 클래스를 인수로 취하는 편리한 생성자를 TestSuite에 추가했습니다. 그 목적은 테스트 메서드를 추출하고이를 포함하는 제품군을 만드는 것입니다. 테스트 메서드는 접두사 " | + | <code java> |
- | public static Test suite () { | + | public static Test suite () |
+ | { | ||
return new TestSuite (MoneyTest.class); | return new TestSuite (MoneyTest.class); | ||
} | } | ||
- | The original way is still useful when you want to run a subset of the test cases only. | + | </ |
- | 3.6 Summary | + | 원래 방법은 테스트 케이스의 하위 집합을 실행하려는 경우에도 유용합니다. |
+ | | ||
+ | 3.6 요약 | ||
+ | JUnit A Cook's Tour는 끝이 났습니다. | ||
+ | 다음 그림은 패턴으로 설명된 JUnit의 디자인을 한 눈에 보여줍니다. | ||
- | We are at the end of our cook’s tour through JUnit. The following figure shows the design of JUnit at a glance explained with patterns. | + | {{: |
+ | 프레임워크의 추상화 중심인 TestCase가 4가지의 디자인 패턴과 어떻게 관련되어 있는지 확인하십시오 | ||
+ | 성숙한 객체 디자인의 그림은 이와 동일한 " | ||
+ | 디자인의 스타는 지원하는 플레이어와 풍부한 관계를 맺고 있습니다. | ||
+ | | ||
+ | 다음은 JUnit의 모든 패턴을 보는 또 다른 방법입니다. | ||
+ | 이 스토리보드에서는 각 패턴의 효과에 대한 추상적인 표현을 차례로 볼 수 있습니다. | ||
+ | 따라서 Command 패턴은 TestCase 클래스를 만들고 Templage Method 패턴은 run 메서드를 만드는 식입니다. | ||
+ | (스토리보드의 표기법은 모든 텍스트가 삭제된 그림 6의 표기법입니다.) | ||
- | + | {{: | |
- | Figure 6: JUnit Patterns Summary | + | |
- | + | 스토리보드에서 주목해야 할 점은 | |
- | Notice how TestCase, the central abstraction in the framework, is involved in four patterns. Pictures of mature object designs show this same " | + | 이것은 |
- | + | 따라서 주의해서 사용해야 합니다. | |
- | Here is another way of looking at all of the patterns in JUnit. In this storyboard you see an abstract representation of the effect of each of the patterns in turn. So, the Command pattern creates the TestCase class, the Template Method pattern creates the run method, and so on. (The notation of the storyboard is the notation of figure 6 with all the text deleted). | + | |
- | + | ||
- | + | ||
- | + | ||
- | Figure 7: JUnit Pattern Storyboard | + | |
- | | + | |
4. 결론 | 4. 결론 | ||
- | To conclude, let’s make some general observations: | + | 결론을 내리기 위해 몇 가지 일반적인 관찰을 해보겠습니다. |
Patterns | Patterns | ||
- | | + | |
- | | + | 패턴 측면에서 디자인을 논의하는 것이 매우 중요하다는 것을 알았습니다. |
- | Pattern density | + | |
+ | | ||
+ | |||
+ | Pattern density | ||
+ | JUnit의 핵심 추상화인 TestCase 주변에는 높은 패턴 " | ||
+ | 패턴 밀도가 높은 디자인은 사용하기 쉽지만 변경하기가 더 어렵습니다. | ||
+ | 우리는 키 추상화 주변의 높은 패턴 밀도가 성숙한 프레임 워크에서 일반적이라는 것을 발견했습니다. | ||
+ | 미성숙한 프레임워크는 그 반대입니다. | ||
+ | 패턴 밀도가 낮아야 합니다. | ||
+ | 실제로 해결하고 있는 문제를 발견하면 솔루션을 " | ||
| | ||
- | There is a high pattern " | + | Eat your own dog food (내부에서 사용) |
- | | + | 기본 단위 테스트 기능을 구현하자마자 직접 적용했습니다. |
- | | + | TestTest는 프레임워크가 오류, 성공 및 실패에 대한 올바른 결과를 보고하는지 확인합니다. |
- | | + | 우리는 프레임워크의 디자인을 계속 발전시켰을 때 이것이 매우 중요하다는 것을 알게 되었습니다. |
+ | 우리는 | ||
+ | |||
+ | Intersection, | ||
+ | 프레임워크 개발에는 가능한 모든 기능을 포함하려는 유혹이 있습니다. | ||
+ | | ||
+ | 그러나 이에 대응하는 힘이 있습니다. 개발자는 프레임워크를 사용하기로 해야 합니다. | ||
+ | | ||
+ | JUnit는 이 스타일로 작성되었습니다. 테스트 실행에 절대적으로 필요한 기능 (테스트 모음 실행, 서로 테스트 실행 격리, 테스트 자동 실행)만 구현합니다. | ||
+ | | ||
+ | 이 패키지의 주목할만한 멤버는 테스트 전후에 추가 코드를 실행할 수 있는 TestDecorator입니다. | ||
+ | |||
+ | 프레임워크 작성자는 코드를 읽습니다. | ||
+ | 우리는 JUnit 코드를 작성하는 것보다 훨씬 더 많은 시간을 소비했고, | ||
+ | 새로운 기능을 추가하는 데 소비한 것만큼 중복 기능을 제거하는 데 거의 시간을 소비했습니다. | ||
+ | 우리는 우리가 상상할 수 있는 한 다양한 방식으로 새로운 클래스를 추가하고 책임을 이동하면서 디자인을 적극적으로 실험했습니다. | ||
+ | 우리는 JUnit, 테스트, 객체 디자인, 프레임워크 개발 및 추가 기사에 대한 기회에 대한 지속적인 인사이트 흐름을 통해 우리의 기사에 대해 보상을 받았습니다 | ||
+ | (그리고 여전히 보상을 받고 있습니다). | ||
- | As soon as we had the base unit testing functionality implemented, | + | 최신 버전의 JUnit는 http://www.junit.org에서 다운로드할 수 있습니다. |
- | this invaluable as we continued to evolve the design of the framework. We found that the most challenging application of JUnit was testing its own behavior. | + | |
- | Intersection, | + | |
- | There is a temptation in framework development to include every feature you can. After all, you want to make the framework as valuable as possible. However, there is a counteracting force- developers have to | + | 5. 감사의 말 |
- | | + | |
- | | + | |
- | | + | 원문: http:// |
- | Framework writers read their code | + | |
- | We spent far more time reading the JUnit code than we spent writing it, and nearly as much time removing duplicate functionality as we spent adding new functionality. We experimented aggressively with the | ||
- | design, adding new classes and moving responsibility around in as many different ways as we could imagine. We were rewarded (and are still being rewarded) for our monomania by a continuous flow of insights | ||
- | into JUnit, testing, object design, framework development, | ||
- | The latest version of JUnit can be downloaded from http:// | ||
- | 5. Acknowledgements | ||
- | Thanks to John Vlissides, Ralph Johnson, and Nick Edgar for careful reading and gentle correction. | ||
+ | {{tag> |