Tes Integrasi Paralel Postgresql di Aplikasi GO


Tes integrasi adalah salah satu level dari piramida pengujian . Biasanya mereka memerlukan lebih banyak waktu, karena di dalamnya kita tidak mengganti apa pun dengan simulasi komponen nyata. Untuk mengurangi waktu pengujian semacam itu, kita dapat menjalankannya secara paralel. Di sini saya akan secara khusus berbicara tentang tes semacam itu untuk Postgresql.

Idealnya, setiap tes harus independen, sehingga tidak akan saling mempengaruhi. Dengan kata lain, setiap fungsi tes memiliki statusnya sendiri. Ini pertanda baik untuk menggunakan tes paralel. Untuk mendapatkan set data pribadi saya untuk setiap fungsi tes, saya membuat fungsi yang, ketika memulai tes, membuat sirkuit sementara, memuat data ke dalamnya dan menghancurkan sirkuit setelah tes selesai. Setiap skema yang dibuat berisi hash dalam nama untuk mencegah konflik nama.


Fungsi pembantu


Mari kita mulai dengan fungsi pembantu untuk menampilkan kesalahan dalam tes. Saya mengambil fungsi pembantu Ben Johnson (Ben Johnson), yang membantu saya menyimpan beberapa baris kode dan membuat kesalahan saya lebih jelas dan terperinci.

Uji data


Untuk menjalankan uji integrasi database, data uji harus disediakan. Alat uji Go memiliki dukungan yang baik untuk memuat data uji dari file. Pertama, pergi buat folder lompatan yang disebut "testdata". Kedua, ketika Anda menjalankan "go test", itu mengubah folder saat ini ke folder paket. Ini memungkinkan Anda untuk menggunakan jalur relatif ke folder testdata untuk memuat kumpulan data uji.

Membuat koneksi database untuk pengujian


package database import ( "math/rand" "strconv" "testing" "time" _ "github.com/lib/pq" "database/sql" ) const ( dbPort = 5439 dbUser = "postgres" dbPassword = "postgres" dbName = "test" ) func CreateTestDatabase(t *testing.T) (*sql.DB, string, func()) { connectionString := fmt.Sprintf("port=%d user=%s password=%s dbname=%s sslmode=disable", dbPort, dbUser, dbPassword, dbName) db, dbErr := sql.Open("postgres", connectionString) if dbErr != nil { t.Fatalf("Fail to create database. %s", dbErr.Error()) } rand.Seed(time.Now().UnixNano()) schemaName := "test" + strconv.FormatInt(rand.Int63(), 10) _, err := db.Exec("CREATE SCHEMA " + schemaName) if err != nil { t.Fatalf("Fail to create schema. %s", err.Error()) } return db, schemaName, func() { _, err := db.Exec("DROP SCHEMA " + schemaName + " CASCADE") if err != nil { t.Fatalf("Fail to drop database. %s", err.Error()) } } } 


Memanggil "CreateTestDatabase" untuk membuat koneksi ke database pengujian dan membuat skema data baru untuk pengujian. Fungsi ini mengembalikan koneksi database, nama skema yang dibuat, dan fungsi purge untuk menghapus skema ini. Untuk tes, lebih baik gagal tes daripada mengembalikan kesalahan ke pemanggil. (Catatan: Kembalinya fungsi pembersihan didasarkan pada Pengujian Lanjutan dengan Bicara dengan Mitchell Hashimoto ).

Unduh Test Dataset


Saya menggunakan file ".sql". Satu (1) sql berisi data untuk satu (1) tabel. Ini termasuk membuat tabel dan mengisinya dengan data. Semua file sql disimpan di folder "testdata". Berikut ini adalah contoh file sql.

 CREATE TABLE book ( title character varying(50), author character varying(50) ); INSERT INTO book VALUES ('First Book','First Author'), ('Second Book','Second Author') ; 

