Veri erişim katmanı nasıl test edilir?


17

JDBC erişimi için Bahar kullanan bir DAO yöntemi var. Satıcının bir ürünü satma başarısını hesaplar.

İşte kod:

public BigDecimal getSellingSuccessRate(long seller_id) {
    String sql = "SELECT SUM(IF(sold_price IS NOT NULL, 1, 0))/SUM(1) 
                  FROM transaction WHERE seller_id = ?";
    Object[] args = {seller_id};
    return getJdbcTemplate().queryForObject(sql, args, BigDecimal.class);
}

Bu yöntemi veya herhangi bir DAO yöntemini JUnit ile test etmeye nasıl devam etmeliyim? Veri erişim mantığını test etmek için en iyi uygulamalar nelerdir? Bazı verilerle yüklenen gömülebilir bir veritabanına karşı test etmeyi düşünüyorum, ancak RDBMS ve şema açısından bir üretim ortamına benzer entegrasyon testleri yapmamalıyız?


DBUnit'e göz atın . Özellikle sorununuzu çözmek için yapılmıştır.
Sergio

Yanıtlar:


15

Birim testi için 'gerçek' bir veritabanı kullanmayla ilgili sorun, testlerin kurulumu, alınması ve yalıtılmasıdır. Tamamen yeni bir MySQL veritabanını döndürmek ve sadece bir birim test için tablolar ve veriler oluşturmak istemezsiniz. Bununla ilgili problemler veritabanının dış doğası ile ilgilidir ve test veritabanınız kapalı, birim testleriniz başarısız olur. Ayrıca, test için benzersiz bir veritabanına sahip olduğunuzdan emin olmayla ilgili sorunlar da vardır. Üstesinden gelinebilirler, ancak daha basit bir cevap var.

Veritabanını taşımak bir seçenektir ancak çalıştırılan gerçek sorguları test etmez. DAO'daki verilerin sistemden düzgün bir şekilde geçtiğinden emin olmak istediğinizde çok daha basit bir çözüm olarak kullanılabilir. Ancak DAO'nun kendisini test etmek için DAO'nun arkasında bir şeye ihtiyacınız var, veriler var ve sorgular düzgün çalışıyor.

Yapılacak ilk şey bir bellek içi veritabanı kullanmaktır. HyperSQL bunun için mükemmel bir seçimdir çünkü başka bir veritabanının lehçesini taklit etme yeteneğine sahiptir - böylece veritabanları arasındaki küçük farklar aynı kalır (veri türleri, işlevler ve benzerleri). hsqldb ayrıca birim testi için bazı güzel özelliklere sahiptir.

db.url=jdbc:hsqldb:file:src/test/resources/testData;shutdown=true;

Bu, veritabanının durumunu (tablolar, ilk veriler) testDatadosyadan yükler. shutdown=trueson bağlantı kapatıldığında veritabanını otomatik olarak kapatır.

Bağımlılık enjeksiyonu kullanarak , birim testlerinin , üretimin (veya testin veya yerelin) oluşturduklarından farklı bir veritabanı seçmesini sağlayın.

DAO'nuz daha sonra veritabanına karşı testler başlatabileceğiniz enjekte edilmiş veritabanını kullanır.

