JUnit으로 Java에서 추상 클래스를 테스트하는 방법은 무엇입니까?


87

JUnit으로 Java 테스트를 처음 사용합니다. Java로 작업해야하고 단위 테스트를 사용하고 싶습니다.

내 문제는 : 일부 추상 메서드가있는 추상 클래스가 있습니다. 그러나 추상적이지 않은 방법이 있습니다. JUnit으로이 클래스를 어떻게 테스트 할 수 있습니까? 예제 코드 (매우 간단 함) :

abstract class Car {

    public Car(int speed, int fuel) {
        this.speed = speed;
        this.fuel = fuel;
    }

    private int speed;
    private int fuel;

    abstract void drive();

    public int getSpeed() {
        return this.speed;
    }

    public int getFuel() {
        return this.fuel;
    }
}

테스트 getSpeed()하고 getFuel()기능 하고 싶습니다 .

이 문제에 대한 비슷한 질문이 여기 에 있지만 JUnit을 사용하지 않습니다.

JUnit FAQ 섹션 에서이 링크를 찾았 지만 저자가이 예제에서 무엇을 말하고 싶어하는지 이해하지 못합니다. 이 코드 줄은 무엇을 의미합니까?

public abstract Source getSource() ;

4
Mockito를 사용하는 두 가지 솔루션 은 stackoverflow.com/questions/1087339/… 를 참조하십시오 .
ddso 2011 년

테스트를위한 다른 프레임 워크를 배우면 어떤 이점이 있습니까? Mockito는 jUnit의 확장입니까, 아니면 완전히 다른 프로젝트입니까?
vasco

Mockito는 JUnit을 대체하지 않습니다. 다른 모의 프레임 워크와 마찬가지로 단위 테스트 프레임 워크와 함께 사용되며 테스트 케이스에서 사용할 모의 객체를 만드는 데 도움이됩니다.
ddso

답변:


104

클래스의 구체적인 구현이없고 메서드가 static테스트의 요점 이 아니라면 ? 구체적인 클래스가있는 경우 해당 메서드를 구체적인 클래스의 공용 API의 일부로 테스트하게됩니다.

나는 당신이 "내가 추상 클래스를 만든 이유 때문에이 메소드들을 계속해서 테스트하고 싶지 않다"라고 생각하는 것을 알고 있지만, 이에 대한 내 반론은 단위 테스트의 요점이 개발자가 변경할 수 있도록하는 것입니다. 테스트를 실행하고 결과를 분석합니다. 이러한 변화의 일부는 추상 클래스의 메소드를 오버라이드 (override) 포함, 모두 수 protectedpublic근본적인 행동 변화를 초래할 수있는. 이러한 변경의 특성에 따라 응용 프로그램이 예상치 못한 방식으로 실행되는 방식에 영향을 미칠 수 있습니다. 좋은 단위 테스트 스위트가 있다면 이러한 유형 변경으로 인해 발생하는 문제는 개발시 명백해야합니다.


17
100 % 코드 커버리지는 신화입니다. 애플리케이션이 어떻게 작동해야하는지에 대한 모든 알려진 가설을 포함 할 수있는 충분한 테스트가 있어야합니다 (코드를 작성하기 전에 테스트 주도 개발로 작성하는 것이 좋습니다). 저는 현재 매우 기능이 뛰어난 TDD 팀에서 일하고 있으며 우리가 개발하면서 작성된 마지막 빌드 당시에는 63 %의 커버리지 만 보유하고 있습니다. 그거 좋아요? 누가 알겠습니까?하지만 돌아가서 더 높이 올리는 것은 시간 낭비라고 생각합니다.
nsfyn55 2011 년

3
확실한. 어떤 사람들은 그것이 좋은 TDD의 위반이라고 주장 할 것입니다. 당신이 팀에 있다고 상상해보십시오. 메서드가 최종적이라고 가정하고 구체적인 구현에서 테스트를하지 않습니다. 누군가 수정자를 제거하고 상속 계층 구조의 전체 분기에 영향을주는 변경을 수행합니다. 테스트 스위트가이를 포착하기를 원하지 않습니까?
nsfyn55 2013-10-05