Dan inilah bagian yang rumit. Karena setiap fungsi berjalan dalam skema data uniknya sendiri, kami tidak bisa hanya menjalankan (menulis) kueri dalam file sql ini. Kita harus menentukan skema sebelum nama tabel untuk membuat tabel atau memasukkan data ke skema sementara yang diinginkan. Misalnya, buku CREATE TABLE ... harus ditulis sebagai CREATE TABLE uniqueschema.book ... dan buku INSERT INTO ... perlu diubah menjadi INSERT INTO uniqueschema.book .... Saya menggunakan ekspresi reguler untuk memodifikasi kueri sebelum mengeksekusi. Berikut adalah kode unduhan data uji:

 package datalayer import ( "bufio" "fmt" "io" "os" "regexp" "testing" "database/sql" "github.com/Hendra-Huang/databaseintegrationtest/testingutil" //     (   ,  79) ) //        var schemaPrefixRegexps = [...]*regexp.Regexp{ regexp.MustCompile(`(?i)(^CREATE SEQUENCE\s)(["\w]+)(.*)`), regexp.MustCompile(`(?i)(^CREATE TABLE\s)(["\w]+)(\s.+)`), regexp.MustCompile(`(?i)(^ALTER TABLE\s)(["\w]+)(\s.+)`), regexp.MustCompile(`(?i)(^UPDATE\s)(["\w]+)(\s.+)`), regexp.MustCompile(`(?i)(^INSERT INTO\s)(["\w]+)(\s.+)`), regexp.MustCompile(`(?i)(^DELETE FROM\s)(["\w]+)(.*)`), regexp.MustCompile(`(?i)(.+\sFROM\s)(["\w]+)(.*)`), regexp.MustCompile(`(?i)(\sJOIN\s)(["\w]+)(.*)`), } //      func addSchemaPrefix(schemaName, query string) string { prefixedQuery := query for _, re := range schemaPrefixRegexps { prefixedQuery = re.ReplaceAllString(prefixedQuery, fmt.Sprintf("${1}%s.${2}${3}", schemaName)) } return prefixedQuery } func loadTestData(t *testing.T, db *sql.DB, schemaName string, testDataNames ...string) { for _, testDataName := range testDataNames { file, err := os.Open(fmt.Sprintf("./testdata/%s.sql", testDataName)) testingutil.Ok(t, err) reader := bufio.NewReader(file) var query string for { line, err := reader.ReadString('\n') if err == io.EOF { break } testingutil.Ok(t, err) line = line[:len(line)-1] if line == "" { query = addSchemaPrefix(schemaName, query) _, err := db.Exec(query) testingutil.Ok(t, err) query = "" } query += line } file.Close() } } 


Uji penciptaan


Sebelum memulai setiap pengujian, database pengujian akan dibuat dengan nama unik untuk skema dan pelaksanaan fungsi pembersihan akan tertunda untuk menghapus skema ini. Nama skema akan dimasukkan ke dalam permintaan dalam ujian. Yang paling penting dalam implementasi ini adalah bahwa koneksi ke database harus dapat disesuaikan untuk mengubah koneksi dari database nyata ke koneksi ke database pengujian. Tambahkan "t.Paralel ()" di awal setiap fungsi tes untuk menunjukkan kepada lingkungan pengujian perlunya menjalankan tes ini secara paralel.
Di bawah ini adalah kode lengkap:

 //            "integration" (. build flags) // +build integration package datalayer import ( "context" "testing" "github.com/Hendra-Huang/databaseintegrationtest/database" "github.com/Hendra-Huang/databaseintegrationtest/testingutil" ) func TestInsertBook(t *testing.T) { t.Parallel() db, schemaName, cleanup := database.CreateTestDatabase(t) defer cleanup() loadTestData(t, db, schemaName, "book") // will load data which the filename is book title := "New title" author := "New author" // those 2 lines code below are not a good practice // but it is intentional to keep the focus only on integration test part // the important part is database connection has to be configurable insertBookQuery = addSchemaPrefix(schemaName, insertBookQuery) // override the query and add schema to the query err := InsertBook(context.Background(), db, title, author) // will execute insertBookQuery with the provided connection testingutil.Ok(t, err) } func TestGetBooks(t *testing.T) { t.Parallel() db, schemaName, cleanup := database.CreateTestDatabase(t) defer cleanup() loadTestData(t, db, schemaName, "book") getBooksQuery = addSchemaPrefix(schemaName, getBooksQuery) books, err := GetBooks(context.Background(), db) testingutil.Ok(t, err) testingutil.Equals(t, 2, len(books)) } 


Catatan: Di bawah "TestGetBooks," saya berasumsi bahwa kueri akan mengembalikan 2 buku, sebagai Saya telah memberikan begitu banyak data uji yang diatur dalam "testdata / book.sql" meskipun ada tes masukkan di atas. Jika kami tidak berbagi sirkuit di antara dua tes, "TestGetBooks" akan gagal, karena sekarang 3 baris dalam tabel, 2 dari tes, 1 dari sisipan tes di atas. Ini adalah keuntungan dari rangkaian terpisah untuk pengujian - datanya independen, dan oleh karena itu pengujian independen satu sama lain.

Contoh proyek yang saya posting di sini github . Anda dapat menyalinnya sendiri, menjalankan tes dan melihat hasilnya.

Kesimpulan


Untuk proyek saya, pendekatan ini mengurangi waktu pengujian hingga 40-50%, dibandingkan dengan tes berurutan. Keuntungan lain dari menjalankan tes secara paralel adalah bahwa kita dapat menghindari beberapa kesalahan yang dapat terjadi ketika aplikasi memproses beberapa tindakan kompetitif.

Selamat mencoba.

- Gambar dari medium.com/kongkow-it-medan/parallel-database-integration-test-on-go-application-8706b150ee2e

Source: https://habr.com/ru/post/id466459/


All Articles