Bagaimana cara menguji penanganan kesalahan dalam pengujian umum?

Saya mulai menggunakan common test sebagai kerangka pengujian saya di erlang.

Misalkan saya memiliki fungsi yang saya harapkan hanya menerima bilangan positif dan fungsi tersebut akan hilang jika tidak.

positive_number(X) when > 0 -> {positive, X}.

dan saya ingin mengujinya

positive_number(-5).

tidak akan berhasil diselesaikan.

Bagaimana cara menguji perilaku ini? Dalam bahasa lain saya akan mengatakan bahwa kasus uji mengharapkan beberapa kesalahan atau pengecualian dan gagal jika fungsinya dalam pengujian tidak menimbulkan kesalahan apa pun karena parameter tidak valid yang tidak valid. Bagaimana cara melakukannya dengan tes umum?

Pembaruan:

Saya bisa membuatnya berfungsi

test_credit_invalid_input(_) ->
  InvalidArgument = -1,
  try mypackage:positive_number(InvalidArgument) of
    _ -> ct:fail(not_failing_as_expected)
  catch
    error:function_clause -> ok
  end.

tapi menurut saya ini terlalu bertele-tele, saya ingin sesuatu seperti:

assert_error(mypackage:positive_number, [-1], error:function_clause)

Saya berasumsi bahwa tes umum memiliki hal ini di beberapa tempat dan kurangnya pengetahuan saya tentang kerangka kerja yang membuat saya mengambil solusi yang bertele-tele.

Pembaruan: Terinspirasi oleh respons Michael, saya membuat fungsi berikut:

assert_fail(Fun, Args, ExceptionType, ExceptionValue, Reason) ->
  try apply(Fun, Args) of
    _ -> ct:fail(Reason)
  catch
    ExceptionType:ExceptionValue -> ok
  end.

dan tes saya menjadi:

test_credit_invalid_input(_) ->
  InvalidArgument = -1,
  assert_fail(fun mypackage:positive_number/1,
              [InvalidArgument],
              error,
              function_clause,
              failed_to_catch_invalid_argument).

tapi menurut saya ini berhasil karena panggilan assert_fail sedikit lebih mudah dibaca daripada panggilan try....catch di setiap kasus uji.

Saya masih berpikir bahwa beberapa implementasi yang lebih baik harus ada di Common Test, IMO merupakan pengulangan yang buruk jika pola pengujian ini diterapkan berulang kali di setiap proyek.


person Jonas Fagundes    schedule 15.11.2015    source sumber


Jawaban (1)


Ubah pengecualian menjadi ekspresi dan cocokkan:

test_credit_invalid_input(_) ->
    InvalidArgument = -1,
    {'EXIT', {function_clause, _}}
            = (catch mypackage:positive_number(InvalidArgument)).

Itu mengubah pengecualian Anda menjadi bukan pengecualian dan sebaliknya, mungkin dengan cara yang sesingkat yang Anda harapkan.

Anda juga selalu dapat menggunakan makro atau fungsi untuk menyembunyikan verbositasnya.

Edit (re: komentar):

Jika Anda menggunakan konstruksi coba tangkap penuh, seperti dalam pertanyaan Anda, Anda kehilangan informasi tentang kasus kegagalan apa pun, karena informasi tersebut dibuang demi atom 'not_failing_as_expected' atau 'failed_to_catch_invalid_argument'.

Mencoba nilai gagal yang diharapkan pada shell:

1> {'EXIT', {function_clause, _}}
        = (catch mypackage:positive_number(-4)).             
{'EXIT',{function_clause,[{mypackage,positive_number,
                                 [-4],
                                 [{file,"mypackage.erl"},{line,7}]},
                      {erl_eval,do_apply,6,[{file,"erl_eval.erl"},{line,661}]},
                      {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,434}]},
                      {erl_eval,expr,5,[{file,"erl_eval.erl"},{line,441}]},
                      {shell,exprs,7,[{file,"shell.erl"},{line,676}]},
                      {shell,eval_exprs,7,[{file,"shell.erl"},{line,631}]},
                      {shell,eval_loop,3,[{file,"shell.erl"},{line,616}]}]}}