31
동의하지 않습니다. TDD에서 작업하든 안하든 추상 클래스의 구체적인 메서드에는 코드가 포함되어 있으므로 테스트가 있어야합니다 (하위 클래스가 있는지 여부에 관계없이). 또한 Java의 단위 테스트는 일반적으로 클래스를 테스트합니다. 따라서 클래스의 일부가 아니라 수퍼 클래스의 메서드를 테스트하는 데는 논리가 없습니다. 그 논리에 따라 우리는 하위 클래스가 전혀없는 클래스를 제외하고 Java에서 어떤 클래스도 테스트해서는 안됩니다. 재정의되는 메서드와 관련하여 테스트를 추가하여 하위 클래스의 테스트에 대한 변경 / 추가 사항을 확인하는 것은 정확히 바로 그 때입니다.
ethanfar

3
@ nsfyn55 구체적인 방법이 있다면 final어떨까요? 구현이 있다면 나는 같은 방법으로 여러 번 테스트하기위한 이유를 볼 수 없습니다 변경
다이옥신

3
모든 구현에 대해 테스트를 실행할 수 있도록 추상 인터페이스를 대상으로하는 테스트가 있어야하지 않습니까? 가능하지 않다면 우리는 Liskov를 위반하는 것입니다. 우리가 알고 고치고 싶을 것입니다. 구현이 일부 확장 (호환) 기능을 추가하는 경우 에만 해당 기능에 대한 특정 단위 테스트가 있어야합니다.
tne

36

추상 클래스를 상속하는 구체적인 클래스를 만든 다음 해당 추상 클래스에서 상속되는 함수를 테스트합니다.


추상 클래스를 확장하는 10 개의 구체적인 클래스가 있고 이러한 각 구체적인 클래스는 하나의 메서드 만 구현하고 다른 두 메서드는 추상에서 구현되기 때문에 이러한 클래스 각각에 대해 동일하다고 가정 해 봅시다. 수업? 내 경우는 추상 클래스에 대한 테스트를 각 하위 클래스에 복사하여 붙여넣고 싶지 않다는 것입니다.
scarface

12

게시 한 예제 클래스를 사용하면 테스트하는 것이별로 의미가없는 것 같 getFuel()으며 getSpeed()0 (세터가 없음) 만 반환 할 수 있기 때문입니다.

그러나 이것이 설명을위한 단순한 예제 일 뿐이고 추상 기본 클래스에서 메서드를 테스트 할 합법적 인 이유가 있다고 가정하면 (다른 사람들은 이미 의미를 지적 했음) 익명을 생성하도록 테스트 코드를 설정할 수 있습니다. 추상 메서드에 대한 더미 (no-op) 구현 만 제공하는 기본 클래스의 하위 클래스입니다.

예를 들어 다음과 같이 TestCase할 수 있습니다.

c = new Car() {
       void drive() { };
   };

그런 다음 나머지 방법을 테스트합니다. 예 :

public class CarTest extends TestCase
{
    private Car c;

    public void setUp()
    {
        c = new Car() {
            void drive() { };
        };
    }

    public void testGetFuel() 
    {
        assertEquals(c.getFuel(), 0);
    }

    [...]
}

(이 예제는 JUnit3 구문을 기반으로합니다. JUnit4의 경우 코드가 약간 다르지만 아이디어는 동일합니다.)


대답 해줘서 고마워요. 예, 제 예는 단순화되었습니다 (그다지 좋지는 않습니다). 여기에서 모든 답변을 읽은 후 더미 클래스를 작성했습니다. 그러나 그의 답변에서 @ nsfyn55를 썼 듯이이 추상 클래스의 모든 자손에 대한 테스트를 작성합니다.
vasco 2011 년

9

어쨌든 솔루션이 필요한 경우 (예 : 추상 클래스의 구현이 너무 많고 테스트가 항상 동일한 절차를 반복하기 때문에) 해당 구현에 의해 실행되는 추상 팩토리 메서드로 추상 테스트 클래스를 만들 수 있습니다. 테스트 클래스. 이 예제는 TestNG와 함께 작동합니다.

의 추상 테스트 클래스 Car:

