Подтвердить что ты не робот

Как проверить абстрактный класс на Java с помощью JUnit?

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

public abstract Source getSource() ;
4b9b3361

Ответ 1

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

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

Ответ 2

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

Ответ 3

С классом класса, который вы опубликовали, кажется, не имеет смысла тестировать 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 код будет немного отличаться, но идея одинаков.)

Ответ 4

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

    ...

Unit test класс 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);
    }
}

Ответ 6

Используйте насмешливость. Например, проверьте Mockito.

Ответ 7

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

Ответ 8

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

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

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

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

Ответ 9

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

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