Jestem nowy w testowaniu Java z JUnit. Muszę pracować z Javą i chciałbym używać testów jednostkowych.
Mój problem jest taki: mam klasę abstrakcyjną z kilkoma metodami abstrakcyjnymi. Ale są metody, które nie są abstrakcyjne. Jak mogę przetestować tę klasę za pomocą JUnit? Przykładowy kod (bardzo prosty):
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;
}
}
Chcę przetestować getSpeed()
i getFuel()
funkcjonować.
Podobne pytanie do tego problemu jest tutaj , ale nie używa JUnit.
W dziale JUnit FAQ znalazłem ten link , ale nie rozumiem, co autor chce powiedzieć na tym przykładzie. Co oznacza ta linia kodu?
public abstract Source getSource() ;
java
junit
abstract-class
Vasco
źródło
źródło
Odpowiedzi:
Jeśli nie masz konkretnych implementacji tej klasy, a metody nie są,
static
jaki jest sens ich testowania? Jeśli masz konkretną klasę, będziesz testować te metody jako część publicznego interfejsu API konkretnej klasy.Wiem, o czym myślisz „Nie chcę ciągle testować tych metod, dlatego stworzyłem klasę abstrakcyjną”, ale moim kontrargumentem jest to, że celem testów jednostkowych jest umożliwienie programistom wprowadzania zmian, uruchom testy i przeanalizuj wyniki. Część tych zmian może obejmować zastąpienie metod klasy abstrakcyjnej, zarówno
protected
ipublic
, jak , co może spowodować fundamentalne zmiany w zachowaniu. W zależności od charakteru tych zmian może to wpłynąć na działanie aplikacji w nieoczekiwany, prawdopodobnie negatywny sposób. Jeśli masz dobry zestaw testów jednostkowych, problemy wynikające z tego typu zmian powinny być widoczne w czasie programowania.źródło
final
? Nie widzę powodu do wielokrotnego testowania tej samej metody, jeśli implementacja nie może się zmienićUtwórz klasę konkretną, która dziedziczy klasę abstrakcyjną, a następnie przetestuj funkcje, które klasa konkretna dziedziczy z klasy abstrakcyjnej.
źródło
Z przykładową klasą, którą opublikowałeś, nie ma sensu testowanie,
getFuel()
agetSpeed()
ponieważ mogą one zwracać tylko 0 (nie ma ustawiania).Jednak zakładając, że był to tylko uproszczony przykład dla celów ilustracyjnych i że masz uzasadnione powody do testowania metod w abstrakcyjnej klasie bazowej (inni już wskazali implikacje), możesz skonfigurować swój kod testowy tak, aby tworzył anonimowy podklasa klasy bazowej, która zapewnia tylko fikcyjne (bez operacji) implementacje dla metod abstrakcyjnych.
Na przykład w swoim
TestCase
możesz to zrobić:c = new Car() { void drive() { }; };
Następnie przetestuj pozostałe metody, np .:
public class CarTest extends TestCase { private Car c; public void setUp() { c = new Car() { void drive() { }; }; } public void testGetFuel() { assertEquals(c.getFuel(), 0); } [...] }
(Ten przykład jest oparty na składni JUnit3. W przypadku JUnit4 kod byłby nieco inny, ale idea jest taka sama).
źródło
Jeśli i tak potrzebujesz rozwiązania (np. Ponieważ masz zbyt wiele implementacji klasy abstrakcyjnej, a testowanie zawsze powtarzałoby te same procedury), możesz stworzyć abstrakcyjną klasę testową z abstrakcyjną metodą fabryczną, która zostanie usprawniona przez implementację tego klasa testowa. Ten przykład działa lub ja z TestNG:
Abstrakcyjna klasa testowa
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); ...
Wdrożenie
Car
class ElectricCar extends Car { private final int batteryCapacity; public ElectricCar(int speed, int fuel, int batteryCapacity) { super(speed, fuel); this.batteryCapacity = batteryCapacity; } ...
Klasa testu jednostkowego
ElectricCarTest
klasyElectricCar
: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 ...
źródło
Mógłbyś zrobić coś takiego
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); } }
źródło
Utworzyłbym wewnętrzną klasę jUnit, która dziedziczy po klasie abstrakcyjnej. Można to utworzyć i mieć dostęp do wszystkich metod zdefiniowanych w klasie abstrakcyjnej.
public class AbstractClassTest { public void testMethod() { ... } } class ConcreteClass extends AbstractClass { }
źródło
Możesz utworzyć instancję klasy anonimowej, a następnie przetestować tę klasę.
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); } }
Należy pamiętać, że widoczność musi dotyczyć
protected
właściwościmyDependencyService
klasy abstrakcyjnejClassUnderTest
.Możesz również zgrabnie połączyć to podejście z Mockito. Zobacz tutaj .
źródło
Mój sposób na sprawdzenie tego jest dość prosty w każdym
abstractUnitTest.java
. Po prostu tworzę klasę w abstractUnitTest.java, która rozszerza klasę abstrakcyjną. I przetestuj to w ten sposób.źródło
Nie możesz przetestować całej klasy abstrakcyjnej. W tym przypadku masz metody abstrakcyjne, co oznacza, że powinny być implementowane przez klasę rozszerzającą daną klasę abstrakcyjną.
W tej klasie programista musi napisać kod źródłowy przeznaczony dla jego logiki.
Innymi słowy, nie ma sensu testować klasy abstrakcyjnej, ponieważ nie jesteś w stanie sprawdzić jej ostatecznego zachowania.
Jeśli masz główną funkcjonalność niezwiązaną z metodami abstrakcyjnymi w jakiejś klasie abstrakcyjnej, po prostu utwórz inną klasę, w której metoda abstrakcyjna zgłosi wyjątek.
źródło
Opcjonalnie można utworzyć abstrakcyjną klasę testową obejmującą logikę wewnątrz klasy abstrakcyjnej i rozszerzyć ją dla każdego testu podklasy. W ten sposób możesz upewnić się, że ta logika zostanie przetestowana dla każdego dziecka osobno.
źródło