Skrip bash Unit Testing


112

Kami memiliki sistem yang memiliki beberapa skrip bash yang berjalan selain kode Java. Karena kami mencoba Menguji Segala Sesuatu yang Mungkin Bisa Rusak, dan skrip bash tersebut mungkin rusak, kami ingin mengujinya.

Masalahnya adalah sulit untuk menguji skrip bash.

Apakah ada cara atau praktik terbaik untuk menguji skrip bash? Atau haruskah kita berhenti menggunakan skrip bash dan mencari solusi alternatif yang dapat diuji?




Ikhtisar alat yang ada: medium.com/wemake-services/…
sobolevn

Jawaban:


48

Sebenarnya ada shunit2 , kerangka kerja pengujian unit berbasis xUnit untuk skrip shell berbasis Bourne. Saya sendiri belum pernah menggunakannya, tetapi mungkin ada baiknya untuk dicoba.

Pertanyaan serupa telah ditanyakan sebelumnya:


3
Saya dapat menegaskan (pun intended) bahwa shunit2 (versi 2.1.6) agak rusak hingga saat ini. AssertNull dan assertNotNull tidak berfungsi, meskipun Anda memberi mereka nilai langsung. assertEquals berfungsi dengan baik, tapi saya pikir saya harus melakukannya sendiri untuk saat ini.
labirin

@labyrinth, apakah Anda yakin masalahnya bukanlah kasus ini: github.com/kward/shunit2/issues/53 "Bagaimana cara menggunakan assertNull dengan benar?"?
Victor Sergienko

1
@Victor Mungkin saja saya tidak cukup berhati-hati dengan tanda kutip ganda saya. Saya akan segera kembali ke peran di mana shunit2 atau sistem pengujian unit bash akan sangat berguna. Saya akan mencobanya lagi.
labirin

5
Saya adalah pengguna dan terkadang kontributor shunit2, dan saya dapat mengonfirmasi bahwa proyek ini berjalan dengan baik pada tahun 2019.
Alex Harvey

31

Saya mendapat jawaban berikut dari kelompok diskusi:

dimungkinkan untuk mengimpor (menyertakan, apa pun) prosedur (fungsi, apa pun namanya) dari file eksternal. Itulah kunci untuk menulis skrip pengujian: Anda memecah skrip Anda menjadi prosedur independen yang kemudian dapat diimpor ke skrip yang sedang berjalan dan skrip pengujian Anda, dan kemudian skrip yang sedang berjalan menjadi sesederhana mungkin.

Metode ini seperti injeksi ketergantungan untuk skrip, dan terdengar masuk akal. Menghindari skrip bash dan menggunakan bahasa yang lebih mudah diuji dan kurang jelas lebih disukai.


4
Saya tidak yakin apakah saya harus memilih atas atau bawah, di satu sisi membagi menjadi bagian-bagian yang lebih kecil itu bagus, tetapi di sisi lain saya membutuhkan kerangka kerja bukan satu set skrip khusus
mpapis

10
Meskipun tidak ada yang salah dengan bash (saya menulis banyak, banyak skrip), itu adalah bahasa yang sulit untuk dikuasai. Aturan praktis saya adalah jika skrip cukup besar untuk membutuhkan pengujian, Anda mungkin harus beralih ke bahasa skrip yang mudah diuji.
Doug

1
Tetapi terkadang Anda perlu memiliki sesuatu yang dapat bersumber di shell pengguna. Tidak jelas bagi saya bagaimana Anda akan melakukannya tanpa menggunakan skrip shell
Itkovian

@ Itkovian - Anda dapat, misalnya, menggunakan npm untuk mengekspor file yang dapat dieksekusi ke jalur, jadi tidak diperlukan sumber (paket npm Anda harus diinstal secara global)
Eliran Malka

1
Saya akan mengikuti saran tentang tidak menggunakan bash. :)
Maciej Wawrzyńczuk

30

Pengujian Bash yang sesuai dengan TAP : Sistem Pengujian Otomatis Bash

TAP, Test Anything Protocol, adalah antarmuka berbasis teks sederhana antara modul pengujian dalam rangkaian pengujian. TAP memulai hidup sebagai bagian dari test harness untuk Perl tetapi sekarang telah diimplementasikan dalam C, C ++, Python, PHP, Perl, Java, JavaScript, dan lainnya.


14
Layak untuk mengungkapkan apa itu TAP dan mengapa seseorang harus peduli, jika tidak, itu hanya copy-paste yang tidak berarti
om-nom-nom

@ om-nom-nom: Saya menautkannya ke situs TAP sekarang.
Janus Troelsen

7
Karena tidak ada orang lain yang mengatakan tidak dapat dibayar: TAP = Test Anything Protocol
JW.

9

