Apakah ada cara yang lebih baik untuk menulis unit test daripada serangkaian 'AssertEquals'?

12

Berikut adalah contoh dasar tentang apa yang harus dilakukan oleh unit test saya, menggunakan qunit:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>

<link rel="stylesheet" href="qunit/qunit-1.13.0.css">
<script src = "qunit/qunit-1.13.0.js"></script>
<script src = "../js/fuzzQuery.js"></script>

<script>

test("Fuzz Query Basics", function()
        {
            equal(fuzzQuery("name:(John Smith)"), "name:(John~ Smith~)");
            equal(fuzzQuery("name:Jon~0.1"), "name:Jon~0.1");
            equal(fuzzQuery("Jon"), "Jon~");
            //etc

        }
    );

</script>
</head>
<body>
    <div id="qunit"></div>
</body>
</html>

Sekarang saya berpikir ini agak berulang.

Bisa memasukkan semua input / output ke dalam array, dan mengulanginya.

test("Fuzz Query Basics", function()
        {
            var equals = [
                           ["name:(John Smith)", "name:(John~ Smith~)"],
                           ["name:Jon~0.1", "name:Jon~0.1"],
                           ["Jon", "Jon~"]
                           ];

            for (var i = 0; i<equals.length; i++)
                {
                    equal(fuzzQuery(equals[i][0]), equals[i][1]);               
                }

        }
    );

Dan ini bekerja dengan baik.

Keuntungan tunggal yang dapat saya pikirkan untuk metode kedua ini, adalah bahwa jika ternyata Anda tidak benar-benar ingin menggunakannya equal, lebih mudah untuk membuat perubahan itu di satu tempat.

Dalam hal keterbacaan, saya tidak berpikir itu konklusif baik, meskipun saya mungkin lebih suka yang kedua.

Mengabstraksi lebih lanjut, Anda bisa memasukkan kasus input / output ke file CSV terpisah, yang mungkin membuatnya lebih mudah untuk dimodifikasi.

Pertanyaannya adalah - apa konvensi umum seputar penulisan unit test semacam ini?

Apakah ada alasan mengapa Anda tidak memasukkannya ke dalam array?

djohnston
sumber
Apakah salah satu dari ini akan memberi tahu Anda nilai mana yang gagal?
JeffO
1
@ JeffO - Ya - Dengan QUnit - Jika tes gagal, output akan menampilkan nilai yang diharapkan dan nilai aktual.
dwjohnston

Jawaban:

8

Tes refactored Anda berbau: Logika Tes Bersyarat .

Alasan Anda harus menghindari penulisan logika kondisional dalam ujian Anda ada dua. Yang pertama adalah itu merusak kemampuan Anda untuk menjadi yakin bahwa kode pengujian Anda benar, seperti yang dijelaskan dalam artikel Pola xUnit yang ditautkan.

Yang kedua adalah bahwa itu mengaburkan arti dari tes. Kami menulis Metode Uji karena mereka menempatkan logika untuk menguji perilaku yang diberikan di satu tempat, dan memungkinkan kami untuk memberikannya nama deskriptif (lihat artikel BDD asli Dan North untuk eksplorasi nilai nama baik untuk tes). Ketika tes Anda disembunyikan di dalam satu fungsi dengan satu forloop, itu mengaburkan makna kode untuk pembaca. Tidak hanya pembaca harus memahami loop, mereka juga harus secara mental mengungkap semua perilaku berbeda yang diuji dalam loop.

Solusinya, seperti biasa, adalah naik ke tingkat abstraksi. Gunakan kerangka uji yang memberi Anda tes parametris , seperti xUnit.NET atau Konteks lakukan (penafian: Saya menulis Konteks). Ini memungkinkan Anda untuk mengelompokkan tes triangulasi untuk perilaku yang sama secara alami, sambil memisahkan tes untuk perilaku yang terpisah.

