Penamaan paket yang tepat untuk pengujian dengan bahasa Go

103

Saya telah melihat beberapa strategi penamaan paket pengujian yang berbeda dalam Go dan ingin tahu apa pro dan kontra masing-masing dan mana yang harus saya gunakan.

Strategi 1:

Nama file: github.com/user/myfunc.go

package myfunc

Uji nama file: github.com/user/myfunc_test.go

package myfunc

Lihat bzip2 sebagai contoh.

Strategi 2:

Nama file: github.com/user/myfunc.go

package myfunc

Uji nama file: github.com/user/myfunc_test.go

package myfunc_test

import (
    "github.com/user/myfunc"
)

Lihat kabel sebagai contoh.

Strategi 3:

Nama file: github.com/user/myfunc.go

package myfunc

Uji nama file: github.com/user/myfunc_test.go

package myfunc_test

import (
    . "myfunc"
)

Lihat string sebagai contoh.

Pustaka standar Go tampaknya menggunakan campuran strategi 1 dan 2. Manakah dari ketiganya yang harus saya gunakan? Ini menyakitkan menambahkan package *_testpaket pengujian saya karena itu berarti saya tidak dapat menguji metode pribadi paket saya tetapi mungkin ada keuntungan tersembunyi yang tidak saya sadari?

Dan
sumber
9
Pertanyaan ini hanya akan menghasilkan opini yang berbeda-beda, tapi saya akan memberikan pendapat saya. Anda tidak perlu menguji metode pribadi Anda. Anda ingin menguji antarmuka paket Anda yang akan digunakan oleh pengembang lain. Jika tes gagal maka Anda tahu metode privat Anda perlu dilihat.
Brenden
2
Contoh [wire] ( github.com/btcsuite/btcd/blob/master/wire/msgtx_test.go ) untuk Strategi 2, sebenarnya sekarang juga merupakan contoh dari Strategi 1 ...
selama

Jawaban:

133

Perbedaan mendasar antara tiga strategi yang Anda daftarkan adalah apakah kode pengujian berada dalam paket yang sama dengan kode yang diuji. Keputusan untuk menggunakan package myfuncatau package myfunc_testdalam file pengujian tergantung pada apakah Anda ingin melakukan pengujian kotak putih atau kotak hitam .

Tidak ada salahnya menggunakan kedua metode dalam sebuah proyek. Misalnya, Anda dapat memiliki myfunc_whitebox_test.godan myfunx_blackbox_test.go.

Perbandingan Paket Kode Tes

  • Pengujian Kotak Hitam: Gunakan package myfunc_test, yang akan memastikan Anda hanya menggunakan pengenal yang diekspor .
  • Pengujian Kotak Putih: Gunakan package myfuncagar Anda memiliki akses ke pengenal yang tidak diekspor. Baik untuk pengujian unit yang memerlukan akses ke variabel, fungsi, dan metode yang tidak diekspor.

Perbandingan Strategi yang Tercantum di Pertanyaan

  • Strategi 1: File myfunc_test.gomenggunakan package myfunc- Dalam hal ini kode uji di myfunc_test.goakan di paket yang sama seperti kode sedang diuji di myfunc.go, yang myfuncdalam contoh ini.
  • Strategi 2: File myfunc_test.gokegunaan package myfunc_test- Dalam hal ini kode uji di myfunc_test.go"akan dikompilasi sebagai paket terpisah, dan kemudian dihubungkan dan menjalankan dengan biner uji utama." [Sumber: Baris 58–59 di kode sumber test.go ]
  • Strategi 3: File myfunc_test.gomenggunakan package myfunc_testtetapi diimpor myfuncmenggunakan notasi titik - Ini adalah varian dari Strategi 2, tetapi menggunakan notasi titik untuk mengimpor myfunc.
