Bash betiklerini birim test etme


112

Java kodunun yanı sıra bazı bash betikleri çalışan bir sistemimiz var. Muhtemelen Bozulabilecek Her Şeyi Test etmeye çalıştığımızdan ve bu bash komut dosyaları bozulabileceğinden, onları test etmek istiyoruz.

Sorun şu ki, bash betiklerini test etmek zor.

Bash komut dosyalarını test etmenin bir yolu veya en iyi uygulaması var mı? Yoksa bash komut dosyalarını kullanmayı bırakıp test edilebilir alternatif çözümler aramalı mıyız?




Mevcut araçlara genel bakış: medium.com/wemake-services/…
sobolevn

Yanıtlar:


48

Bir aslında yoktur shunit2 Bourne tabanlı kabuk komut için bir xUnit bazlı birim test çerçeve. Ben kendim kullanmadım ama kontrol etmeye değer olabilir.

Daha önce de benzer sorular sorulmuştu:


3
Shunit2'nin (sürüm 2.1.6) bugüne kadar biraz bozuk olduğunu iddia edebilirim (amaçlanan). AssertNull ve assertNotNull, onları doğrudan değerlerle besleseniz bile çalışmaz. assertEquals iyi çalışıyor, ama sanırım şimdilik kendi başıma dönmem gerekecek.
labirent

@labyrinth, sorunun bununla ilgili olmadığına emin misin: github.com/kward/shunit2/issues/53 " assertNull nasıl doğru kullanılır?"?
Victor Sergienko

1
@Victor Çift alıntılarım konusunda yeterince dikkatli olmamam kesinlikle mümkün. Yakında shunit2 veya bazı bash birim test sistemlerinin çok faydalı olacağı bir role geri dönüyorum. Tekrar deneyeceğim.
labirent

5
Ben bir kullanıcıyım ve bazen shunit2'ye katkıda bulunuyorum ve projenin 2019'da canlı ve iyi durumda olduğunu doğrulayabilirim.
Alex Harvey

31

Bir tartışma grubundan şu yanıtı aldım:

Harici bir dosyadan bir yordamı (işlev, adı ne olursa olsun) içe aktarmak (dahil olmak üzere) mümkündür. Bu, bir test komut dosyası yazmanın anahtarıdır: betiğinizi bağımsız prosedürlere bölersiniz, bunlar daha sonra hem çalışan betiğinize hem de test betiğinize aktarılabilir ve ardından çalışan betiğiniz mümkün olduğunca basit olur.

Bu yöntem, komut dosyaları için bağımlılık ekleme gibidir ve kulağa mantıklı geliyor. Bash komut dosyalarından kaçınmak ve daha fazla test edilebilir ve daha az anlaşılmaz bir dil kullanmak tercih edilir.


4
Yukarıya mı aşağıya mı oy vermem gerektiğinden emin değilim, bir yandan daha küçük parçalara bölmek iyidir, ancak ikinci yandan bir çerçeveye ihtiyacım var bir dizi özel komut dosyası değil
mpapis

10
Bash ile ilgili yanlış bir şey olmasa da (birçok senaryo yazdım), ustalaşması zor bir dil. Genel kuralım, eğer bir komut dosyası testlere ihtiyaç duyacak kadar büyükse, muhtemelen kolayca test edilebilen bir komut dosyası diline geçmelisiniz.
Doug

1
Ancak bazen, bir kullanıcı kabuğundan kaynaklanabilecek bir şeye ihtiyacınız vardır. Bir kabuk senaryosuna başvurmadan bunu nasıl yapacağımı bilmiyorum
Itkovian

@Itkovian - örneğin, bir yürütülebilir dosyayı yola aktarmak için npm kullanabilirsiniz, bu nedenle kaynak sağlama gerekmez (npm paketinizin global olarak yüklenmesi gerekir)
Eliran Malka

1
Bash'i kullanmama konusundaki tavsiyelere uyacağım. :)
Maciej Wawrzyńczuk

30

TAP uyumlu Bash testi: Bash Otomatikleştirilmiş Test Sistemi

TAP, Her Şeyi Test Etme Protokolü, bir test donanımındaki test modülleri arasındaki basit metin tabanlı bir arayüzdür. TAP, Perl için test koşum takımının bir parçası olarak hayata başladı ancak şimdi C, C ++, Python, PHP, Perl, Java, JavaScript ve diğerlerinde uygulamaları var.


14
Bu Ne TAP değer ifşa olduğunu ve neden, aksi takdirde sadece anlamsız bir bakım var olmalıdır copy-paste
om-nom-nom