Benar nilai keberhasilan yang diharapkan pada shell:

2> {'EXIT', {function_clause, _}} = (catch mypackage:positive_number(3)). 
** exception error: no match of right hand side value {positive,3}

Dalam kedua kasus tersebut Anda mendapatkan banyak informasi, namun yang terpenting, dari keduanya Anda dapat mengetahui parameter apa yang digunakan untuk memanggil fungsi Anda yang sedang diuji (walaupun dalam kasus kedua ini hanya karena fungsi Anda determinate dan satu lawan satu).

Dalam kasus sederhana seperti ini hal-hal ini tidak terlalu penting, tetapi secara prinsip, ini penting, karena nanti dengan kasus-kasus yang lebih kompleks di mana mungkin nilai yang gagal pada fungsi Anda tidak dikodekan secara keras seperti di sini, Anda dapat tidak tahu nilai apa yang menyebabkan fungsi Anda gagal, atau nilai pengembalian apa. Hal ini mungkin akan menjadi perbedaan antara melihat penelusuran balik yang buruk selama satu atau dua saat dan menyadari dengan tepat apa masalahnya, atau menghabiskan 15 menit untuk menyiapkan tes untuk benar-benar mengetahui apa yang terjadi... ATAU, lebih buruk lagi, jika itu adalah heisenbug Anda mungkin menghabiskan waktu berjam-jam untuk mencarinya!

person Michael    schedule 15.11.2015
comment
Solusi ini berfungsi tetapi menghasilkan pesan kesalahan yang lebih membingungkan pada kegagalan pengujian dibandingkan solusi saya sebelumnya. Saya menyukai ide fungsi pembantu. - person Jonas Fagundes; 16.11.2015
comment
@JonasFagundes Anda dapat menyertakan makro EUnit dan menggunakannya dengan baik di bawah Uji Umum. - person Adam Lindberg; 16.11.2015
comment
@JonasFagundes Apakah pesan kesalahan yang membingungkan itu penting? Saya tahu jejak tumpukan Erlang tidak terlalu bagus, tetapi terutama karena Erlang berfungsi, Anda akan tahu fungsi mana yang gagal, di mana dan mengapa... sedangkan jika Anda menggunakan try catch seperti dalam pertanyaan Anda, Anda benar-benar membuang semua informasi itu dan ganti dengan 'not_failing_as_expected'. Saya memahami keinginan Anda untuk melakukan ini sampai taraf tertentu, tetapi menurut saya ini adalah cara yang salah. Saya akan menambahkan ini ke jawaban untuk memperjelas, sebenarnya tidak bisa melakukannya di komentar. - person Michael; 16.11.2015
comment
@JonasFagundes Tentu saja Anda dapat menyesuaikan solusi fungsi pembantu pilihan Anda untuk menggabungkan ini juga, jika Anda tidak keberatan menambahkan banyak verbositas yang membingungkan pada kesalahan Anda. - person Michael; 16.11.2015
comment
@Michael Saya mengerti maksud Anda tetapi dalam kasus uji memiliki terlalu banyak informasi itu buruk karena tidak memiliki informasi yang cukup, menemukan jumlah yang tepat itu adalah bagian yang sulit :) ct:fail dapat menerima format string dan argumen tetapi saya memutuskan bahwa memiliki sebuah tag ditambah nama kasus uji dalam laporan kesalahan sudah cukup dalam kasus ini. Itu tidak pernah kehilangan informasi dalam sistem yang sedang diuji, hanya kasus uji yang membuang informasi yang tidak perlu (ini akan menghasilkan error:function_clause yang menurut saya bukan praktik yang baik, saya akan mencabangkan fungsi untuk menangkapnya dan membuat pesan kesalahan yang tepat) . - person Jonas Fagundes; 16.11.2015