Как проверить абстрактный класс на 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() ;
Ответы
Ответ 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. См. здесь.