FYI: artikel ini adalah versi perluasan dari ceramah saya di SQA Days # 25.Berdasarkan pengalaman saya dengan kolega, saya dapat menyatakan: pengujian kode DB bukan praktik yang tersebar luas. Ini bisa berpotensi berbahaya. Logika DB ditulis oleh manusia seperti semua kode "biasa" lainnya. Jadi, bisa ada kegagalan yang dapat menyebabkan konsekuensi negatif untuk suatu produk, bisnis atau pengguna. Apakah ini adalah prosedur tersimpan yang membantu backend atau itu adalah ETL memodifikasi data di gudang - selalu ada risiko dan pengujian membantu menguranginya. Saya ingin memberi tahu Anda apa itu tSQLt dan bagaimana ini membantu kami untuk menguji kode DB.
Konteksnya
Ada gudang besar menggunakan SQL Server dan berisi data uji klinis yang berbeda. Itu diisi dari berbagai sumber (terutama database berorientasi dokumen). Banyak ETL mengubah data di dalam gudang pada banyak kesempatan. Data ini dapat dimuat ke dalam DB yang lebih kecil untuk digunakan oleh aplikasi web yang berorientasi pada tugas-tugas spesifik kecil. Beberapa pelanggan klien diminta untuk mengimplementasikan API untuk kebutuhan mereka. API semacam itu sering menggunakan prosedur tersimpan dan kueri yang berbeda.
Secara umum, ada jumlah kode yang cukup besar di sisi DBMS.
Mengapa kita membutuhkan ini?
Seperti yang Anda lihat dari pendahuluan, kode DB adalah bagian dari kode aplikasi dan juga mengandung bug.
Saya kira banyak dari kita yang akrab dengan Kurva Boehm: bug selalu lebih mahal untuk diperbaiki nanti dalam prosesnya. Kesalahan yang dibuat pada tahap pengembangan sebelumnya dan dilokalkan pada tahap selanjutnya bisa lebih mahal. Ini karena perlunya melalui banyak langkah sementara (pengkodean, pengujian unit, pengujian integrasi, pengujian sistem, dll.) Dua kali: untuk debugging dan untuk mengembalikan kode ke tahap di mana ia ditemukan. Efek ini berlaku untuk kasus gudang juga. Jika ada kesalahan dalam prosedur ETL dan data dimodifikasi beberapa kali, kita harus:
- pergi melalui semua langkah transformasi data kembali ke sumber masalah
- perbaiki masalah
- mendapatkan kembali data yang tepat (suntingan manual tambahan mungkin diperlukan)
- pastikan tidak ada data rusak lainnya yang disebabkan oleh bug.
Jangan lupa bahwa kami tidak menjual mainan lunak. Kesalahan dalam bidang seperti uji klinis dapat membahayakan tidak hanya bisnis tetapi juga kesehatan manusia.
Bagaimana cara menguji?
Karena kita berbicara tentang pengujian kode, yang kami maksud adalah pengujian unit dan integrasi. Hal-hal ini sangat berulang dan menyiratkan regresi persisten. Sebenarnya, pengujian semacam itu tidak pernah dilakukan secara manual (yah, kecuali beberapa kasus tunggal mungkin).
Bonus bagus: tes dapat menjadi bahan pendukung untuk mendokumentasikan kode. Misalnya, persyaratan mungkin terlihat seperti ini (dapat diklik):
File XLS, 2 kolom dengan persyaratan + informasi tambahan terfragmentasi di kolom lain + markup membingungkan. Mungkin sulit untuk mengembalikan keinginan awal jika perlu. Tes dapat membantu mencatat nuansa implementasi. Tentu saja, mereka tidak boleh dianggap sebagai pengganti dokumentasi.
Sayangnya, kompleksitas pengujian meningkat dengan meningkatnya kompleksitas kode, jadi, efek ini dapat diperhalus.
Tes bisa menjadi lapisan keamanan tambahan terhadap penggabungan spontan. Tes otomatis CI membantu dengan masalah ini karena formalisme mereka.
Jadi, jika kita memutuskan untuk menggunakan otomatisasi, kita perlu memilih alat untuk itu.
Apa yang akan digunakan untuk pengujian?
Dalam hal pengujian kode DB, saya melihat 2 pendekatan: SQL-powered (ketika alat berfungsi dalam DBMS secara langsung) dan non-SQL-powered. Inilah perbedaan utama yang saya temukan:
Dalam hal SQL Server, kami memiliki beberapa pilihan:
Skala "Luar Biasa - Gagal" subyektif, maaf, sulit untuk menemukan jalan keluar.
"Penampilan pertama" - tanggal paling awal penampilan kerangka yang bisa saya temukan - rilis paling awal atau komit.
Seperti yang Anda lihat, alternatif bertenaga SQL ditinggalkan cukup lama, dan tSQLt adalah satu-satunya produk yang saat ini didukung. Selain itu, tSQLt menang secara fungsional. Satu-satunya hal adalah, TST menawarkan serangkaian pernyataan yang lebih kaya daripada tSQLt; Namun, saya ragu ini bisa melebihi semua kontra.
Dokumentasi tSQLt memiliki beberapa nuansa, saya akan menjelaskannya nanti.
Di dunia yang tidak menggunakan SQL, banyak hal yang tidak begitu jelas. Alternatif berkembang, meskipun tidak super-aktif. DbFit adalah alat yang cukup menarik berdasarkan kerangka kerja FitNesse. Itu menyiratkan menggunakan markup wiki untuk tes menulis. Slacker juga menarik: pendekatan BDD disarankan untuk pengujian kode DB.
Saya harus mengatakan tentang pernyataan dalam solusi non-SQL-powered. Pada pandangan pertama, jumlah asersi lebih sedikit, dan kita dapat berpikir bahwa alat semacam itu lebih buruk. Tetapi kita harus ingat bahwa mereka pada dasarnya berbeda dari tSQLt, jadi, pandangan dangkal semacam itu tidak benar.
Baris terbaru - "NUnit, dll." - Lebih seperti pengingat. Banyak kerangka kerja unit-testing yang biasa dapat diterapkan pada kode DB dengan bantuan pustaka tambahan. Ada banyak N / A di baris ini karena baris ini, pada kenyataannya, termasuk beberapa alat. Itu adalah sumber "nuansa" di kolom "pernyataan" - alat yang berbeda dapat menyediakan perangkat yang berbeda dan tidak ada jaminan bahwa semua pernyataan dapat diterapkan ke DB.
Sebagai metrik lain yang menarik, kita dapat mempertimbangkan
tren Google .
Nuansa:
- Saya memutuskan untuk tidak memasukkan Slacker karena nama ini dapat berarti hal yang berbeda (dan pertanyaan seperti "Kerangka kerja Slacker" hampir tidak terlihat pada grafik).
- Hanya ingin tahu (dan karena satu slot tetap kosong), saya telah menambahkan tren TST. Tapi itu hampir tidak menunjukkan kepada kita gambaran nyata karena itu singkatan yang dapat berarti hal yang berbeda juga.
- Saya belum memasukkan NUnit dan analognya. Alat-alat ini adalah kerangka kerja untuk pengujian kode "biasa", sehingga tren mereka tidak deskriptif untuk konteks kita.
Seperti yang Anda lihat, tSQLt adalah alat yang paling dicari dalam daftar. Alat lain (kurang) populer adalah DbFit. Alat lain memiliki popularitas yang terbatas.
Secara umum, kita dapat melihat bahwa tSQLt bersinar dengan latar belakang.
Apa itu tSQLt?
Sangat mudah untuk menebak bahwa tSQLt adalah kerangka kerja unit-test bertenaga SQL. Situs resmi adalah
https://tsqlt.org .
Dijanjikan bahwa tSQLt mendukung SQL Server mulai dari 2005 SP2. Saya belum memeriksa revisi awal tersebut, tetapi saya tidak melihat masalah dengan 2012 di server-dev kami dan 2017 di komputer lokal saya.
Sumber terbuka, lisensi Apache 2.0,
tersedia di GitHub . Seperti biasa, kita dapat membayar, berkontribusi, menggunakan gratis dalam proyek komersial dan, yang lebih penting, tidak perlu takut spyware di CLR.
Mekanika
Kasus uji adalah prosedur tersimpan. Mereka dapat digabungkan ke dalam kelas uji (suite uji dalam terminologi xUnit).
Kelas tes tidak lain adalah skema DB. tSQLt perlu mendaftarkan mereka dengan prosedur NewTestClass yang menambahkan kelas uji ke tabel khusus.
Dimungkinkan untuk menentukan prosedur Penyetelan. Prosedur seperti itu akan berjalan sebelum setiap test case dijalankan.
Prosedur Teardown setelah uji kasus dijalankan tidak diperlukan. Setiap test case dengan SetUp dijalankan dalam transaksi terpisah yang dibatalkan setelah pengumpulan hasil. Ini sangat nyaman tetapi memiliki beberapa konsekuensi negatif - saya akan menjelaskannya sedikit nanti.
Kerangka kerja ini memungkinkan menjalankan uji kasus satu per satu, seluruh kelas uji sekaligus atau bahkan semua kelas uji terdaftar dengan satu perintah tunggal.
Fitur dan contoh
Tidak ingin mengulangi panduan resmi, saya akan menunjukkan fitur tSQLt pada contoh.
Penafian:- contoh disederhanakan
- kode asli bukan sepenuhnya milikku - itu adalah kreasi kolektif
- Contoh 2 difiksikan oleh saya untuk menunjukkan fitur lebih lengkap.
Contoh # 1: CsvSql
Berikut ini diimplementasikan atas permintaan salah satu pelanggan klien. Ada SQL-queries yang disimpan di bidang Nvarchar (MAX). UI minimal dibuat untuk melihatnya. Set hasil yang dihasilkan oleh query ini digunakan di backend untuk penulisan lebih lanjut sebagai file CSV. File CSV dapat diminta oleh panggilan API.
Kumpulan hasil berukuran besar dan mengandung banyak kolom. Contoh hipotetis dari rangkaian hasil tersebut:
Kumpulan hasil ini merupakan data uji klinis. Mari kita perhatikan perhitungan [ClinicsNum] lebih dekat. Kami memiliki 2 tabel: [Uji Coba] dan [Klinik].
Ada sebuah FK: [Klinik]. [TrialID] -> [Trial]. [TrialID]. Jelas, cukup menggunakan COUNT (*) untuk mendapatkan sejumlah klinik:
SELECT COUNT(*), ... FROM dbo.Trial LEFT JOIN dbo.Clinic ON Trial.ID = Clinic.TrialID WHERE Trial.Name = @trialName GROUP BY ...
Bagaimana kami bisa menguji permintaan seperti itu? Pertama, mari kita gunakan rintisan FakeTable, yang akan membuat pekerjaan kita lebih mudah.
EXEC tSQLt.FakeTable 'dbo.Trial'; EXEC tSQLt.FakeTable 'dbo.Clinic';
FakeTable membuat hal yang sederhana - mengganti nama tabel lama dan membuat yang baru dengan nama yang sama. Nama yang sama, kolom yang sama, tetapi tanpa kendala dan pemicu.
Kami membutuhkan ini karena:
- Tes DB dapat berisi beberapa data yang dapat mencegah jalannya pengujian dengan benar. FakeTable memungkinkan kita untuk tidak bergantung pada mereka.
- Biasanya, kita hanya perlu mengisi beberapa kolom untuk keperluan pengujian. Tabel dapat memuat banyak dari mereka, seringkali mengandung kendala dan pemicu. Kami membuatnya lebih mudah untuk memasukkan data nanti - kami hanya akan memasukkan informasi tes yang diperlukan, menjaga tes seminimal mungkin.
- Tidak akan ada pemicu yang tidak diinginkan, jadi, kita tidak perlu khawatir tentang efek pasca.
Kemudian kami memasukkan data uji yang diperlukan:
INSERT INTO dbo.Trial ([ID], [Name]) VALUES (1, 'Valerian'); INSERT INTO dbo.Clinic ([ID], [TrialID], [Name]) VALUES (1, 1, 'Clinic1'), (2, 1, 'Clinic2');
Kami memperoleh kueri dari DB, membuat tabel [Aktual] dan mengisinya dengan hasilnya
atur dari kueri.
DECLARE @sqlStatement NVARCHAR(MAX) = (SELECT… CREATE TABLE actual ([TrialID], ...); INSERT INTO actual EXEC sp_executesql @sqlStatement, ...
Sekarang, kami mengisi [Diharapkan] - nilai yang kami harapkan:
CREATE TABLE expected ( ClinicsNum INT ); INSERT INTO expected SELECT 2
Saya ingin menarik perhatian Anda bahwa kami hanya memiliki satu kolom di tabel [Diharapkan], meskipun kami memiliki set lengkap di kolom [Sebenarnya].
Ini disebabkan oleh salah satu fitur berguna dari prosedur AssertEqualsTable yang akan kami gunakan untuk verifikasi nilai.
EXEC tSQLt.AssertEqualsTable 'expected', 'actual', 'incorrect number of clinics';
Itu hanya membandingkan kolom-kolom yang disajikan di kedua tabel. Ini sangat nyaman dalam kasus kami karena permintaan yang diuji mengembalikan banyak kolom, masing-masing terhubung dengan logika yang cukup rumit. Kami tidak ingin mengembang kasus uji, jadi fitur ini sangat membantu. Tentu saja, fitur ini adalah pedang bermata dua. Jika [Aktual] diisi melalui SELECT TOP 0 dan pada satu titik kolom yang tidak terduga muncul, kotak uji tersebut tidak akan menangkap ini. Anda harus menulis cek tambahan untuk meliput ini.
Prosedur kembar AssertEqualsTable
Perlu disebutkan bahwa tSQLt berisi 2 prosedur seperti AssertEqualsTable. Mereka adalah AssertEqualsTableSchema dan AssertResultSetsHaveSameMetaData. Yang pertama melakukan hal yang sama seperti AssertEqualsTable tetapi pada metadata tabel. Yang kedua melakukan hal yang sama tetapi pada metadata set hasil.
Contoh # 2: Kendala
Contoh sebelumnya telah menunjukkan kepada kita bagaimana kita dapat menghilangkan kendala. Tetapi bagaimana jika kita perlu memeriksanya? Secara teknis, kendala juga merupakan bagian dari logika, dan mereka dapat dianggap sebagai kandidat untuk dicakup oleh tes.
Pertimbangkan situasi dari contoh sebelumnya. 2 tabel - [Uji Coba] dan [Klinik]; [TrialID] FK:
Mari kita coba menulis test case untuk memeriksanya. Pertama, seperti pada kasus sebelumnya, kami memalsukan tabel:
EXEC tSQLt.FakeTable '[dbo].[Trial]' EXEC tSQLt.FakeTable '[dbo].[Clinic]'
Tujuannya sama - menyingkirkan batasan yang tidak perlu. Kami ingin cek terisolasi tanpa upaya yang tidak semestinya.
Selanjutnya, kami mengembalikan batasan yang ingin kami uji menggunakan ApplyConstraint:
EXEC tSQLt.ApplyConstraint '[dbo].[Clinic]', 'Trial_FK';
Sekarang kami memiliki konfigurasi untuk pemeriksaan. Pemeriksaan itu sendiri adalah bahwa mencoba memasukkan data akan menyebabkan pengecualian. Untuk lulus ujian kasus, kita perlu menangkap pengecualian ini. Penangan pengecualian ExpectException dapat membantu.
EXEC tSQLt.ExpectException @ExpectedMessage = 'The INSERT statement conflicted...', @ExpectedSeverity = 16, @ExpectedState = 0;
Kami dapat mencoba memasukkan yang tidak dapat dimasukkan setelah pengaturan handler.
INSERT INTO [dbo].[Clinic] ([TrialID]) VALUES (1)
Pengecualian ditangkap. Tes lulus.
Prosedur kembar Terapkan Kendala
Cara pengujian pemicu yang diajukan oleh penulis tSQLt mirip dengan kendala pengujian. Kita bisa menggunakan prosedur ApplyTrigger untuk mengembalikan pelatuk ke tabel. Setelah itu, semuanya berjalan seperti pada contoh di atas - mulai pelatuk, periksa hasilnya.
ExpectNoException - antonim ExpectException
Ada prosedur ExpectNoException untuk kasus ketika pengecualian tidak boleh terjadi. Ini bekerja dengan cara yang sama seperti karya ExpectException kecuali bahwa tes gagal jika terjadi pengecualian.
Contoh # 3: Semaphore
Ada beberapa prosedur tersimpan dan layanan windows. Awal eksekusi mereka dapat disebabkan oleh berbagai peristiwa luar. Namun, urutan eksekusi mereka sudah diperbaiki. Jadi, diperlukan untuk menerapkan kontrol akses di sisi DB - mis. Semafor. Dalam kasus kami, semaphore adalah sekelompok prosedur tersimpan yang bekerja bersama.
Mari kita lihat prosedur di dalam semaphore. Kami memiliki 2 tabel - [Proses] dan [ProcStatus]:
Tabel [Proses] berisi daftar proses yang diizinkan untuk dieksekusi. [ProcStatus], jelas, berisi daftar status proses dari tabel sebelumnya.
Jadi, apa yang dilakukan prosedur kami? Pertama, ia melakukan pemeriksaan berikut:
- Kami telah melewati nama proses sebagai salah satu parameter input prosedur. Nama ini dicari di bidang [Nama] pada tabel [Proses].
- Jika nama proses telah ditemukan, itu memeriksa bendera [IsRunable] dari tabel [Proses].
- Jika bendera HIDUP, kami menganggap bahwa prosesnya dapat berjalan. Pemeriksaan terakhir terjadi di tabel [ProcStatus]. Kita perlu memastikan bahwa proses saat ini tidak berjalan, yang berarti tidak adanya catatan tentang proses dengan status "InProg" di tabel [ProcStatus].
Jika semuanya OK dan semua pemeriksaan dilewati, kami menambahkan catatan baru tentang proses kami ke dalam tabel [ProcStatus] dengan status "InProg". ID dari catatan baru ini dikembalikan dengan parameter output ProcStatusId.
Jika ada yang salah, kami mengharapkan yang berikut:
- Email ke administrator sistem dikirim.
- ProcStatusId = -1 dikembalikan.
- Tidak ada catatan [ProcStatus] baru ditambahkan.
Mari kita buat kasus uji untuk memeriksa kasus tidak adanya proses di tabel [Proses].
Kami menggunakan FakeTable lagi. Ini tidak begitu kritis di sini, tetapi bisa nyaman karena:
- Dijamin tidak akan ada data yang bisa mengganggu pelaksanaan test case.
- Pemeriksaan lebih lanjut atas ketidakhadiran data [ProcStatus] baru akan disederhanakan.
EXEC tSQLt.FakeTable 'dbo.Process'; EXEC tSQLt.FakeTable 'dbo.ProcStatus';
Ada prosedur [SendEmail] yang namanya berbicara sendiri. Kita harus menerima teleponnya. tSQLt menyarankan menggunakan tiruan SpyProcedure untuk itu.
EXEC tSQLt.SpyProcedure 'dbo.SendEmail'
SpyProcedure melakukan hal berikut:
- Membuat tabel dengan nama yang terlihat seperti [dbo]. [ProcedureName_SpyProcedureLog]
- Sama seperti FakeTable, menggantikan prosedur asli dengan yang dibuat secara otomatis, dengan nama yang sama, tetapi dengan log masuk di dalamnya. Anda juga dapat menambahkan logika Anda sendiri ke prosedur yang dibuat jika diperlukan.
Tidak sulit untuk menebak bahwa log direkam ke tabel [dbo]. [SendEmail_SpyProcedureLog]. Tabel ini berisi kolom [_ID_] yang untuk nomor urut panggilan. Kolom berikutnya diberi nama setelah parameter dilewatkan ke prosedur dan digunakan untuk mengumpulkan mereka, jadi, nilai parameter dapat diverifikasi juga.
Hal terakhir yang perlu kita lakukan sebelum pemanggilan semaphore adalah membuat variabel untuk menyimpan nilai [ProcStatusId] (lebih tepatnya, -1, karena catatan tidak akan ditambahkan).
DECLARE @ProcStatusId BIGINT;
Kami menyebutnya semafor:
EXEC dbo.[Semaphore_JobStarter] 'SomeProcess', @ProcStatusId OUTPUT; -- here we get -1
Sekarang kami memiliki semua data yang diperlukan untuk pemeriksaan. Mari kita mulai dari mengecek
bahwa pesan telah dikirim.
IF NOT EXISTS ( SELECT * FROM dbo.SendEmail_SpyProcedureLog) EXEC tSQLt.Fail 'SendEmail has not been run.';
Dalam hal ini, kami tidak memeriksa parameter yang diteruskan dan menguji fakta pengiriman saja. Saya ingin menarik perhatian Anda pada prosedur Gagal. Ini memungkinkan kita untuk "secara resmi" gagal ujian. Jika Anda perlu membangun konstruksi yang canggih, Gagal dapat membantu.
Sekarang kami memeriksa tidak adanya catatan di tabel [ProcStatus] dengan prosedur AssertEmptyTable.
EXEC tSQLt.AssertEmptyTable 'dbo.ProcStatus';
Di sinilah FakeTable yang kami gunakan di awal membantu kami. Dengan itu, kita dapat mengharapkan tabel kosong dan menguji menggunakan satu baris kode. Cara yang benar untuk memeriksanya tanpa pemalsuan tabel adalah membandingkan jumlah baris sebelum dan sesudah eksekusi prosedur, dan itu akan membutuhkan lebih banyak tindakan.
Kita dapat dengan mudah memeriksa ProcStatusId = -1 kesetaraan dengan AssertEquals.
EXEC tSQLt.AssertEquals -1, @ProcStatusId, 'Wrong ProcStatusId.';
AssertEquals adalah minimalis. Itu hanya membandingkan 2 nilai, tidak ada yang luar biasa.
Prosedur kembar AssertEquals
Kami memiliki prosedur perbandingan nilai berikut:
- AssertEquals
- AssertNotEquals
- AssertEqualsString
- Tegas
Nama-nama itu sudah jelas, saya pikir. Satu-satunya prosedur yang ingin saya tekankan adalah AssertEqualsString. Ini prosedur yang didedikasikan untuk verifikasi nilai string. Mengapa kita perlu satu prosedur lagi, mengingat diberikan AssertEquals universal? Masalahnya adalah, AssertEquals / AssertNotEquals / AssertLike bekerja dengan tipe SQL_VARIANT. NVARCHAR (MAX) tidak termasuk dalam SQL_VARIANT, jadi pengembang tSQLt harus membuat prosedur tambahan.
Fungsi palsu
Dengan cepat, kita dapat memanggil FakeFunction prosedur yang mirip dengan SpyProcedure. Palsu ini memungkinkan mengganti fungsi apa pun dengan yang lebih sederhana. Karena fungsi SQL Server berfungsi seperti tabung pasta gigi (hasilnya dikembalikan melalui satu-satunya "lubang"), secara teknis tidak mungkin untuk menerapkan fungsi logging. Penggantian logika batin adalah satu-satunya cara yang tersedia.
Perangkap
Saya ingin memberi tahu Anda tentang beberapa jebakan yang dapat Anda hadapi selama penggunaan tSQLt. Dalam hal ini "jebakan" berarti beberapa masalah yang disebabkan oleh pembatasan SQL Server dan / atau yang tidak mungkin diselesaikan oleh pengembang kerangka kerja.
Rollback dan dooming transaksi
Masalah pertama dan utama yang dihadapi oleh tim kami adalah kemunduran transaksi dan malapetaka. SQL Server tidak dapat mengembalikan transaksi bersarang secara terpisah. Itu selalu memutar kembali semua transaksi hingga ke bagian terluar. Mempertimbangkan bahwa tSQLt membungkus setiap pengujian ke dalam transaksi yang terpisah, ini dapat menjadi masalah karena kemunduran di dalam prosedur yang tersimpan dapat memecah suatu percobaan dengan kesalahan eksekusi non-deskriptif.
Sebagai solusinya, kami menggunakan savepoints. Idenya sederhana. Pada awalnya, kami memeriksa apakah kami melakukan transaksi atau tidak. Jika ya, kami mengira itu adalah transaksi tSQLt dan menempatkan savepoint, jadi kami akan memutar kembali jika diperlukan. Jika tidak, kami memulai transaksi baru. Bahkan, kami tidak mengizinkan bersarang.
Masalahnya rumit oleh malapetaka transaksi - itu bisa terjadi jika pengecualian dilemparkan. Transaksi yang gagal tidak dapat dilakukan seperti halnya digulung kembali ke savepoint, jadi kita harus menggulungnya kembali ke transaksi terluar lagi.
Mempertimbangkan poin-poin yang dijelaskan di atas, kita harus menggunakan struktur berikut:
DECLARE @isNestedTransaction BIT = CASE WHEN @@trancount > 0 THEN 'true' ELSE 'false' END; BEGIN TRY IF @isNestedTransaction = 'false' BEGIN TRANSACTION ELSE SAVE TRANSACTION SavepointName;
Mari kita tinjau sepotong demi sepotong kode. Pertama, kita perlu menentukan apakah kita melakukan transaksi atau tidak.
DECLARE @isNestedTransaction BIT = CASE WHEN @@trancount > 0 THEN 'true' ELSE 'false' END;
Setelah menurunkan flag @isNestedTransaction, kita dapat memulai blok TRY dan mengatur savepoint atau memulai transaksi tergantung pada situasinya.
BEGIN TRY IF @isNestedTransaction = 'false' BEGIN TRANSACTION ELSE SAVE TRANSACTION SavepointName;
Setelah melakukan sesuatu yang bermanfaat, kami berkomitmen hasilnya jika itu adalah prosedur "nyata".
Tentu saja, jika ini adalah uji coba, kita tidak perlu melakukan apa pun. tSQLt akan mengembalikan perubahan pada akhirnya secara otomatis.
Jika ada yang tidak beres dan kami masuk ke blok CATCH, kami perlu menentukan apakah transaksi tersebut sesuai atau tidak.
BEGIN CATCH DECLARE @isCommitable BIT = CASE WHEN XACT_STATE() = 1 THEN 'true' ELSE 'false' END;
Kita dapat kembali ke savepoint hanya jika:
- Transaksi berkomitmen
- Ini adalah uji coba, jadi, savepoint ada.
Dalam semua kasus lain, kita harus memutar kembali seluruh transaksi.
IF @isCommitable = 'true' AND @isNestedTransaction = 'true' ROLLBACK TRANSACTION SavepointName; ELSE ROLLBACK; THROW; END CATCH;
Ya, sayangnya, jika kami telah mencapai kondisi transaksi yang tidak dapat dilakukan selama uji coba, kami masih mendapatkan kesalahan eksekusi.
Faketable dan masalah kunci asing
Mari kita tinjau tabel [Uji Coba] dan [Klinik] yang familier
Kami ingat tentang [TrialID] FK. Masalah apa yang bisa ditimbulkannya? Dalam contoh di atas kami menerapkan FakeTable di kedua tabel. Jika kami menggunakannya hanya di salah satu dari mereka, kami akan mencapai pengaturan berikut:
Jadi, upaya untuk memasukkan catatan ke dalam [Klinik] dapat gagal bahkan jika kami telah menyiapkan data dalam versi [Trial] palsu.
[dbo].[Test_FK_Problem] failed: (Error) The INSERT statement conflicted with the FOREIGN KEY constraint "Trial_Fk". The conflict occurred in database "HabrDemo", table "dbo.tSQLt_tempobject_ba8f36353f7a44f6a9176a7d1db02493", column 'TrialID'.[16,0]{Test_FK_Problem,14}
Kesimpulan: palsu semua atau tidak ada. Jika tidak ada, Anda harus menyiapkan DB dengan semua data uji yang diperlukan.
SpyProcedure pada prosedur sistem
Sayangnya, kami tidak dapat memata-matai prosedur sistem:
[HabrDemo].[test_test] failed: (Error) Cannot use SpyProcedure on sys.sp_help because the procedure does not exist[16,10] {tSQLt.Private_ValidateProcedureCanBeUsedWithSpyProcedure,7}
Dalam contoh semafor, kami melacak panggilan prosedur [SendEmail], yang dibuat oleh pengembang kami. Dalam hal ini, itu tidak diperlukan hanya dengan pengujian. Itu perlu untuk membuat prosedur terpisah karena diperlukan untuk menyiapkan beberapa data sebelum mengirim. Tetapi Anda harus siap secara mental untuk menulis prosedur interlayer untuk memenuhi tujuan pengujian.
Pro
Instalasi cepat
Instalasi tSQLt terdiri dari 2 langkah dan membutuhkan waktu sekitar 2 menit. Anda perlu mengaktifkan CLR jika saat ini tidak aktif dan menjalankan skrip SQL tunggal. Itu saja: sekarang Anda dapat menambahkan kelas tes pertama Anda dan menulis kasus uji.
Belajar cepat
tSQLt mudah dipelajari. Butuh sedikit lebih dari satu hari kerja untuk saya. Saya bertanya pada kolega dan sepertinya butuh sekitar 1 hari kerja untuk orang lain juga. Saya ragu itu bisa memakan waktu lebih lama.
Integrasi CI cepat
Butuh sekitar 2 jam untuk mengatur integrasi CI pada proyek kami. Waktu dapat bervariasi, tentu saja, tetapi itu bukan masalah secara umum, dan dapat dilakukan dengan cepat.
Seperangkat instrumen yang luas
Ini subjektif, tetapi dalam pandangan saya fungsionalitas tSQLt kaya dan bagian terbesar dari kebutuhan dapat dicakup olehnya. Jika itu tidak cukup, Anda selalu dapat menggunakan prosedur Gagal untuk kasus langka dan canggih.
Dokumentasi yang mudah
Panduan resmi nyaman dan konsisten. Anda dapat dengan mudah memahami penggunaan tSQLt dalam waktu singkat meskipun itu merupakan alat pengujian unit pertama Anda.
Hapus keluaran
Output tes dapat diambil dalam format teks ilustrasi:
[tSQLtDemo].[test_error_messages] failed: (Failure) Expected an error to be raised. [tSQLtDemo].[test_tables_comparison] failed: (Failure) useful and descriptive error message Unexpected/missing resultset rows! |_m_|Column1|Column2| +---+-------+-------+ |< |2 |Value2 | |= |1 |Value1 | |= |3 |Value3 | |> |2 |Value3 | +----------------------+ |Test Execution Summary| +----------------------+ |No|Test Case Name |Dur(ms)|Result | +--+------------------------------------+-------+-------+ |1 |[tSQLtDemo].[test_constraint] | 83|Success| |2 |[tSQLtDemo].[test_trial_view] | 83|Success| |3 |[tSQLtDemo].[test_error_messages] | 127|Failure| |4 |[tSQLtDemo].[test_tables_comparison]| 147|Failure| ----------------------------------------------------------------------------- Msg 50000, Level 16, State 10, Line 1 Test Case Summary: 4 test case(s) executed, 2 succeeded, 2 failed, 0 errored. -----------------------------------------------------------------------------
Ini juga dapat diturunkan dari DB (dapat diklik) ...
... atau bahkan sebagai XML.
<?xml version="1.0" encoding="UTF-8"?> <testsuites> <testsuite id="1" name="tSQLtDemo" tests="3" errors="0" failures="1" timestamp="2019-06-22T16:46:06" time="0.433" hostname="BLAHBLAHBLAH\SQL2017" package="tSQLt"> <properties /> <testcase classname="tSQLtDemo" name="test_constraint" time="0.097" /> <testcase classname="tSQLtDemo" name="test_error_messages" time="0.153"> <failure message="Expected an error to be raised." type="tSQLt.Fail" /> </testcase> <testcase classname="tSQLtDemo" name="test_trial_view" time="0.156" /> <system-out /> <system-err /> </testsuite> </testsuites>
Format terakhir memungkinkan integrasi CI tanpa masalah. Secara khusus, kami menggunakan tSQLt bersama dengan Atlassian Bamboo.
Mendukung redgate
Sebagai salah satu pro, saya dapat menyebutkan dukungan dari salah satu penyedia alat DBA terbesar - RedGate. Plugin SQL Server Management Studio mereka bernama SQL Test bekerja dengan tSQLt dari awal. Selain itu, RedGate membantu pengembang utama tSQLt dengan dev-environment, menurut kata-katanya dalam
grup Google .
Cons
Tidak ada meja sementara yang dipalsukan
tSQLt tidak mengizinkan memalsukan tabel sementara. Pikiran, jika perlu, Anda dapat menggunakan addon tidak resmi. Sayangnya, addon ini hanya berfungsi dengan SQL Server 2016+.
Bekerja dengan DB luar
tSQLt dirancang untuk bekerja dengan kode dalam DB yang sama di mana kerangka kerja diinstal. Jadi, tidak mungkin untuk menggunakannya dengan DB luar. Setidaknya, palsu tidak akan bekerja.
CREATE PROCEDURE [tSQLtDemo].[test_outer_db] AS BEGIN SELECT TOP 10 * FROM [AdventureWorks2017].[Person].[Password] EXEC tSQLt.FakeTable '[AdventureWorks2017].[Person].[Password]' SELECT TOP 10 * FROM [AdventureWorks2017].[Person].[Password] END
Kelihatannya asersi berfungsi, tetapi kemampuan kerja mereka tidak dijamin, tentu saja.
CREATE PROCEDURE [tSQLtDemo].[test_outer_db_assertions] AS BEGIN SELECT TOP 1 * INTO
Bug dokumentasi
Meskipun saya sebutkan di atas bahwa panduannya nyaman dan konsisten, dokumentasi memiliki beberapa masalah. Ini berisi bagian-bagian yang sudah ketinggalan zaman.
Contoh 1.
"Panduan mulai cepat" menyarankan mengunduh kerangka kerja dari SourceForge.
Mereka pindah dari SourceForge
sejauh tahun 2015 .
Contoh 2.
Panduan ApplyConstraint menggunakan desain besar dengan prosedur Gagal di dalam contoh penangkapan pengecualian. Ini dapat diganti dengan kode sederhana dan jelas menggunakan ExpectException.
CREATE PROCEDURE ConstraintTests.[test ReferencingTable_ReferencedTable_FK prevents insert of orphaned rows] AS BEGIN EXEC tSQLt.FakeTable 'dbo.ReferencedTable'; EXEC tSQLt.FakeTable 'dbo.ReferencingTable'; EXEC tSQLt.ApplyConstraint 'dbo.ReferencingTable','ReferencingTable_ReferencedTable_FK'; DECLARE @ErrorMessage NVARCHAR(MAX); SET @ErrorMessage = ''; BEGIN TRY INSERT INTO dbo.ReferencingTable ( id, ReferencedTableId ) VALUES ( 1, 11 ) ; END TRY BEGIN CATCH SET @ErrorMessage = ERROR_MESSAGE(); END CATCH IF @ErrorMessage NOT LIKE '%ReferencingTable_ReferencedTable_FK%' BEGIN EXEC tSQLt.Fail 'Expected error message containing ''ReferencingTable_ReferencedTable_FK'' but got: ''',@ErrorMessage,'''!'; END END GO
Dan ini diharapkan, karena ...
Pengabaian sebagian
Ada jeda yang berkepanjangan dalam pengembangan dari awal 2016 hingga Juni 2019. Ya, sayangnya, alat ini sebagian ditinggalkan. Pengembangan perlahan dimulai pada 2019,
menurut GitHub . Meskipun Grup Google resmi
memiliki utas di mana Sebastian, pengembang tSQLt utama, ditanya tentang masa depan proyek. Pertanyaan terakhir ditanyakan pada 2 Maret 2019, tanpa jawaban.
Masalah SQL Server 2017
Instalasi tSQLt mungkin memerlukan beberapa tindakan tambahan jika Anda menggunakan SQL Server 2017. Microsoft menerapkan perubahan keamanan pertama sejak 2012 dalam rilis ini. Bendera tingkat server "CLR ketat keamanan" telah ditambahkan. Bendera ini melarang pembuatan rakitan yang tidak ditandatangani (bahkan AMAN). Deskripsi mendetail pantas mendapatkan artikel yang terpisah (dan, untungnya, kami
sudah memiliki artikel yang bagus; lihat juga artikel berikut dalam urutan. Bersiaplah secara mental untuk ini.
Tentu saja, saya bisa mengaitkan masalah ini dengan "jebakan", tetapi masalah ini dapat diatasi oleh pengembang tSQLt.
Masalah GitHub telah muncul . Namun, itu belum terselesaikan sejak Oktober 2017.
Alternatif (±) untuk DBMS lainnya
tSQLt bukan sejenis. Meskipun Anda tidak dapat menggunakannya dalam DBMS lain karena nuansa CLR dan T-SQL, Anda masih dapat menemukan sesuatu yang serupa. Perlu disebutkan bahwa alternatif ini tidak terlalu dekat dengan tSQLt, jadi maksud saya pendekatan bertenaga SQL.
Sebagai contoh, pengguna PostgreSQL dapat mencoba
pgTAP . Ini adalah alat yang dikembangkan dengan baik dan secara aktif menggunakan PL / pgSQL asli untuk pengujian dan format output TAP. Alat serupa
MyTap dapat membantu Anda dengan tes di bawah MySQL. Kerangka kerja ini sedikit kurang fungsional daripada pgTAP tetapi masih bisa bermanfaat. Dan itu dalam pengembangan aktif juga. Jika Anda adalah pengguna Oracle yang bahagia, Anda memiliki kesempatan untuk menggunakan alat
utPLSQL yang sangat kuat. Ini berkembang sangat aktif dan menyediakan sejumlah besar fitur.
Kesimpulan
Saya ingin menyampaikan 2 ide:
Yang pertama: kegunaan pengujian kode DB. Tidak penting jika Anda menggunakan SQL Server, Oracle, MySQL, atau yang lainnya. Jika DB Anda berisi logika yang belum diuji, Anda mengambil risiko. Karena semua bug lain dalam semua kode lainnya, bug kode DB dapat merusak produk dan perusahaan yang menyediakannya.
Yang kedua: pilihan alat. Bagi mereka yang bekerja dengan SQL Server, tSQLt, bahkan jika itu bukan pemenang 100%, tentu layak mendapat perhatian. Meskipun perkembangannya lambat dan beberapa masalah, ini masih merupakan kerangka kerja praktis yang bisa membuat pekerjaan Anda jauh lebih mudah.
Sumber yang membantu saya (daftar tidak lengkap)