¿Cómo probar la clase abstracta en Java con JUnit?

Soy nuevo en las pruebas de Java con JUnit. Tengo que trabajar con Java y me gustaría usar pruebas unitarias.

Mi problema es: tengo una clase abstracta con algunos métodos abstractos. Pero hay algunos métodos que no son abstractos. ¿Cómo puedo probar esta clase con JUnit? Código de ejemplo (muy simple):

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

Quiero probar las getSpeed() y getFuel() .

Hay una pregunta similar a este problema aquí , pero no está usando JUnit.

En la sección de preguntas frecuentes de JUnit, encontré este enlace , pero no entiendo lo que el autor quiere decir con este ejemplo. ¿Qué significa esta línea de código?

 public abstract Source getSource() ; 

Si no tiene implementaciones concretas de la clase y los métodos no son static ¿de qué sirve probarlos? Si tienes una clase concreta, probarás esos métodos como parte de la API pública de la clase concreta.

Sé lo que estás pensando “No quiero probar estos métodos una y otra vez, esa es la razón por la que creé la clase abstracta”, pero mi argumento en contra es que el objective de las pruebas unitarias es permitir a los desarrolladores realizar cambios, ejecutar las pruebas y analizar los resultados. Parte de esos cambios podría incluir la anulación de los métodos de la clase abstracta, tanto los protected como los public , lo que podría generar cambios de comportamiento fundamentales. Dependiendo de la naturaleza de esos cambios, podría afectar la forma en que su aplicación se ejecuta de forma inesperada, posiblemente negativa. Si tiene un buen conjunto de pruebas de la unidad, los problemas derivados de estos tipos deben ser evidentes en el momento del desarrollo.

Cree una clase concreta que herede la clase abstracta y luego pruebe las funciones que hereda la clase concreta de la clase abstracta.

Con la clase de ejemplo que publicaste no parece tener mucho sentido probar getFuel() y getSpeed() ya que solo pueden devolver 0 (no hay definidores).

Sin embargo, suponiendo que este fuera solo un ejemplo simplificado con fines ilustrativos, y que tiene motivos legítimos para probar métodos en la clase base abstracta (otros ya han señalado las implicaciones), puede configurar su código de prueba para que cree un anónimo subclase de la clase base que solo proporciona implementaciones ficticias (no operativas) para los métodos abstractos.

Por ejemplo, en tu TestCase puedes hacer esto:

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

Luego prueba el rest de los métodos, por ejemplo:

 public class CarTest extends TestCase { private Car c; public void setUp() { c = new Car() { void drive() { }; }; } public void testGetFuel() { assertEquals(c.getFuel(), 0); } [...] } 

(Este ejemplo se basa en la syntax JUnit3. Para JUnit4, el código sería ligeramente diferente, pero la idea es la misma).

Si necesita una solución de todos modos (por ejemplo, porque tiene demasiadas implementaciones de la clase abstracta y la prueba siempre repetiría los mismos procedimientos), entonces podría crear una clase de prueba abstracta con un método abstracto de fábrica que se ejecutará mediante la implementación de esa clase. clase de prueba Este ejemplo funciona o me funciona con TestNG:

La clase de prueba abstracta de 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); ... 

Implementación del Car

 class ElectricCar extends Car { private final int batteryCapacity; public ElectricCar(int speed, int fuel, int batteryCapacity) { super(speed, fuel); this.batteryCapacity = batteryCapacity; } ... 

Prueba de unidad clase ElectricCarTest de la clase 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 ... 

Podrías hacer algo como esto

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

Puede instanciar una clase anónima y luego probar esa clase.

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

Tenga en cuenta que la visibilidad debe estar protected para la propiedad myDependencyService de la clase abstracta ClassUnderTest .

También puedes combinar este enfoque cuidadosamente con Mockito. Mira aquí .

Crearía una subclase jUnit que hereda de la clase abstracta. Esto puede crearse una instancia y tener acceso a todos los métodos definidos en la clase abstracta.

No puedes probar la clase abstracta completa. En este caso, tienes métodos abstractos, esto significa que deben implementarse por clase que extienda una clase abstracta dada.

En esa clase el progtwigdor tiene que escribir el código fuente que está dedicado a la lógica de él.

En otras palabras, no hay ningún sentido de probar la clase abstracta porque no puedes verificar el comportamiento final de la misma.

Si tiene una funcionalidad importante no relacionada con los métodos abstractos en alguna clase abstracta, simplemente cree otra clase donde el método abstracto arrojará alguna excepción.

Como opción, puede crear una clase de prueba abstracta que cubra la lógica dentro de la clase abstracta y ampliarla para cada prueba de subclase. Para que de esta manera pueda garantizar que esta lógica se probará para cada niño por separado.

Mi forma de probar esto es bastante simple, dentro de cada abstractUnitTest.java . Simplemente creo una clase en abstractUnitTest.java que extiende la clase abstracta. Y pruébalo de esa manera.