JUnit ile Java'da soyut sınıf nasıl test edilir?


87

JUnit ile Java testinde yeniyim. Java ile çalışmalıyım ve birim testleri kullanmak istiyorum.

Benim problemim şu: Bazı soyut yöntemler içeren soyut bir sınıfım var. Ancak soyut olmayan bazı yöntemler var. Bu sınıfı JUnit ile nasıl test edebilirim? Örnek kod (çok basit):

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

Test etmek getSpeed()ve getFuel()işlev görmek istiyorum .

Bu soruna benzer soru burada , ancak JUnit kullanmıyor.

JUnit SSS bölümünde bu bağlantıyı buldum , ancak yazarın bu örnekle ne söylemek istediğini anlamıyorum. Bu kod satırı ne anlama geliyor?

public abstract Source getSource() ;

4
Mockito kullanan iki çözüm için stackoverflow.com/questions/1087339/… sayfasına bakın .
ddso

Test etmek için başka bir çerçeve öğrenmenin herhangi bir avantajı var mı? Mockito yalnızca jUnit'in bir uzantısı mı yoksa tamamen farklı bir proje mi?
vasco

Mockito, JUnit'in yerini almaz. Diğer alaycı çerçeveler gibi, bir birim test çerçevesine ek olarak kullanılır ve test durumlarınızda kullanmak üzere sahte nesneler oluşturmanıza yardımcı olur.
ddso

Yanıtlar:


105

Sınıfın somut bir uygulamasına sahip değilseniz ve yöntemler değilse static, onları test etmenin amacı nedir? Somut bir sınıfınız varsa, bu yöntemleri somut sınıfın genel API'sinin bir parçası olarak test edeceksiniz.

Ne düşündüğünüzü biliyorum "Bu yöntemleri tekrar tekrar test etmek istemiyorum, bu yüzden soyut sınıfı yarattım", ancak buna karşı argümanım, birim testlerinin amacının geliştiricilerin değişiklik yapmasına izin vermesidir. testleri çalıştırın ve sonuçları analiz edin. Bu değişikliklerin bir kısmı, soyut sınıfınızın yöntemlerini geçersiz kılmayı içerebilir, hem protectedve bu public, temel davranış değişikliklerine neden olabilir. Bu değişikliklerin niteliğine bağlı olarak, uygulamanızın beklenmedik, muhtemelen olumsuz şekillerde çalışma şeklini etkileyebilir. İyi bir birim test paketi sorunlarınız varsa, bu türlerden kaynaklanan değişiklikler geliştirme sırasında görünür olmalıdır.


17
% 100 kod kapsamı bir efsanedir. Uygulamanızın nasıl davranması gerektiğine ilişkin bilinen tüm hipotezlerinizi kapsayacak kadar tam olarak yeterli teste sahip olmalısınız (tercihen, Test Driven Development kodunu yazmadan önce yazılmalıdır). Şu anda çok iyi işleyen bir TDD ekibi üzerinde çalışıyorum ve tamamı geliştirdiğimiz şekilde yazılan son yapımızdan itibaren yalnızca% 63 kapsama sahibiz. İyi mi? kim bilir? Ama geri dönüp bunu daha yükseğe çıkarmaya çalışmanın zaman kaybı olduğunu düşünürdüm.
nsfyn55

3
Elbette. Bazıları bunun iyi bir TDD ihlali olduğunu iddia edebilir. Bir takımda olduğunuzu hayal edin. Yöntemin nihai olduğunu varsayarsınız ve herhangi bir somut uygulamada testler koymazsınız. Birisi değiştiriciyi kaldırır ve devralma hiyerarşisinin tüm bir dalını aşağı çeken değişiklikler yapar. Test süitinizin bunu yakalamasını istemez miydiniz?
nsfyn55

31
Katılmıyorum. TDD'de çalışsanız da çalışmasanız da, soyut sınıfınızın somut yöntemi kod içerir, bu nedenle testlere sahip olmaları gerekir (alt sınıflar olup olmadığına bakılmaksızın). Ayrıca, Java testleri (normalde) sınıflarında birim testi. Bu nedenle, sınıfın bir parçası olmayan, süper sınıfının bir parçası olan test yöntemlerinde gerçekten hiçbir mantık yoktur. Bu mantığı takip ederek, hiçbir alt sınıfı olmayan sınıflar dışında, Java'da hiçbir sınıfı test etmemeliyiz. Geçersiz kılınan yöntemlerle ilgili olarak, bu tam olarak alt sınıfın testindeki değişiklikleri / eklemeleri kontrol etmek için bir test eklediğiniz zamandır.
ethanfar

3
@ nsfyn55 Ya somut yöntemler olsaydı final? Uygulama eğer aynı yöntem birden çok kez test etmek için bir neden görmüyorum olamaz değiştirmek
Dioksin

3
Testlerin soyut arayüzü hedeflemesini sağlamamız gerekmez mi, böylece onları tüm uygulamalar için çalıştırabiliriz? Mümkün değilse, bilmek ve düzeltmek istediğimiz Liskov'u ihlal etmiş oluruz. Yalnızca uygulama bazı genişletilmiş (uyumlu) işlevsellik eklerse, bunun için (ve yalnızca bu ekstra işlevsellik için) belirli bir birim testimiz olmalıdır.
tne