Nikita Sobolev menulis posting blog yang sangat bagus yang membandingkan beberapa kerangka kerja pengujian bash yang berbeda: Menguji aplikasi Bash

Untuk yang tidak sabar: Kesimpulan Nikita adalah menggunakan Kelelawar tetapi tampaknya Nikita melewatkan proyek inti Kelelawar yang menurut saya akan digunakan di masa mendatang karena proyek Kelelawar asli belum aktif dipertahankan sejak 2013.


7

Epoxy adalah kerangka kerja pengujian Bash yang saya rancang terutama untuk menguji perangkat lunak lain, tetapi saya juga menggunakannya untuk menguji modul bash, termasuk dirinya sendiri dan Karton .

Keuntungan utama adalah overhead pengkodean yang relatif rendah, penyarangan pernyataan tidak terbatas, dan pemilihan pernyataan yang fleksibel untuk diverifikasi.

Saya membuat presentasi yang membandingkannya dengan BeakerLib - kerangka kerja yang digunakan oleh beberapa orang di Red Hat.


6

Mengapa Anda mengatakan bahwa "sulit" untuk menguji skrip bash?

Apa yang salah dengan pembungkus uji seperti:

 #!/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
Pertama, skrip bash tidak terlalu mudah dibaca. Kedua, ekspektasi rumit seperti memeriksa apakah file kunci dibuat dengan PID dari skrip bash yang membuatnya.
nimcap

10
Lebih penting lagi, sulit untuk menguji skrip shell karena umumnya memiliki banyak efek samping dan menggunakan sumber daya sistem seperti sistem file, jaringan, dll. Idealnya, pengujian unit bebas efek samping dan tidak bergantung pada sumber daya sistem.
jayhendren

4

Saya membuat shellspec karena saya menginginkan alat yang mudah digunakan dan berguna.

Ini ditulis oleh skrip shell POSIX murni. Ini telah diuji dengan banyak cangkang lebih dari shunit2. Ini memiliki fitur yang kuat daripada kelelawar / inti kelelawar.

Misalnya, mendukung blok bersarang, mudah dibuat tiruan / stub, mudah dilewati / tertunda, pengujian berparameter, nomor baris pernyataan, dieksekusi berdasarkan nomor baris, eksekusi paralel, eksekusi acak, formatter TAP / JUnit, cakupan dan integrasi CI, profiler, dll. .

Lihat demo di halaman proyek.


3

Saya sangat menyukai shell2junit , sebuah utilitas untuk menghasilkan keluaran seperti JUnit dari pengujian skrip Bash. Ini berguna karena laporan yang dihasilkan kemudian dapat dibaca oleh sistem integrasi berkelanjutan, seperti plug-in JUnit untuk Jenkins dan Bamboo.

Meskipun shell2junit tidak menyediakan kerangka kerja skrip Bash yang komprehensif seperti shunit2 , shell2junit memungkinkan Anda memiliki laporan hasil pengujian yang bagus.


3

Coba bashtest . Ini cara sederhana untuk menguji skrip Anda. Misalnya, Anda memiliki do-some-work.shyang mengubah beberapa file konfigurasi. Misalnya, tambahkan baris baru PASSWORD = 'XXXXX'ke file konfigurasi /etc/my.cfg.

Anda menulis perintah bash baris demi baris dan kemudian memeriksa keluaran.

Install:

pip3 install bashtest

Membuat tes hanyalah menulis perintah bash.

File 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

Jalankan tes:

bashtest *.bashtest

Anda dapat menemukan beberapa contoh di sini dan di sini


3

Mungkin ini bisa digunakan, atau dikontribusikan

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

Dimaksudkan untuk menulis hasil dalam protokol TAP yang menurut saya bagus untuk CI, dan bagus untuk mereka yang menginginkan lingkungan shell. Saya membayangkan beberapa hal berjalan di lingkungan shell jadi, beberapa mungkin berpendapat harus diuji di lingkungan shell mereka.


3

Cobalah untuk menegaskan.sh

source "./assert.sh"

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

Semoga membantu!


3

Saya tidak percaya tidak ada yang berbicara tentang K3 ! Ini kompatibel dengan baik TAP dan JUnit, itu shell murni (yaitu, tidak ada bahasa lain yang terlibat), ia bekerja mandiri juga, dan itu sederhana dan langsung.

Pengujian terlihat seperti ini (cuplikan diambil dari halaman proyek):

#!/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

Lari sederhana:

$ 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

Tes terakhir menunjukkan "tidak ok", tetapi kode keluarnya adalah 0 karena a TODO. Seseorang dapat mengatur verbose juga:

$ 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

Ubah namanya untuk menggunakan .tekstensi dan letakkan di tsubdirektori, dan Anda dapat menggunakan prove(1)(bagian dari Perl) untuk menjalankannya:

$ 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

