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

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() ;



답변

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

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


답변

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


답변

게시 한 예제 클래스를 사용하면 테스트하는 것이별로 의미가없는 것 같 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의 경우 코드가 약간 다르지만 아이디어는 동일합니다.)


답변

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


답변

이런 식으로 할 수 있습니다

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);
    }
}


답변

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

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


class ConcreteClass extends AbstractClass {

}


답변

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

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와 깔끔하게 결합 할 수도 있습니다. 를 참조하십시오 여기 .