36

Soyut sınıfı miras alan somut bir sınıf oluşturun ve ardından somut sınıfın soyut sınıftan miras aldığı işlevleri test edin.


Soyut sınıfı genişleten 10 somut sınıfınız varsa ve bu somut sınıfların her biri yalnızca 1 yöntem uygularsa ve diyelim ki diğer 2 yöntem bu sınıfların her biri için aynıdır, çünkü bunlar soyut olarak uygulanır. sınıf? Benim durumum, soyut sınıfa yönelik testleri her bir alt sınıfa kopyalayıp yapıştırmak istemememdir.
scarface

12

Gönderdiğiniz örnek sınıfla test etmek pek mantıklı görünmüyor getFuel()ve getSpeed()yalnızca 0 döndürebildikleri için (ayarlayıcı yok).

Bununla birlikte, bunun açıklama amacıyla basitleştirilmiş bir örnek olduğunu ve soyut temel sınıfta yöntemleri test etmek için meşru nedenleriniz olduğunu varsayarsak (diğerleri, sonuçları zaten belirtmişlerdir), test kodunuzu anonim oluşturacak şekilde ayarlayabilirsiniz. soyut yöntemler için sadece kukla (işlemsiz) uygulamalar sağlayan temel sınıfın alt sınıfı.

Örneğin, TestCasesizde bunu yapabilirsiniz:

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

Ardından geri kalan yöntemleri test edin, örneğin:

public class CarTest extends TestCase
{
    private Car c;

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

    public void testGetFuel() 
    {
        assertEquals(c.getFuel(), 0);
    }

    [...]
}

(Bu örnek JUnit3 sözdizimine dayanmaktadır. JUnit4 için kod biraz farklı olacaktır, ancak fikir aynıdır.)


Cevap için teşekkürler. Evet, örneğim basitleştirildi (ve o kadar da iyi değildi). Buradaki tüm cevapları okuduktan sonra kukla sınıf yazdım. Ancak cevabında @ nsfyn55 yazdığı gibi, bu soyut sınıfın her soyundan gelenler için test yazıyorum.
vasco

9

Yine de bir çözüme ihtiyacınız varsa (örneğin, soyut sınıfın çok fazla uygulamasına sahip olduğunuzdan ve test her zaman aynı prosedürleri tekrar edeceğinden), o zaman bunun uygulanmasıyla ortadan kaldırılacak olan bir soyut fabrika yöntemiyle soyut bir test sınıfı oluşturabilirsiniz. test sınıfı. Bu örnekler TestNG ile çalışır veya ben:

Soyut test sınıfı 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);
    ...

Uygulanması Car

class ElectricCar extends Car {

    private final int batteryCapacity;

    public ElectricCar(int speed, int fuel, int batteryCapacity) {
        super(speed, fuel);
        this.batteryCapacity = batteryCapacity;
    }

    ...

Birim Test sınıfı ElectricCarTestSınıf 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

Bunun gibi bir şey yapabilirsin

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

4

Soyut sınıftan miras alan bir jUnit iç sınıfı yaratırdım. Bu, somutlaştırılabilir ve soyut sınıfta tanımlanan tüm yöntemlere erişebilir.

public class AbstractClassTest {
   public void testMethod() {
   ...
   }
}


class ConcreteClass extends AbstractClass {

}

3
Bu harika bir tavsiye. Yine de bir örnek sağlanarak geliştirilebilir. Belki de tarif ettiğiniz sınıfın bir örneği.
SDJMcHattie

2

Anonim bir sınıfı başlatabilir ve ardından bu sınıfı test edebilirsiniz.

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

Görünürlüğün soyut sınıfın protectedözelliği için olması gerektiğini unutmayın .myDependencyServiceClassUnderTest

Ayrıca bu yaklaşımı Mockito ile düzgün bir şekilde birleştirebilirsiniz. Buraya bakın .


2

Bunu test etme yöntemim, her birinin içinde oldukça basit abstractUnitTest.java. Basitçe abstractUnitTest.java'da soyut sınıfı genişleten bir sınıf oluşturuyorum. Ve bu şekilde test edin.


0

Soyut sınıfın tamamını test edemezsiniz. Bu durumda soyut metotlarınız vardır, bu onların soyut sınıfı genişleten sınıf tarafından uygulanması gerektiği anlamına gelir.

Bu sınıfta programcı, kendi mantığına adanmış kaynak kodunu yazmalıdır.

Başka bir deyişle, soyut sınıfı test etmenin bir anlamı yoktur çünkü onun son davranışını kontrol edemezsiniz.

Bazı soyut sınıflarda soyut yöntemlerle ilgili olmayan büyük işlevselliğe sahipseniz, soyut yöntemin bazı istisnalar atacağı başka bir sınıf oluşturun.


0

Bir seçenek olarak, soyut sınıf içinde mantığı kapsayan soyut bir test sınıfı oluşturabilir ve her bir alt sınıf testi için genişletebilirsiniz. Böylece bu mantığın her çocuk için ayrı ayrı test edilmesini sağlayabilirsiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.