Benjamin Hodgson
sumber
Pertanyaan yang bagus, omong-omong
Benjamin Hodgson
1
1) Jika Anda naik ke tingkat abstraksi, bukankah Anda menyembunyikan detail yang sama yang Anda katakan dikaburkan oleh for loop? 2) tidak yakin uji parameterisasi berlaku di sini. Sepertinya ada persamaan di sini di suatu tempat, tapi saya punya banyak situasi yang mirip dengan OPs di mana saya memiliki kumpulan data 10-20 nilai dan hanya ingin menjalankan semuanya melalui SUT. Ya, masing-masing nilai berbeda dan berpotensi menguji boudari yang berbeda tetapi tampaknya benar-benar datang nama tes "invent" untuk setiap nilai tunggal akan menjadi kerja keras. Saya menemukan nilai optimal / rasio ukuran kode dalam menggunakan yang serupa ...
DXM
... loop. Selama ketika tes gagal, menegaskan mencetak apa yang gagal, pengembang memiliki umpan balik yang cukup untuk menunjukkan masalah dengan tepat.
DXM
@ DXM 1) kerangka uji menyediakan fungsionalitas uji parametris. Kami mempercayai kerangka uji secara implisit sehingga kami tidak menulis tes untuk itu. 2) tes parametris untuk tujuan ini: Anda melakukan langkah yang persis sama setiap kali tetapi dengan nilai input / output yang berbeda. Kerangka kerja pengujian menghemat kebutuhan Anda untuk menulis nama untuk masing-masing dengan menjalankan input yang berbeda melalui metode pengujian yang sama.
Benjamin Hodgson
5

Sepertinya Anda benar-benar menginginkan Tes Unit Berbasis Data. Karena Anda menyebutkan menggunakan QUnit, saya menemukan sebuah plugin yang memungkinkan pengujian parameter:

https://github.com/AStepaniuk/qunit-parameterize

Tidak ada yang salah secara ideologis dengan tes yang didorong data, selama kode tes itu sendiri tidak bersyarat. Melihat kode tes Anda, tampaknya menjadi kandidat yang sangat baik untuk Tes Data Driven.

Kode contoh untuk GitHub README:

QUnit
    .cases([
        { a : 2, b : 2, expectedSum : 4 },
        { a : 5, b : 5, expectedSum : 10 },
        { a : 40, b : 2, expectedSum : 42 }
    ])
    .test("Sum test", function(params) {
        var actualSum = sum(params.a, params.b);
        equal(actualSum, params.expectedSum);
    });
Greg Burghardt
sumber
1
Setuju, sepertinya tes berbasis data. Tetapi sepertinya itulah yang sudah dia miliki dalam contoh kode kedua.
Robert Harvey
1
@RobertHarvey - Benar. Ada istilah yang diterima untuk apa yang dia coba capai, dan sebuah plugin ada untuk kerangka pengujian yang digunakan untuk membuatnya lebih mudah untuk menulis tes semacam ini. Saya pikir itu layak dicatat dalam jawaban untuk masa depan, itu saja.
Greg Burghardt
1

Anda mengulangi diri Anda lebih sedikit dengan menggunakan array yang lebih mudah dikelola. Salah satu pendekatan yang saya suka gunakan adalah memiliki metode terpisah yang mengatur, bertindak dan menegaskan tes, tetapi menerima parameter input yang saya uji dengan jadi saya punya 1 metode pengujian per set input.

Ini memungkinkan saya untuk langsung mengetahui tes / input mana yang gagal.

Kevin
sumber
0

Saya suka pendekatan kedua Anda, tetapi saya akan menambahkan 2 Poin

  • jangan gunakan array untuk menyimpan data yang diuji, karena bekerja dengan indeks adalah cara yang tidak bersih
  • jangan gunakan forloop

`

[
    {
        process: "name:(John Smith)",
        result: "name:(John~ Smith~)"
    },
    {
        process: "name:Jon~0.1", 
        result: "name:Jon~0.1"
    },
    {
        process: "Jon", 
        result: "Jon~"
    }
]
.forEach(function(data){

    var result = fuzzQuery(data.process);
    equal(result, data.result);
});

Saya tidak yakin tentang qunit, tetapi pelari ujian yang baik akan menunjukkan kepada Anda apa yang gagal input string, dan apa hasil yang diharapkan

tenbits
sumber