Heim > Artikel > Backend-Entwicklung > Vereinfachen Sie Go-Integrationstests mit gofacto: Eine leistungsstarke Fabrik für Scheindaten
Das Schreiben von Integrationstests mit Datenbanken ist für die Entwicklung von Webanwendungen von entscheidender Bedeutung, da es das Vertrauen in unseren Code stärkt und sicherstellt, dass unsere Anwendung wie erwartet funktioniert. Allerdings kann die Vorbereitung von Scheindaten für diese Tests eine Herausforderung darstellen, insbesondere in Go, wo es für diese Aufgabe keinen integrierten Ansatz oder keine Standardbibliothek gibt. In diesem Artikel wird die Gofacto-Bibliothek vorgestellt, die den Prozess der Erstellung von Scheindaten und deren Einfügen in Datenbanken für Go-Integrationstests vereinfacht.
gofacto ist eine Go-Bibliothek, die die Erstellung und das Einfügen von Scheindaten in Datenbanken vereinfacht. Es bietet einen intuitiven Ansatz zum Definieren von Datenschemata und zum effizienten Umgang mit Datenbankeinfügungen. Mit gofacto können Entwickler Testdaten schnell vorbereiten, ohne umfangreiche Boilerplate-Codes schreiben zu müssen, sodass sie sich auf das Schreiben aussagekräftiger Tests konzentrieren können.
Sehen wir uns an, was wir normalerweise tun, wenn wir Integrationstests mit Datenbanken in Go schreiben. Angenommen, wir haben eine Tabelle mit dem Namen „Benutzer“ in der Datenbank und sie weist das folgende Schema auf:
CREATE TABLE users ( id INT PRIMARY KEY, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL );
Angenommen, wir möchten eine Funktion namens getUserByID testen, die einen Benutzer anhand seiner ID aus der Benutzertabelle abruft. Um diese Funktion zu testen, müssen wir vor dem Testen dieser Funktion einige Scheindaten in der Datenbank vorbereiten. So machen wir es normalerweise:
type User struct { ID int Gender string Name string Email string } // build and insert mock user mockUser := User{ ID: 1, Gender: "male", Name: "Alice", Email: "aaa@gmail.com", } err := insertToDB(mockUser) // action result, err := getUserByID(mockUser.ID) // assertion // ...
insertToDB ist eine Funktion, die Scheindaten in die Datenbank einfügt. Es kann sehr komplex sein, wenn wir unformatierte SQL-Abfragen verwenden.
Dieser Ansatz erscheint machbar, da das Schema einfach ist und wir uns nur mit einer Tabelle befassen.
Sehen wir uns den Fall an, wenn wir uns mit zwei Tabellen, Benutzern und Beiträgen, befassen. Jeder Benutzer kann mehrere Beiträge haben und die Beziehung zwischen den Tabellen wird durch das Feld „user_id“ in der Beitragstabelle hergestellt.
CREATE TABLE posts ( id INT PRIMARY KEY, user_id INT NOT NULL, title VARCHAR(255) NOT NULL, content TEXT NOT NULL, FOREIGN KEY (user_id) REFERENCES users(id) );
Angenommen, wir möchten eine Funktion namens getPostsByUserID testen, die alle Beiträge nach der ID eines Benutzers aus der Beitragstabelle abruft.
type Post struct { ID int UserID int Title string Content string } // build and insert mock user mockUser := User{ ID: 1, Gender: "male", Name: "Alice", Email: "aaa@gmail.com", } err := insertToDB(mockUser) // build and insert mock post mockPost1 := Post{ ID: 1, UserID: mockUser.ID, // manually set the foreign key Title: "Post 1", Content: "Content 1", } err = insertToDB(mockPost1) // build and insert mock post mockPost2 := Post{ ID: 2, UserID: mockUser.ID, // manually set the foreign key Title: "Post 2", Content: "Content 2", } err = insertToDB(mockPost2) // action result, err := getPostsByUserID(mockUser.ID) // assertion // ...
Wir erstellen zunächst einen Benutzer und erstellen dann zwei Beiträge für diesen Benutzer. Im Vergleich zum vorherigen Beispiel wird es komplexer, da wir uns mit zwei Tabellen befassen und die Beziehung zwischen ihnen herstellen.
Was ist, wenn wir mehrere Beiträge mit unterschiedlichen Benutzern erstellen möchten?
Wir müssen für jeden Beitrag einen Benutzer erstellen und dafür ist mehr Code erforderlich.
// build and insert mock user mockUser1 := User{ ID: 1, Gender: "male", Name: "Alice", Email: "aaa@gmail.com", } err := insertToDB(mockUser1) // build and insert mock user mockUser2 := User{ ID: 2, Gender: "female", Name: "Bob", Email: "bbb@gmail.com", } err = insertToDB(mockUser2) // build and insert mock post mockPost1 := Post{ ID: 1, UserID: mockUser1.ID, // manually set the foreign key Title: "Post 1", Content: "Content 1", } err = insertToDB(mockPost1) // build and insert mock post mockPost2 := Post{ ID: 2, UserID: mockUser2.ID, // manually set the foreign key Title: "Post 2", Content: "Content 2", } err = insertToDB(mockPost2) // action result, err := getPostsByUserID(mockUser1.ID) // assertion // ...
Es wird immer komplexer und fehleranfälliger, wenn wir mehrere Scheindaten mit unterschiedlichen Benutzern und Beiträgen erstellen müssen.
Beachten Sie auch, dass wir nur zu Demonstrationszwecken ein einfaches Schema verwenden. Der Code wird in den realen Anwendungen komplexer sein.
In den obigen Beispielen gibt es einige Probleme:
Jetzt wollen wir sehen, wie die Gofacto-Bibliothek uns helfen kann, die oben genannten Probleme zu lösen und den gesamten Prozess zu vereinfachen.
Sehen wir uns das erste Beispiel mit der Benutzertabelle an.
// initialize a factory with User struct (also use `WithDB` to pass the database connection) f := gofacto.New(User{}).WithDB(db) // build and insert mock user mockUser, err := f.Build(ctx).Insert() // action result, err := getUserByID(mockUser.ID) // assertion // ...
Um gofacto zu verwenden, verwenden wir zunächst die Funktion „Neu“, um eine neue Factory mit „Benutzer“ zu initialisieren. Da wir Daten in die Datenbank einfügen müssen, verwenden wir WithDB, um die Datenbankverbindung an die Fabrik weiterzuleiten.
Dann verwenden wir die Build-Funktion, um die Scheindaten zu erstellen. Die Funktion „Einfügen“ fügt die Scheindaten in die Datenbank ein und gibt die Scheindaten zurück, die mit der automatisch inkrementierten ID in die Datenbank eingefügt wurden.
Beachten Sie, dass alle Felder der Scheindaten standardmäßig zufällig generiert werden. In diesem Fall ist das in Ordnung, da uns der Wert der Felder egal ist.
Falls wir den Wert der Felder angeben möchten, können wir die Funktion „Überschreiben“ verwenden, um den Wert der Felder festzulegen.
mockUser, err := f.Build(ctx).Overwrite(User{Gender: "male"}).Insert() // mockUser.Gender == "male"
Bei Verwendung der Überschreibfunktion müssen wir nur die Felder angeben, die wir überschreiben möchten. Die anderen Felder werden wie gewohnt zufällig generiert.
Sehen wir uns den Fall an, in dem wir mehrere Beiträge mit einem Benutzer erstellen möchten.
Damit gofacto die Beziehung zwischen den Tabellen kennt, müssen wir die richtigen Tags in der Struktur definieren.
type Post struct { ID int UserID int `gofacto:"foreignKey,struct:User"` Title string Content string }
The tag tells gofacto that the UserID field is a foreign key that references the ID field of the User struct.
Now, we can create multiple posts with one user easily.
mockUser := User{} mockPosts, err := f.BuildList(ctx, 2).WithOne(&mockUser).Insert() // must pass pointer to the struct to `WithOne` // mockPosts[0].UserID == mockUser.ID // mockPosts[1].UserID == mockUser.ID // action result, err := getPostsByUserID(mockUser.ID) // assertion // ...
In order to create multiple posts, we use BuildList function with the number of posts that we want to create. Then, we use WithOne function to specify that all the posts belong to one user. The Insert function returns a list of posts that have been inserted into the database with the auto-incremented ID.
gofacto library makes sure all the fields are correctly set randomly, and the relationship between the tables is correctly established.
Let's see the case where we want to create multiple posts with different users.
mockUser1 := User{} mockUser2 := User{} mockPosts, err := f.BuildList(ctx, 2).WithMany([]interface{}{&mockUser1, &mockUser2}).Insert() // mockPosts[0].UserID == mockUser1.ID // mockPosts[1].UserID == mockUser2.ID // action result, err := getPostsByUserID(mockUser1.ID) // assertion // ...
We use WithMany function to specify that each post is associated with a different user.
We've seen how gofacto simplifies writing integration tests with databases in Go. It reduces boilerplate code and makes it easier to prepare mock data with multiple tables and establish relationships between them. Most importantly, gofacto abstracts away the complexity of preparing mock data, allowing developers to focus on writing meaningful tests. To start using gofacto in your Go projects, visit the GitHub repository for installation instructions and more detailed documentation.
As a new library developer, I'd love to hear your thoughts on gofacto! Any feedback, advice or criticism is appreciated. If you use it in your Go projects, please share your experience. Found a bug or have an idea? Open an issue on the gofacto GitHub repo. Want to contribute code? Pull requests are welcome! Your feedback and contributions will help improve gofacto and benefit the Go community. Thanks for checking it out!
Das obige ist der detaillierte Inhalt vonVereinfachen Sie Go-Integrationstests mit gofacto: Eine leistungsstarke Fabrik für Scheindaten. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!