@ om-nom-nom: Onu şimdi TAP sitesine bağladım.
Janus Troelsen

7
Hiç kimse ödenemez demediğinden: TAP = Her Şeyi Test Etme Protokolü
JW.

9

Nikita Sobolev, birkaç farklı bash testi çerçevesini karşılaştıran mükemmel bir blog yazısı yazdı: Bash uygulamalarını test etme

Sabırsız olanlar için: Nikita'nın sonucu Yarasalar'ı kullanmaktı, ancak Nikita, Bats projesi 2013'ten beri aktif olarak sürdürülmediğinden ileride kullanacağım gibi görünen Yarasalar çekirdeği projesini kaçırmış gibi görünüyor .


7

Epoksi , esas olarak diğer yazılımları test etmek için tasarladığım bir Bash test çerçevesidir, ancak bunu kendisi ve Carton dahil olmak üzere bash modüllerini de test etmek için kullanıyorum. .

Başlıca avantajları, nispeten düşük kodlama ek yükü, sınırsız onaylama yerleştirme ve doğrulamak için esnek iddia seçimidir.

Red Hat'ta bazılarının kullandığı bir çerçeve olan BeakerLib ile karşılaştıran bir sunum yaptım .


6

Neden bash betiklerini test etmenin "zor" olduğunu söylüyorsunuz?

Gibi test sarmalayıcıların nesi yanlış:

 #!/bin/bash
 set -e
 errors=0
 results=$($script_under_test $args<<ENDTSTDATA
 # inputs
 # go
 # here
 #
 ENDTSTDATA
 )
 [ "$?" -ne 0 ] || {
     echo "Test returned error code $?" 2>&1
     let errors+=1
     }

 echo "$results" | grep -q $expected1 || {
      echo "Test Failed.  Expected $expected1"
      let errors+=1
 }
 # and so on, et cetera, ad infinitum, ad nauseum
 [ "$errors" -gt 0 ] && {
      echo "There were $errors errors found"
      exit 1
 }

4
İlk olarak, bash betikleri çok okunabilir değildir. İkinci olarak, beklentiler, onu oluşturan bash betiğinin PID'si ile bir kilit dosyasının oluşturulup oluşturulmadığını kontrol etmek gibi karmaşıktır.
nimcap

10
Daha da önemlisi, kabuk komut dosyalarını test etmek zordur çünkü bunlar genellikle çok sayıda yan etkiye sahiptir ve dosya sistemi, ağ vb. Gibi sistem kaynaklarını kullanır. İdeal olarak, birim testleri yan etkisizdir ve sistem kaynaklarına bağlı değildir.
jayhendren

4

Oluşturduğum shellspec Bir üzere, kullanımı kolay ve kullanışlı bir araç istedik çünkü.

Saf POSIX kabuk betiği ile yazılmıştır. Shunit2'den çok daha fazla mermiyle test etti. Yarasa / yarasa çekirdeğinden daha güçlü özelliklere sahiptir.

Örneğin, iç içe blok desteği, takılması kolay / saplama, atlaması / beklemesi kolay, parametreli testler, onaylama satır numarası, satır numarasına göre yürütme, paralel yürütme, rasgele yürütme, TAP / JUnit biçimlendirici, kapsam ve CI entegrasyonu, profil oluşturucu vb. .

Proje sayfasındaki demoya bakın.


3

Bash komut dosyası testlerinden JUnit benzeri çıktı üreten bir yardımcı program olan shell2junit'i çok seviyorum . Bu yararlıdır çünkü oluşturulan rapor daha sonra Jenkins ve Bamboo için JUnit eklentileri gibi sürekli entegrasyon sistemleri tarafından okunabilir.

Shell2junit gibi kapsamlı Bash komut dosyası çerçeve sağlamasa da shunit2 , bu test sonuçlarının güzel raporlamaya sahip izin vermez.


3

Bashtest'i deneyin . Komut dosyalarınızı test etmenin basit yolu. Örneğin, do-some-work.shbazı yapılandırma dosyalarınızı değiştirdiğiniz var . Örneğin, PASSWORD = 'XXXXX'yapılandırma dosyasına yeni satır ekleyin/etc/my.cfg .

Bash komutlarını satır satır yazarsınız ve ardından çıktıyı kontrol edersiniz.

Yüklemek:

pip3 install bashtest

Test oluşturma, sadece bash komutlarını yazmaktır.

Dosya test-do-some-work.bashtest:

# run the script  
$ ./do-some-work.sh > /dev/null

