Je suis nouveau dans Java tester avec JUnit. Je dois travailler avec Java et j'aimerais utiliser des tests unitaires.
Mon problème est: j'ai une classe abstraite avec quelques méthodes abstraites. Mais il existe des méthodes qui ne sont pas abstraites. Comment puis-je tester cette classe avec JUnit? Exemple de code (très 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;
}
}
Je veux tester les fonctions getSpeed()
et getFuel()
.
Une question similaire à ce problème est ici , mais il n’utilise pas JUnit.
Dans JUnit FAQ section, j'ai trouvé ce lien , mais je ne comprends pas ce que l'auteur veut dire avec cet exemple. Que signifie cette ligne de code?
public abstract Source getSource() ;
Si vous n'avez pas d'implémentations concrètes de la classe et que les méthodes ne sont pas static
, à quoi servent-elles de les tester? Si vous avez une classe concrète, vous testerez ces méthodes dans le cadre de l'API publique de la classe concrète.
Je sais ce que vous pensez "Je ne veux pas tester ces méthodes à maintes reprises et c'est la raison pour laquelle j'ai créé la classe abstraite", mais mon contre-argument est que le but des tests unitaires est de permettre aux développeurs d'apporter des modifications, lancez les tests et analysez les résultats. Une partie de ces changements pourrait inclure le remplacement des méthodes de votre classe abstraite, à la fois protected
et public
, ce qui pourrait entraîner des changements de comportement fondamentaux. En fonction de la nature de ces modifications, cela pourrait affecter le fonctionnement de votre application de manière inattendue, voire négative. Si vous avez une bonne suite de tests unitaires, les problèmes découlant de ces types doivent être évidents au moment du développement.
Créez une classe concrète qui hérite de la classe abstraite, puis testez les fonctions de la classe concrète héritées de la classe abstraite.
Avec l'exemple de classe que vous avez posté, tester getFuel()
et getSpeed()
n'a pas beaucoup de sens car ils ne peuvent que renvoyer 0 (il n'y a pas de setters).
Cependant, en supposant qu'il ne s'agit que d'un exemple simplifié à des fins d'illustration et que vous avez des raisons légitimes de tester des méthodes dans la classe de base abstraite (d'autres ont déjà souligné les implications), vous pouvez configurer votre code de test de manière à créer une valeur anonyme. sous-classe de la classe de base qui fournit simplement des implémentations factices (no-op) pour les méthodes abstraites.
Par exemple, dans votre TestCase
, vous pouvez faire ceci:
c = new Car() {
void drive() { };
};
Puis testez le reste des méthodes, par exemple:
public class CarTest extends TestCase
{
private Car c;
public void setUp()
{
c = new Car() {
void drive() { };
};
}
public void testGetFuel()
{
assertEquals(c.getFuel(), 0);
}
[...]
}
(Cet exemple est basé sur la syntaxe JUnit3. Pour JUnit4, le code serait légèrement différent, mais l'idée est la même.)
Si vous avez quand même besoin d’une solution (par exemple parce que vous avez trop d’implémentations de la classe abstraite et que le test répète toujours les mêmes procédures), vous pouvez créer une classe de test abstraite avec une méthode de fabrique abstraite qui sera remplacée par cette implémentation. classe de test. Cet exemple fonctionne ou moi avec TestNG:
La classe de test abstraite 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);
...
implémentation de Car
class ElectricCar extends Car {
private final int batteryCapacity;
public ElectricCar(int speed, int fuel, int batteryCapacity) {
super(speed, fuel);
this.batteryCapacity = batteryCapacity;
}
...
Classe de test unitaire ElectricCarTest
de la classe 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
...
Tu pourrais faire quelque chose comme ça
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);
}
}
Je créerais une classe interne jUnit qui hérite de la classe abstraite. Cela peut être instancié et avoir accès à toutes les méthodes définies dans la classe abstraite.
public class AbstractClassTest {
public void testMethod() {
...
}
}
class ConcreteClass extends AbstractClass {
}
Vous pouvez instancier une classe anonyme, puis tester cette classe.
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);
}
}
N'oubliez pas que la visibilité doit être protected
pour la propriété myDependencyService
de la classe abstraite ClassUnderTest
.
Vous pouvez également combiner parfaitement cette approche avec Mockito. Voir ici .
Ma façon de tester cela est assez simple, au sein de chaque abstractUnitTest.Java
. Je crée simplement une classe dans le abstractUnitTest.Java qui étend la classe abstraite. Et testez-le de cette façon.
Vous ne pouvez pas tester toute la classe abstraite. Dans ce cas, vous avez des méthodes abstraites, cela signifie qu'elles doivent être implémentées par classe qui étend une classe abstraite donnée.
Dans cette classe, le programmeur doit écrire le code source dédié à sa logique.
En d'autres termes, il n'y a pas de sens à tester une classe abstraite car vous n'êtes pas en mesure de vérifier son comportement final.
Si vous avez une fonctionnalité majeure non liée aux méthodes abstraites dans une classe abstraite, créez simplement une autre classe où la méthode abstraite lève une exception.
En option, vous pouvez créer une classe de test abstraite couvrant la logique à l'intérieur de la classe abstraite et l'étendre pour chaque test de sous-classe. Vous pourrez ainsi vérifier que cette logique sera testée séparément pour chaque enfant.