Matthew Rankin
sumber
1
Perlu dicatat bahwa menggunakan Strategi 1 juga akan menyimpan file dengan _test.goterpisah dari paket yang sedang diuji (perilaku yang sama seperti Strategi 2). Ini tampaknya tidak didokumentasikan per github.com/golang/go/issues/15315
Kevin Deenanauth
Saya melihat paket yang kuat menggunakan Strategi 3, tetapi saya tidak mengerti apa gunanya?
PickBoy
1
Saya mem-fork sebuah paket dan membuat perubahan, dan sekarang semua pengujian saya mencoba mengimpor repo asli alih-alih paket bercabang saya. Dengan Strategi 3, saya tidak perlu mengubah "github.com/original/link" menjadi "github.com/my/fork", karena ini hanya mereferensikan '.' sebagai gantinya.
nmarley
1
@Kevinana Ini hanya mengejutkan saya. Saya pikir saya telah menemukan jebakan ketika saya baru saja menemukan _test.godengan nama non- _testpaket berisi a func init()yang mengubah beberapa variabel paket global untuk pengujian. Saya salah.
Zyl
1
@nmarley the .tidak menyelesaikan masalah garpu Anda. Ini bukan impor relatif. Itu hanya mengimpor pengidentifikasi "ke dalam paket saat ini".
qaisjp
19

Itu tergantung pada cakupan pengujian Anda. Tes tingkat tinggi (integrasi, penerimaan, dll ...) mungkin harus ditempatkan dalam paket terpisah untuk memastikan bahwa Anda menggunakan paket melalui API yang diekspor.

Jika Anda memiliki paket besar dengan banyak internal yang perlu diuji, gunakan paket yang sama untuk pengujian Anda. Tapi itu bukan undangan bagi pengujian Anda untuk mengakses sedikit pun status privat. Itu akan membuat refactoring menjadi mimpi buruk. Ketika saya menulis struct saat pergi, saya sering mengimplementasikan antarmuka. Ini adalah metode antarmuka yang saya panggil dari pengujian saya, tidak semua metode / fungsi pembantu secara individual.

mdwhatcott.dll
sumber
13

Anda harus menggunakan strategi 1 jika memungkinkan. Anda dapat menggunakan foo_testnama paket khusus untuk menghindari siklus impor, tetapi sebagian besar ada di sana sehingga pustaka standar dapat diuji dengan mekanisme yang sama. Misalnya, stringstidak dapat diuji dengan strategi 1 karena testingpaketnya bergantung strings. Seperti yang Anda katakan, dengan strategi 2 atau 3 Anda tidak memiliki akses ke pengenal pribadi paket, jadi biasanya lebih baik untuk tidak menggunakannya kecuali Anda harus melakukannya.

guelfey
sumber
10
Bagaimana tidak memiliki akses ke pengenal pribadi dalam ujian bukanlah suatu kebajikan?
jub0bs
3
berdasarkan praktik pengujian yang baik, Anda tidak menguji detail implementasi internal untuk artefak kode; melakukan nak, adalah bau kode
Gerardo Lima
0

Satu catatan penting yang ingin saya tambahkan import .dari Golang CodeReviewComments :

The import .Bentuk dapat berguna dalam tes itu, karena melingkar dependensi, tidak dapat dibuat bagian dari paket yang diuji:

package foo_test

import (
    "bar/testutil" // also imports "foo"
    . "foo"
)

Dalam kasus ini, file pengujian tidak dapat berada dalam paket foo karena digunakan bar/testutil, yang mengimpor foo. Jadi kami menggunakan 'impor.' formulir untuk membiarkan file berpura-pura menjadi bagian dari paket foo meskipun sebenarnya bukan.

Kecuali untuk kasus yang satu ini, jangan gunakanimport . dalam program Anda. Itu membuat program lebih sulit dibaca karena tidak jelas apakah nama seperti Quux adalah pengenal tingkat atas dalam paket saat ini atau dalam paket yang diimpor.

Eric
sumber