# testing that the line "PASSWORD = 'XXXXX'" is in the file /etc/my.cfg   
$ grep -Fxq "PASSWORD = 'XXXXX'" /etc/my.cfg && echo "YES"
YES

Testleri çalıştırın:

bashtest *.bashtest

Burada ve burada bazı örnekler bulabilirsiniz


3

Belki bu kullanılabilir veya katkıda bulunabilir

https://thorsteinssonh.github.io/bash_test_tools/

Sonuçların CI için iyi olduğunu ve kabuk ortamları isteyenler için iyi olduğunu düşündüğüm TAP protokolünde yazılması amaçlanmıştır. Bazı şeylerin kabuk ortamlarında çalıştığını hayal ediyorum, bu yüzden bazıları kabuk ortamlarında test edilmesi gerektiğini iddia edebilir.


3

Assert.sh deneyin

source "./assert.sh"

local expected actual
expected="Hello"
actual="World!"
assert_eq "$expected" "$actual" "not equivalent!"
# => x Hello == World :: not equivalent!

Umarım yardımcı olur!


3

Kimsenin OSHT'den bahsetmediğine inanamıyorum ! Bu uyumludur hem TAP JUnit , saf bir kabuktur (yani, başka diller dahil değildir), bağımsız olarak da çalışır ve basit ve doğrudandır.

Test şuna benzer (proje sayfasından alınan pasajlar):

#!/bin/bash
. osht.sh

# Optionally, indicate number of tests to safeguard against abnormal exits
PLAN 13

# Comparing stuff
IS $(whoami) != root
var="foobar"
IS "$var" =~ foo
ISNT "$var" == foo

# test(1)-based tests
OK -f /etc/passwd
NOK -w /etc/passwd

# Running stuff
# Check exit code
RUNS true
NRUNS false

# Check stdio/stdout/stderr
RUNS echo -e 'foo\nbar\nbaz'
GREP bar
OGREP bar
NEGREP . # verify empty

# diff output
DIFF <<EOF
foo
bar
baz
EOF

# TODO and SKIP
TODO RUNS false
SKIP test $(uname -s) == Darwin

Basit bir çalışma:

$ bash test.sh
1..13
ok 1 - IS $(whoami) != root
ok 2 - IS "$var" =~ foo
ok 3 - ISNT "$var" == foo
ok 4 - OK -f /etc/passwd
ok 5 - NOK -w /etc/passwd
ok 6 - RUNS true
ok 7 - NRUNS false
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
ok 9 - GREP bar
ok 10 - OGREP bar
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
not ok 13 - TODO RUNS false # TODO Test Know to fail

Son test "tamam değil" olarak gösteriliyor, ancak çıkış kodu 0, çünkü bir TODO. Ayrıntılı bilgi de ayarlanabilir:

$ OSHT_VERBOSE=1 bash test.sh # Or -v
1..13
# dcsobral \!= root
ok 1 - IS $(whoami) != root
# foobar =\~ foo
ok 2 - IS "$var" =~ foo
# \! foobar == foo
ok 3 - ISNT "$var" == foo
# test -f /etc/passwd
ok 4 - OK -f /etc/passwd
# test \! -w /etc/passwd
ok 5 - NOK -w /etc/passwd
# RUNNING: true
# STATUS: 0
# STDIO <<EOM
# EOM
ok 6 - RUNS true
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
ok 7 - NRUNS false
# RUNNING: echo -e foo\\nbar\\nbaz
# STATUS: 0
# STDIO <<EOM
# foo
# bar
# baz
# EOM
ok 8 - RUNS echo -e 'foo\nbar\nbaz'
# grep -q bar
ok 9 - GREP bar
# grep -q bar
ok 10 - OGREP bar
# \! grep -q .
ok 11 - NEGREP . # verify empty
ok 12 - DIFF <<EOF
# RUNNING: false
# STATUS: 1
# STDIO <<EOM
# EOM
not ok 13 - TODO RUNS false # TODO Test Know to fail

