Как протестировать абстрактный класс в Java с помощью JUnit? - PullRequest
78 голосов
/ 27 сентября 2011

Я новичок в тестировании Java с JUnit.Я должен работать с 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 я нашел эту ссылку , но я не понимаю, что автор хочет сказать в этом примере.Что означает эта строка кода?

public abstract Source getSource() ;

Ответы [ 10 ]

104 голосов
/ 27 сентября 2011

Если у вас нет конкретных реализаций класса, а методы не static, какой смысл их тестировать? Если у вас есть конкретный класс, вы будете тестировать эти методы как часть открытого API конкретного класса.

Я знаю, что вы думаете: «Я не хочу тестировать эти методы снова и снова, поэтому я создал абстрактный класс», но мой контраргумент состоит в том, что цель модульных тестов - позволить разработчикам вносите изменения, запускайте тесты и анализируйте результаты. Часть этих изменений может включать в себя переопределение методов вашего абстрактного класса, protected и public, что может привести к фундаментальным изменениям поведения. В зависимости от характера этих изменений это может повлиять на работу вашего приложения неожиданным, возможно, негативным образом. Если у вас есть хороший пакет модульного тестирования, проблемы, возникающие из-за изменений этих типов, должны быть очевидны во время разработки.

31 голосов
/ 27 сентября 2011

Создайте конкретный класс, который наследует абстрактный класс, а затем протестируйте функции, которые конкретный класс наследует от абстрактного класса.

12 голосов
/ 28 сентября 2011

В примере класса, который вы опубликовали, нет смысла тестировать getFuel() и getSpeed(), поскольку они могут возвращать только 0 (нет установщиков).

Однако, если предположить, чтоэто был просто упрощенный пример для иллюстративных целей, и что у вас есть законные основания для тестирования методов в абстрактном базовом классе (другие уже указали на последствия), вы можете настроить свой тестовый код так, чтобы он создавал анонимный подкласс базовогокласс, который просто предоставляет фиктивные (безоперационные) реализации для абстрактных методов.

Например, в вашем 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 код будет немного другим, но идея та же.)

8 голосов
/ 27 июля 2015

Если вам все равно нужно решение (например, из-за того, что у вас слишком много реализаций абстрактного класса, и тестирование всегда будет повторять одни и те же процедуры), вы можете создать класс абстрактного теста с методом абстрактной фабрики, который будет вызываться реализация этого тестового класса. Этот пример работает или у меня с 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 голосов
/ 09 апреля 2014

Вы могли бы сделать что-то вроде этого

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);
    }
}
3 голосов
/ 04 марта 2014

Я бы создал внутренний класс jUnit, который наследуется от абстрактного класса. Это может быть реализовано и иметь доступ ко всем методам, определенным в абстрактном классе.

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


class ConcreteClass extends AbstractClass {

}
2 голосов
/ 27 июля 2017

Вы можете создать экземпляр анонимного класса и затем протестировать этот класс.

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.Смотри здесь .

1 голос
/ 26 августа 2018

Мой способ тестирования довольно прост, в каждом abstractUnitTest.java.Я просто создаю класс в abstractUnitTest.java, который расширяет абстрактный класс.И проверить это таким образом.

0 голосов
/ 08 июня 2018

Как вариант, вы можете создать абстрактный тестовый класс, охватывающий логику внутри абстрактного класса, и расширить его для каждого теста подкласса.Таким образом, вы можете гарантировать, что эта логика будет проверена для каждого ребенка в отдельности.

0 голосов
/ 27 сентября 2011

Вы не можете протестировать целый абстрактный класс.В этом случае у вас есть абстрактные методы, это означает, что они должны быть реализованы классом, расширяющим данный абстрактный класс.

В этом классе программист должен написать исходный код, который предназначен для его логики.

Другими словами, нет смысла тестировать абстрактный класс, потому что вы не можете проверить его окончательное поведение.

Если у вас есть основные функциональные возможности, не связанные с абстрактными методами в каком-то абстрактном классе, просто создайте другой класс, в котором абстрактный метод вызовет некоторое исключение.

...