Gömülü C geliştiricileri için iyi birim testi örnekleri [kapalı]


20

Önümüzdeki hafta bölümümle birim testi ve test odaklı geliştirme hakkında bir konuşma yapacağım. Bunun bir parçası olarak, son zamanlarda yazdığım bazı kodlardan bazı gerçek dünya örneklerini göstereceğim, ama aynı zamanda konuşmada yazacağım bazı çok basit örnekleri de göstermek istiyorum.

Web'de iyi örnekler için arama yaptım, ancak özellikle geliştirme alanımız için geçerli olanları bulmakta zorlanıyorum. Yazdığımız yazılımların neredeyse tamamı, küçük mikrodenetleyiciler üzerinde çalışan, gömülü kontrol sistemleridir. 'Alt' katmandan uzak kaldığınız sürece birim testine kolayca uygulanabilen bir çok C kodu var (hedefin kendisinden ziyade PC'de birim testinden bahsedeceğim): doğrudan konuşan şeyler mikrodenetleyici çevre birimlerine. Bununla birlikte, bulduğum çoğu örnek, dize işlemeye (örneğin Python Romen rakamlarına mükemmel dalış örneği) dayanma eğilimindedir ve dizeleri neredeyse hiç kullanmadığımızdan bu gerçekten uygun değildir (kodumuzun kullandığı tek kütüphane işlevleri hakkında vardır memcpy, memcmpve memset,strcat veya düzenli ifadeler tam olarak doğru değildir).

Öyleyse, soru üzerine: Lütfen canlı bir oturumda birim testini göstermek için kullanabileceğim bazı işlev örnekleri sunabilir miyiz? (Değişime tabi) düşünceme göre iyi bir cevap muhtemelen şöyle olacaktır:

  • Herkesin (sadece arada sırada kod yazanlar bile) anlayabileceği kadar basit bir işlev;
  • Anlamsız görünmeyen bir işlev (yani parite veya CRC'yi çalışmak muhtemelen iki sayıyı bir araya getiren ve rastgele bir sabit ekleyen bir işlevden daha iyidir);
  • Bir odanın önünde yazmak için yeterince kısa bir işlev (hataları azaltmak için Vim'in birçok panosundan yararlanabilirim ...);
  • Sayıları, dizileri, işaretçileri veya yapıları parametre olarak alan ve dizeleri işlemek yerine benzer bir şey döndüren bir işlev;
  • Basit bir hataya sahip (örneğin >yerine >=), çoğu durumda yine de işe yarayacak, ancak belirli bir kenar durumla kırılacak olan bir fonksiyon: bir birim testi ile tanımlanması ve düzeltilmesi kolaydır.

Düşüncesi olan var mı?

Muhtemelen alakalı olmasa da, testlerin kendileri muhtemelen Google Test Çerçevesi kullanılarak C ++ 'da yazılacaktır: tüm başlıklarımızda zaten #ifdef __cplusplus extern "C" {etraflarında sarmalayıcı vardır; bu şimdiye kadar yaptığım testlerle iyi çalıştı.


Burada "problem" i TDD'yi yönetime satmak için bir sunum oluştururken, bu bana istenen forma oldukça iyi uyuyor gibi geliyor. OP bu soruna mevcut çözümler istiyor gibi görünüyor.
Technophile

Yanıtlar:


15

İşte len bayt üzerinde bir sağlama toplamı üretmesi gereken basit bir işlev .

int checksum(void *p, int len)
{
    int accum = 0;
    unsigned char* pp = (unsigned char*)p;
    int i;
    for (i = 0; i <= len; i++)
    {
        accum += *pp++;
    }
    return accum;
}

Bir çit direği hatası var: for ifadesinde, test olmalıdır i < len.

Eğlenceli olan, eğer böyle bir metin dizesine uygularsanız ...

char *myString = "foo";
int testval = checksum(myString, strlen(myString));

"doğru cevabı" alacaksın! Bunun nedeni, sağlama toplamı alınan ekstra baytın sıfır dize sonlandırıcısı olmasıdır. Böylece bu sağlama toplamı işlevini koda koyabilir ve hatta onunla birlikte gönderebilir ve asla bir sorun fark edemezsiniz - yani, metin dizelerinden başka bir şeye uygulamadan önce.

İşte bu hatayı işaretleyecek basit bir birim testi (çoğu zaman ... :-)

void main()
{
    // Seed the random number generator
    srand(time(NULL));

    // Fill an array with junk bytes
    char buf[1024];
    int i;
    for (i = 0; i < 1024; i++)
    {
        buf[i] = (char)rand();
    }

    // Put the numbers 0-9 in the first ten bytes
    for (i = 0; i <= 9; i++)
    {
        buf[i] = i;
    }

    // Now, the unit test. The sum of 0 to 9 should
    // be 45. But if buf[10] isn't 0 - which it won't be,
    // 255/256 of the time - this will fail.
    int testval = checksum(buf, 10);
    if (testval == 45)
    {
        printf("Passed!\n");
    }
    else
    {
        printf("Failed! Expected 45, got %d\n", testval);
    }
}

Çok iyi! Bu sadece umduğum türden bir cevap: teşekkür ederim.
DrAl

Bu bellek yığınında zaten çöpünüz olan bir tampon oluşturduğunuzda, rastgele sayılarla başlatmak gerçekten gerekli mi?
Snake Sanders

@SnakeSanders Evet derim, çünkü birim testlerinin olabildiğince belirleyici olmasını istiyorsunuz. Kullandığınız derleyici geliştirici makinenize 0 ve test makinenize 10 koyarsa, hatayı bulmak için korkunç bir zamanınız olacaktır. Aynı nedenle, sabit bir tohum yerine zamana bağımlı hale getirmenin kötü bir fikir olduğunu düşünüyorum.
Andrew,

Birim testinde deterministik olmayan davranışlara güvenmek kötü bir fikirdir. Bir lapa lapa testi er ya da geç baş ağrısı verecektir ...
sigy

2

Kabarcık sıralama gibi bir sıralama işlevi uygulamaya ne dersiniz ? Sıralama işlevi çalıştıktan sonra, birim testi ve TDD'yi tanıtmak için iyi olan ikili aramaya devam edebilirsiniz .

Sıralama ve arama, yanlış anlaşılması kolay karşılaştırmalara bağlıdır. Ayrıca dikkatle yapılması gereken işaretçileri değiştirmeyi de içerir. Her ikisi de hatalara eğilimlidir, bu yüzden karışıklıktan çekinmeyin :)

Birkaç fikir daha:

  • Birim testleri, yeniden düzenleme yaparken çok yardımcı olur. Kabarcık sıralamanız çalıştığında, bunu daha güçlü bir sıraya dönüştürebilirsiniz qsortve testler yine de geçmeli ve yeni sıralama işlevinizin de işe yaradığını kanıtlamalıdır.
  • Sıralama test etmek kolaydır, sonuç ya sıralanır ya da değildir, bu da onu iyi bir aday yapar.
  • Aynı şey arama için; ya var ya da yok.
  • Sıralama için test yazma, test için ne tür girdilerin kullanılacağı gibi tartışmaları açar (sıfır eleman, rastgele girdi, yinelenen girişler, devasa diziler vb.).

Basit bir hata için testin hayatı nasıl kolaylaştıracağını gösteren spesifik önerileriniz var mı?
DrAl

@DrAl: Cevabımı bununla güncelledim.
Martin Wickman
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.