Bir .tuzantı kullanmak için yeniden adlandırın ve bir talt dizine koyun ve prove(1)çalıştırmak için (Perl'in bir bölümünü) kullanabilirsiniz:

$ prove
t/test.t .. ok
All tests successful.
Files=1, Tests=13,  0 wallclock secs ( 0.03 usr  0.01 sys +  0.11 cusr  0.16 csys =  0.31 CPU)
Result: PASS

JUnit çıkışı üretmek için ayarlayın OSHT_JUNITveya -jgeçirin. JUnit ayrıca prove(1).

Birlikte iddialarını kendi dosyalarını kaynak ve ardından çalıştırarak bu kütüphaneyi hem test fonksiyonları kullandık IS/ OKkullanarak ve bunların negatifleri ve komut dosyalarını RUN/ NRUN. Benim için bu çerçeve, en az ek yük için en fazla kazancı sağlar.


1

Burada sunulan birçok çözümü denedim, ancak çoğunu hantal ve kullanımı zor buldum, bu yüzden kendi küçük test çerçevemi oluşturdum: https://github.com/meonlol/t-bash

Bu, depodaki temel bir JUnit stili iddiasıyla doğrudan çalıştırabileceğiniz tek bir dosyadır.

Bunu profesyonel olarak birkaç dahili projede kullandım ve bash komut dosyalarımızı süper kararlı ve gerilemeye dirençli hale getirmeyi başardım.



0

Outthentic'e bir göz atın , herhangi bir komut satırı uygulamasını test etmek için basit, birçok dilde (Perl, Python, Ruby, Bash seçime göre) ve çapraz platform (Linux, Windows) çerçevesi tarafından genişletilebilir.


-2

Python'un bu kadar büyük avantajları varken daha büyük komut dosyaları için bash kullanmayı haklı çıkarmayı zor buldum :

  • Try / Except, bir hata durumunda değişiklikleri geri alma yeteneği ile daha sağlam komut dosyaları yazmaya olanak tanır.
  • 'Gibi belirsiz bir sözdizimi kullanmak zorunda değilsinizif [ x"$foo" = x"$bar"]; then ... eğilimli ' .
  • Seçeneklerin ve argümanların kolay ayrıştırılması getoptModülü ayrıştırmak için daha da kolay bir modül var, ancak ad benden kaçıyor).
  • Python, temel dizeler ve diziler yerine listeler / diktler ve nesnelerle çalışmanıza izin verir.
  • Düzenli ifade, veritabanları gibi uygun dil araçlarına erişim (emin olun her şeyi mysqlbash komutuna aktarabilirsiniz , ancak kod yazmanın en güzel yolu değildir).
  • Doğru biçim $*veya "$*"veya "$@"veya $1veya veya veya veya veya veya veya "$1"dosya adlarındaki boşluklar bir sorun değil, vb, vb. Kullanma konusunda endişelenmenize gerek yok .

Şimdi bash'ı yalnızca en basit betikler için kullanıyorum.


3
Python'un avantajları olduğu gerçeğini inkar etmiyorum ama ikinci noktanız pek iyi ifade edilmiyor. Aynı karşılaştırma olarak da yapılabilirdi if [[ $foo = $bar ]]; then .... Bu hala python'un sunduğundan daha iyi değil, ama sunduğunuzdan daha iyi.
Shrikant Sharat

8
Bazı sistemlerde (ör. Gömülü) python mevcut değildir ve fazladan şeyler yükleyemezsiniz / kurmak istemezsiniz.
Rui Marques

2
Şahsen bash'ı seviyorum, ancak biraz sinirli olabileceğine katılıyorum. Genellikle çok daha proaktif olmanız gerekir, oysa Python'da hataları ortaya çıktıktan sonra ele alabilirsiniz. Bununla birlikte, bash trap(hata durumunda temizlemek / geri almak için) ve regex'e (ie [[ $1 =~ ^[1-3]{3}$ ]]) sahiptir. Kullandığınız belirsiz sözdiziminin testbash yerine eski uygulamalarına atıfta bulunduğundan oldukça eminim . Bash, mevcut komut satırı araçlarıyla arayüz oluşturmak için harikadır ... Sıklıkla tek bir boruya awkveya grepPython alternatifinden çok daha kolaydır.
Altı

1
BTW, bahsettiğiniz ayrıştırıcı modül büyük olasılıkla optparseveya onun halefidir argparse. getoptModülü kullanan hiç kimseyi görmedim ya da kişisel olarak kullanmadım. Yardımcı getoptprogram ancak harika. Güzel bir modele sahip olduğunuzda, kabuktan bağımsız değişken ayrıştırmak hiç sorun olmaz. Git tarzı alt komutları veya başka bir şeyi uygulamaya çalışmadığınız sürece, bu çok fazla sorun değildir.
Altı

Python, bash'ın ulaşabileceği her yerde çalışmaz. Bunu diyorum çünkü bash ile python'u, aynı kod mantığını test ettik ve ikisinden de bir şeyler yapmalarını istedik. Bash, erişebildiği her dizine girdi. Öte yandan, python bazı dizin izinlerini ve dosyalarını ve ayrıca çok hızlı artan ve azalan dizinleri işleyemedi.
vianna77
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.