Birim testleri daha sonra aşağıdaki gibi görünecektir (kısalık için dahil olmayan sıkıcı şeyler demet):

    @Before
    public void setUpDB() {
        DBConnection connection = new DBConnection();
        try {
            conn = connection.getDBConnection();
            insert = conn.prepareStatement("INSERT INTO data (txt, ts, active) VALUES (?, ?, ?)");
        } catch (SQLException e) {
            e.printStackTrace();
            fail("Error instantiating database table: " + e.getMessage());
        }
    }

    @After
    public void tearDown() {
        try {
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    private void addData(String txt, Timestamp ts, boolean active) throws Exception {
        insert.setString(1, txt);
        insert.setTimestamp(2, ts);
        insert.setBoolean(3, active);
        insert.execute();
    }

    @Test
    public void testGetData() throws Exception {
        // load data
        Calendar time = Calendar.getInstance();
        long now = time.getTimeInMillis();
        long then1h = now - (60 * 60 * 1000);  // one hour ago
        long then2m = now - (60 * 1000 * 2);   // two minutes ago
        addData("active_foo", new Timestamp(then1h), true);     // active but old
        addData("inactive_bar", new Timestamp(then1h), false);  // inactive and old
        addData("active_quz", new Timestamp(then2m), true);     // active and new
        addData("inactive_baz", new Timestamp(then2m), false);  // inactive and new

        DataAccess dao = new DataAccess();
        int count = 0;
        for (Data data : dao.getData()) {
            count++;
            assertTrue(data.getTxt().startsWith("active"));
        }

        assertEquals("got back " + count + " rows instead of 1", count, 1);
    }

Ve böylece, DAO'yu çağıran ve test süresince var olan bir anında veritabanında kurulan verileri kullanan bir birim testiniz var. Çalışmadan önce harici kaynaklar veya veritabanının durumu hakkında endişelenmenize veya bilinen bir duruma geri yüklemenize gerek yoktur (iyi bilinen 'bilinen durum' geri döndürülmesi önemsizdir 'yoktur').

DBUnit , veritabanı kurma, tablolar oluşturma ve veri yükleme daha basit bir süreç tarif ne yapabilirim. Gerçek veritabanını herhangi bir nedenden dolayı kullanmanız gerekiyorsa, bu kullanmak için çok daha iyi bir araçtır.

Yukarıdaki kod github üzerinde TestingWithHsqldb kavramının kanıtı için yazdığım bir maven projesinin bir parçasıdır


2
HSQL başka bir db satıcının lehçesi alay edebilir kısmı hakkında bilmiyordum. Teşekkür ederim.
Michael

1
Bu yoluyla yapılabilir @Dog veritabanı özelliklerinde gibi sql.syntax_mys=trueyolu hsqldb işleri değiştirir: "Gerçek ayarladığınızda, bu özellik, METİN ve AUTO_INCREMENT türleri için destek sağlayan ve aynı zamanda bu lehçenin diğer bazı yönleri ile uyumluluğu sağlamak." iken sql.syntax_ora=trueyok "Bu özellik true ayarlandığında, standart dışı türleri için destek sağlar. Aynı zamanda Dual IS, ROWNUM, NEXTVAL ve currval sözdizimi ve sağlayan ve aynı zamanda bu lehçenin diğer bazı yönleri ile uyumluluğu sağlamak."

DBUnit yoludur :)
Silviu Burcea

@SilviuBurcea DBUnit, karmaşık bir veritabanı test ortamı kurmanın 'tesisatının' çoğunu, elle yapmaktan çok daha kolay hale getirir. Gerekirse, elle nasıl yapılacağını bilmek hala yararlıdır (yukarıda belirtilen 'elle' yaklaşımı DBUnit'in bir seçenek olmadığı diğer dillere geçirilebilir).


2

İlk olarak, üretim ortamında asla test yapmamalısınız. Üretim ortamınızı yansıtan ve orada entegrasyon testleri yapan bir test ortamınız olmalıdır.

Bunu yaparsanız, bir dizi şey yapabilirsiniz.

  • Mockito gibi bir alaycı çerçeve kullanılarak uygun SQL'in sahte bir öğeye gönderilip gönderilmediğini test eden birim testleri yazın. Bu, yönteminizin yapılması gerekeni yapmasını sağlar ve entegrasyonu resimden çıkarır.
  • Birim testlerinizde test ettiğiniz SQL'in uygunluğunu gösteren test SQL komut dosyaları yazın. Bu, test komut dosyalarınıza dayalı olarak açıklamaları ve benzerlerini çalıştırabileceğiniz, aklınıza gelebilecek tüm ayarlama sorunlarına yardımcı olabilir.
  • @Sergio tarafından belirtildiği gibi DBUnit kullanın.

Woops, üretim ortamını gerçekten söylediğimde, bunun simülasyonunu kastetmiştim. Cevabınız için teşekkür ederim, Mockito'ya bir göz atacağım çünkü bu da öğrenmek istediğim bir şey.
Michael

1

Projemizde, her geliştirici boş bir veritabanı çalıştırıyor, yapısı üretim veritabanı ile aynı.

TestInitialize'ın her birim testinde, veritabanına bir bağlantı ve işlem artı her test için ihtiyacımız olan bazı varsayılan nesneler oluştururuz. Ve her yöntem veya sınıfın sonunda her şey geri alınır.

Bu şekilde, sql katmanını test etmek mümkündür. Aslında, her sorgu veya veritabanı çağrısı bu şekilde test edilmelidir.

Dezavantajı yavaş olmasıdır, bu yüzden onu düzenli birim testlerimizden ayrı bir projeye koyarız. Bunu bir bellek içi veritabanı kullanarak hızlandırmak mümkündür, ancak fikir aynı kalır.


Bellek içi bir veritabanı kullanılıyorsa, tüm test paketleri çalışmadan önce bir bırakma oluşturma yaklaşımı, geri alma işlemi yerine kullanılabilir; bu da çok daha hızlıdır.
Downhillski

Bunu daha önce hiç böyle düşünmemiştim. Testlerimizde, çoğu test benzersiz bir 'x' kullanıcısı oluşturur. Bir kez db oluşturmak, bu nesneleri yeniden kullanmak için testleri değiştirmek anlamına gelir.
Carra

Biliyorum, aynı sayfadayız ve yaklaşımınızı seviyorum. yaklaşımınız, siparişe bakılmaksızın her test vakasının bağımsız olarak çalıştırılabileceğini ve her çalıştırmadan önce veri tablosunun durumunun aynı olduğunu garanti eder.
Downhillski

Bu doğru, sipariş o zaman önemli değil. Daha önce testlerin başarısız olduğunu gördük çünkü birim bilgisayarında ve yerel makinemizde birim test çalıştırması sırası farklı.
Carra
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.