abstract class CarTest {

// the factory method
abstract Car createCar(int speed, int fuel);

// all test methods need to make use of the factory method to create the instance of a car
@Test
public void testGetSpeed() {
    Car car = createCar(33, 44);
    assertEquals(car.getSpeed(), 33);
    ...

구현 Car

class ElectricCar extends Car {

    private final int batteryCapacity;

    public ElectricCar(int speed, int fuel, int batteryCapacity) {
        super(speed, fuel);
        this.batteryCapacity = batteryCapacity;
    }

    ...

클래스 ElectricCarTest의 단위 테스트 클래스 ElectricCar:

class ElectricCarTest extends CarTest {

    // implementation of the abstract factory method
    Car createCar(int speed, int fuel) {
        return new ElectricCar(speed, fuel, 0);
    }

    // here you cann add specific test methods
    ...

5

이런 식으로 할 수 있습니다

public abstract MyAbstractClass {

    @Autowire
    private MyMock myMock;        

    protected String sayHello() {
            return myMock.getHello() + ", " + getName();
    }

    public abstract String getName();
}

// this is your JUnit test
public class MyAbstractClassTest extends MyAbstractClass {

    @Mock
    private MyMock myMock;

    @InjectMocks
    private MyAbstractClass thiz = this;

    private String myName = null;

    @Override
    public String getName() {
        return myName;
    }

    @Test
    public void testSayHello() {
        myName = "Johnny"
        when(myMock.getHello()).thenReturn("Hello");
        String result = sayHello();
        assertEquals("Hello, Johnny", result);
    }
}

4

추상 클래스에서 상속하는 jUnit 내부 클래스를 만듭니다. 이것은 인스턴스화 될 수 있으며 추상 클래스에 정의 된 모든 메소드에 액세스 할 수 있습니다.

public class AbstractClassTest {
   public void testMethod() {
   ...
   }
}


class ConcreteClass extends AbstractClass {

}

3
이것은 훌륭한 조언입니다. 하지만 예제를 제공하면 개선 될 수 있습니다. 설명하고있는 클래스의 예일 것입니다.
SDJMcHattie

2

익명 클래스를 인스턴스화 한 다음 해당 클래스를 테스트 할 수 있습니다.

public class ClassUnderTest_Test {

    private ClassUnderTest classUnderTest;

    private MyDependencyService myDependencyService;

    @Before
    public void setUp() throws Exception {
        this.myDependencyService = new MyDependencyService();
        this.classUnderTest = getInstance();
    }

    private ClassUnderTest getInstance() {
        return new ClassUnderTest() {    
            private ClassUnderTest init(
                    MyDependencyService myDependencyService
            ) {
                this.myDependencyService = myDependencyService;
                return this;
            }

            @Override
            protected void myMethodToTest() {
                return super.myMethodToTest();
            }
        }.init(myDependencyService);
    }
}

가시성은 추상 클래스 protected의 속성 myDependencyService에 대한 것이 어야합니다 ClassUnderTest.

이 접근 방식을 Mockito와 깔끔하게 결합 할 수도 있습니다. 를 참조하십시오 여기 .


2

이것을 테스트하는 내 방법은 각 abstractUnitTest.java. 추상 클래스를 확장하는 abstractUnitTest.java에 클래스를 간단히 만듭니다. 그리고 그렇게 테스트하십시오.


0

전체 추상 클래스를 테스트 할 수 없습니다. 이 경우 추상 메서드가 있습니다. 이는 주어진 추상 클래스를 확장하는 클래스에 의해 구현되어야 함을 의미합니다.

그 클래스에서 프로그래머는 자신의 로직 전용 소스 코드를 작성해야합니다.

즉, 추상 클래스의 최종 동작을 확인할 수 없기 때문에 테스트하는 감각이 없습니다.

일부 추상 클래스의 추상 메서드와 관련되지 않은 주요 기능이있는 경우 추상 메서드가 일부 예외를 throw하는 다른 클래스를 만듭니다.


0

옵션으로 추상 클래스 내부의 논리를 포함하는 추상 테스트 클래스를 만들고 각 하위 클래스 테스트에 대해 확장 할 수 있습니다. 이렇게하면이 논리가 각 하위 항목에 대해 개별적으로 테스트되도록 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.