Java 프로젝트를 진행 중입니다. 단위 테스트를 처음 사용합니다. Java 클래스에서 개인 메소드를 단위 테스트하는 가장 좋은 방법은 무엇입니까?
Java 프로젝트를 진행 중입니다. 단위 테스트를 처음 사용합니다. Java 클래스에서 개인 메소드를 단위 테스트하는 가장 좋은 방법은 무엇입니까?
답변:
일반적으로 개인 메소드를 직접 단위 테스트하지 않습니다. 그것들은 비공개이므로 구현 세부 사항을 고려하십시오. 아무도 그들 중 하나를 부르지 않고 특정 방식으로 작동 할 것으로 기대하지 않습니다.
대신 공용 인터페이스를 테스트해야합니다. 개인 메소드를 호출하는 메소드가 예상대로 작동하는 경우 확장하여 개인 메소드가 올바르게 작동한다고 가정합니다.
일반적으로 나는 그것을 피할 것입니다. 개인 메소드가 너무 복잡하여 별도의 단위 테스트가 필요한 경우 자체 클래스가 필요하다는 의미입니다. 재사용 가능한 방식으로 작성하도록 권장 할 수 있습니다. 그런 다음 새 클래스를 테스트하고 이전 클래스에서 해당 클래스의 공용 인터페이스를 호출해야합니다.
반면에 구현 세부 사항을 별도의 클래스로 분류하면 복잡한 인터페이스가있는 클래스, 이전 클래스와 새 클래스 사이에 많은 데이터가 전달되는 클래스 또는 OOP 관점에서 잘 보일 수있는 디자인으로 이어지지 만 때로는 그렇지 않습니다. 문제 영역에서 오는 직관과 일치하십시오 (예 : 개인 메소드 테스트를 피하기 위해 가격 책정 모델을 두 조각으로 나누는 것은 매우 직관적이지 않으며 나중에 코드를 유지 관리 / 연장 할 때 문제가 발생할 수 있음). 항상 함께 바뀌는 "트윈 클래스"를 원하지 않습니다.
캡슐화와 테스트 가능성 사이의 선택에 직면했을 때, 나는 두 번째로 가고 싶습니다. 적절하게 테스트되지 않았기 때문에 올바르게 작동하지 않는 멋진 OOP 디자인보다 올바른 코드 (즉, 올바른 출력 생성)를 갖는 것이 더 중요합니다. Java에서는 메소드에 "기본"액세스 권한을 부여하고 단위 테스트를 동일한 패키지에 넣을 수 있습니다. 단위 테스트는 개발중인 패키지의 일부일 뿐이므로 테스트와 테스트중인 코드간에 종속성이있는 것이 좋습니다. 구현을 변경할 때 테스트를 변경해야 할 수도 있지만, 구현마다 변경 될 때마다 코드를 다시 테스트해야하며 테스트를 수정하여 수정해야하는 경우에는 그것.
일반적으로 클래스는 둘 이상의 인터페이스를 제공 할 수 있습니다. 사용자를위한 인터페이스와 관리자를위한 인터페이스가 있습니다. 두 번째 코드는 코드가 적절히 테스트되도록 더 많이 노출시킬 수 있습니다. 개인 메서드에 대한 단위 테스트 일 필요는 없습니다 (예 : 로깅). 로깅도 "캡슐화를 중단"하지만 여전히 유용하기 때문에 여전히 수행합니다.
프라이빗 메소드 테스트는 복잡성에 따라 다릅니다. 일부 한 줄의 개인 메서드는 테스트의 추가 노력을 실제로 보증하지는 않지만 (공개 메서드라고도 할 수 있음) 일부 개인 메서드는 공용 메서드와 마찬가지로 복잡하고 공용 인터페이스를 통해 테스트하기가 어려울 수 있습니다.
내가 선호하는 기술은 개인 메소드 패키지를 비공개로 만드는 것인데, 동일한 패키지에서 단위 테스트에 액세스 할 수는 있지만 여전히 다른 모든 코드에서 캡슐화됩니다. 이것은 복잡한 논리의 모든 부분을 다루기 위해 공개 메소드 테스트에 의존하지 않고 개인 메소드 논리를 직접 테스트하는 이점을 제공합니다.
이것이 Google Guava 라이브러리의 @VisibleForTesting 주석과 쌍을 이루는 경우,이 패키지 전용 메소드를 테스트 용으로 만 표시되도록 명시하고 있으므로 다른 클래스에서 호출해서는 안됩니다.
이 기술의 반대자들은 이것이 캡슐화를 깨뜨리고 개인 패키지를 열어서 동일한 패키지로 코딩 할 것이라고 주장합니다. 나는 이것이 캡슐화를 깨고 다른 클래스에 개인 코드를 공개한다는 것에 동의하지만, 복잡한 논리 테스트는 엄격한 캡슐화 보다 중요 하며 테스트를 위해 눈에 잘 띄는 패키지 전용 메소드를 사용하지 않는 것은 개발자의 책임이어야 함을 주장합니다 코드베이스 사용 및 변경
테스트 전 개인 방법 :
private int add(int a, int b){
return a + b;
}
테스트 준비가 된 패키지 전용 메소드 :
@VisibleForTesting
int add(int a, int b){
return a + b;
}
참고 : 테스트를 동일한 패키지에 넣는 것은 동일한 실제 폴더에 넣는 것과 다릅니다. 기본 코드와 테스트 코드를 별도의 실제 폴더 구조로 분리하는 것이 일반적으로 좋은 방법이지만이 방법은 클래스가 동일한 패키지에 정의되어있는 한 작동합니다.
외부 API를 사용할 수 없거나 원하지 않는 경우 순수한 표준 JDK API를 사용하여 리플렉션을 사용하여 개인 메소드에 액세스 할 수 있습니다. 여기에 예가 있습니다
MyObject obj = new MyObject();
Method privateMethod = MyObject.class.getDeclaredMethod("getFoo", null);
privateMethod.setAccessible(true);
String returnValue = (String) privateMethod.invoke(obj, null);
System.out.println("returnValue = " + returnValue);
Java 학습서 http://docs.oracle.com/javase/tutorial/reflect/ 또는 Java API http://docs.oracle.com/javase/7/docs/api/java/lang/reflect/package-summary를 확인 하십시오. 자세한 내용은 html 을 참조하십시오.
@kij가 자신의 답변을 인용 한 것처럼 리플렉션을 사용하는 간단한 솔루션이 실제로 개인 메서드를 테스트하는 것이 좋은 경우가 있습니다.
단위 테스트 사례는 코드 단위를 테스트하는 것을 의미합니다. 인터페이스를 테스트하는 경우 코드 단위를 테스트한다는 의미는 아니기 때문에 인터페이스를 테스트하는 것은 아닙니다. 일종의 블랙 박스 테스트가됩니다. 또한 인터페이스 수준에서 문제를 확인한 다음 작동하지 않는 부분을 디버깅하는 것보다 가장 작은 단위 수준에서 문제를 찾는 것이 좋습니다. 따라서 단위 테스트 케이스는 범위와 상관없이 테스트해야합니다. 다음은 개인용 메소드를 테스트하는 방법입니다.
java를 사용하는 경우 Deencapsulation.invoke를 제공하는 jmockit을 사용하여 테스트중인 클래스의 개인용 메소드를 호출 할 수 있습니다. 리플렉션을 사용하여 결국 호출하지만 멋진 래퍼를 제공합니다. ( https://code.google.com/p/jmockit/ )
우선, 다른 저자들이 제안한 바와 같이 : 개인 방법을 실제로 테스트해야한다면 두 번 생각하십시오. 그리고 그렇다면 ...
.NET에서는 "내부"방법으로 변환 할 수 있으며, 패키지 "할 InternalVisible을 당신의 단위 테스트 프로젝트에".
Java에서는 테스트 할 클래스에 테스트 자체를 작성할 수 있으며 테스트 메소드는 개인 메소드도 호출 할 수 있어야합니다. 실제로 Java 환경이 크지 않으므로 최상의 방법은 아닙니다.
감사.
나는 보통 그러한 방법들을 보호한다. 수업이 있다고 가정 해 봅시다.
src/main/java/you/Class.java
다음과 같이 테스트 클래스를 만들 수 있습니다.
src/test/java/you/TestClass.java
이제 보호 된 메소드에 액세스 할 수 있으며 단위 테스트를 수행 할 수 있지만 (JUnit 또는 TestNG는 중요하지 않음)이 메소드를 원하지 않는 호출자로부터 유지합니다.
이것은 maven 스타일의 소스 트리를 필요로합니다.
Java를 사용하여 개인 메소드를 테스트 해야하는 경우 fest assert
and 및 / 또는을 사용할 수 있습니다 fest reflect
. 반사를 사용합니다.
maven으로 라이브러리를 가져 오거나 (주어진 버전은 내가 생각하는 최신 버전이 아닙니다) 클래스 경로에서 직접 가져 오십시오.
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-assert</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>org.easytesting</groupId>
<artifactId>fest-reflect</artifactId>
<version>1.2</version>
</dependency>
예를 들어, 'myPrivateMethod'라는 개인 메소드가있는 'MyClass'라는 클래스가있는 경우 String을 매개 변수로 사용하여 값을 'this is cool testing!'로 업데이트하면 다음 junit 테스트를 수행 할 수 있습니다.
import static org.fest.reflect.core.Reflection.method;
...
MyClass objectToTest;
@Before
public void setUp(){
objectToTest = new MyClass();
}
@Test
public void testPrivateMethod(){
// GIVEN
String myOriginalString = "toto";
// WHEN
method("myPrivateMethod").withParameterTypes(String.class).in(objectToTest).invoke(myOriginalString);
// THEN
Assert.assertEquals("this is cool testing !", myOriginalString);
}
이 라이브러리를 사용하면 모든 빈 속성을 비공개로 설정하고 세터를 작성하지 않아도 Mockito 또는 다른 Mock Framework와 함께 사용할 수 있습니다. 현재 알아야 할 유일한 것은 (다음 버전에서 더 나을지 모르겠다) 조작하려는 대상 필드 / 메소드의 이름과 서명입니다.
C #에서 일반적으로하는 일은 내 메서드가 개인용이 아닌 보호되도록하는 것입니다. 약간 덜 개인 액세스 수정 자이지만 테스트중인 클래스에서 상속되지 않는 모든 클래스에서 메소드를 숨 깁니다.
public class classUnderTest
{
//this would normally be private
protected void methodToTest()
{
//some code here
}
}
classUnderTest에서 직접 상속되지 않은 클래스는 methodToTest가 존재한다는 것을 모릅니다. 내 테스트 코드 에서이 메소드에 대한 액세스를 제공하고 확장하는 특수 테스트 클래스를 만들 수 있습니다 ...
class TestingClass : classUnderTest
{
public void methodToTest()
{
//this returns the method i would like to test
return base.methodToTest();
}
}
이 수업은 테스트 프로젝트에만 있습니다. 유일한 목적은이 단일 방법에 대한 액세스를 제공하는 것입니다. 그것은 대부분의 다른 수업이없는 장소에 접근 할 수있게 해줍니다.
protected
는 공개 API의 일부이며 동일한 제한이 발생합니다 (변경 될 수없고 문서화되어야합니다 ...). C # internal
에서는와 함께 사용할 수 있습니다 InternalsVisibleTo
.
단위 테스트를 테스트중인 클래스의 내부 클래스에 배치하면 개인 메소드를 쉽게 테스트 할 수 있습니다. TestNG를 사용하면 유닛 테스트는 다음과 같이 @Test로 주석이 달린 공개 정적 내부 클래스 여야합니다.
@Test
public static class UserEditorTest {
public void test_private_method() {
assertEquals(new UserEditor().somePrivateMethod(), "success");
}
}
내부 클래스이므로 private 메서드를 호출 할 수 있습니다.
내 테스트는 maven에서 실행되며 자동으로 이러한 테스트 사례를 찾습니다. 수업 하나만 시험하고 싶다면 할 수 있습니다
$ mvn test -Dtest=*UserEditorTest
출처 : http://www.ninthavenue.com.au/how-to-unit-test-private-methods