Atur OSHT_JUNITatau teruskan -juntuk menghasilkan keluaran JUnit. JUnit juga bisa digabungkan dengan prove(1).

Saya telah menggunakan pustaka ini untuk menguji fungsi dengan mencari file mereka dan kemudian menjalankan pernyataan dengan IS/ OKdan negatifnya, dan skrip dengan menggunakan RUN/ NRUN. Bagi saya, kerangka kerja ini memberikan keuntungan paling banyak dengan overhead paling sedikit.


1

Saya telah mencoba banyak solusi yang disajikan di sini, tetapi menemukan sebagian besar dari mereka besar dan sulit digunakan, jadi saya membangun kerangka pengujian kecil saya sendiri: https://github.com/meonlol/t-bash

Ini hanya satu file di repo yang dapat Anda jalankan secara langsung, dengan set dasar gaya JUnit menegaskan.

Saya telah menggunakannya secara profesional di beberapa proyek internal dan mampu membuat skrip bash kami sangat stabil dan tahan regresi.



0

Lihatlah Outthentic , sederhana, dapat dikembangkan oleh banyak bahasa (Perl, Python, Ruby, Bash on choice) dan kerangka kerja lintas platform (Linux, Windows) untuk menguji aplikasi baris perintah apa pun.


-2

Saya merasa sulit untuk membenarkan penggunaan bash untuk skrip yang lebih besar ketika Python memiliki keuntungan yang sangat besar:

  • Coba / Kecuali memungkinkan penulisan skrip yang lebih kuat dengan kemampuan untuk membatalkan perubahan jika terjadi kesalahan.
  • Anda tidak harus menggunakan sintaks yang tidak jelas seperti ' if [ x"$foo" = x"$bar"]; then ...' yang rentan terhadap kesalahan.
  • Penguraian opsi dan argumen dengan mudah menggunakan getoptmodul (dan ada modul yang lebih mudah untuk mengurai argumen, tetapi namanya luput dari saya).
  • Python memungkinkan Anda untuk bekerja dengan list / dicts dan objek alih-alih string dan array dasar.
  • Akses ke alat bahasa yang tepat seperti regex, database (Anda pasti bisa memasukkan semuanya ke dalam mysqlperintah di bash, tetapi ini bukan cara terbaik untuk menulis kode).
  • Tidak perlu khawatir tentang menggunakan bentuk yang benar dari $*atau "$*"atau "$@"atau $1atau "$1", spasi dalam nama file tidak menjadi masalah, dll, dll, dll.

Sekarang saya hanya menggunakan bash untuk skrip yang paling sederhana.


3
Tidak menyangkal fakta bahwa Python memiliki kelebihan tetapi poin kedua Anda tidak dimasukkan dengan baik. Perbandingan yang sama bisa dilakukan sebagai if [[ $foo = $bar ]]; then .... Ini masih tidak lebih baik dari apa yang ditawarkan python, tapi lebih baik dari apa yang Anda sajikan.
Shrikant Sharat

8
Beberapa sistem (misalnya tertanam) tidak memiliki python dan Anda tidak dapat / tidak ingin menginstal barang tambahan.
Rui Marques

2
Saya pribadi suka pesta, tetapi setuju bahwa itu bisa sedikit sulit. Anda biasanya harus jauh lebih proaktif sedangkan dengan Python Anda dapat mengatasi kesalahan setelah muncul. Namun, bash memiliki trap(untuk membersihkan / membatalkan jika terjadi kesalahan) serta regex (yaitu [[ $1 =~ ^[1-3]{3}$ ]]). Saya cukup yakin sintaks tidak jelas yang Anda gunakan mengacu pada implementasi lama test, bukan bash. Bash sangat bagus untuk berinteraksi dengan alat baris perintah yang ada ... Seringkali satu pipa ke awkatau grepjauh lebih mudah daripada alternatif Python.
Enam

1
BTW, modul parser yang Anda maksud kemungkinan besar optparseatau penerusnya argparse. Saya belum pernah melihat seseorang menggunakan getoptmodul ini, dan saya juga belum pernah menggunakannya secara pribadi. Namun getoptutilitasnya bagus. Penguraian argumen dari shell bukanlah masalah sama sekali setelah Anda mendapatkan pola yang bagus. Kecuali Anda mencoba menerapkan sub-perintah git-style atau semacamnya, itu tidak terlalu merepotkan.
Enam

Python tidak akan berjalan kemana-mana di mana bash bisa menjangkau. Saya mengatakan itu karena kami menguji bash versus python, logika kode yang sama, dan meminta keduanya untuk melakukan sesuatu. Bash memasuki setiap direktori yang dapat diaksesnya. Di sisi lain, python tidak dapat menangani beberapa izin direktori dan file serta direktori yang meningkat dan menurun dengan sangat cepat.
vianna77
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.