Top Go-Module: Schreiben von Unit-Tests mit作证

更新:上午1点。Mai 2021 wd das zentrale Repository von GoCenter einschließlich aller Funktionen eingestellt。Weitere information zur einstein der Center找到了你博客- beitrag zur einstein
Jeden Monat verleiht GoCenter den leistungsstärksten Modulen ein金花鼠徽章als Auszeichnung für ihre Leistung。Wir schreiben über einige柴油机Top-Module和我在一起。
我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是,我的意思是。Diese Warnungen machen ängstlich, weil wir glauben, wir könnten etwas kaputt machen。Aber Anwendungen müssen sich ändern,嗯besser zu werden und innovativ zu sein。
软件单元测试软件单元测试。Sie helfen den Entwicklern zu wissen, ob die kleinen Teile ihrer软件ihre vorgesehene Funktion korrekt erfüllen。mitddritigen Umfang an Unit-Tests fühlen sich Entwickler sicherer, ihre Implementierungen zu ändern und sogar von Grund auf neu zu refaktorisieren。你的智慧nämlich,你的智慧überprüfen können, ob die neue Version immer noch wie vorgesehen funktioniert。
Je komplexer die软件,desto wichtiger werden单元测试,ebenso wie ein soldier Satz von dafür vorgesehenen工具在der gleichen Sprache。Und da Testcode mit guter Abdeckung sehr umfangreich sein kann, muss er genauso lesbar Und wartbar sein wie der producktcode, damit Entwickler ihn verwenden Und seine Vorteile nutzen。
Für unsere Go-Community-Projekte wie GoCenter nutzen wir ausgiebig das beliebte作证-模块,das eine Reihe von Golang-Paketen für die Durchführung wichtiger Unit-Test-Funktionen bereitstellt。
在dieem Artikel erfahren Sie, wie Sie die wicichtigsten Funktionen von作证nutzen können,嗯Go zu schreiben, die einfach zu lesen and zu warten sind中的单元测试。Der Artikel beschreibt, wie Unit-Tests bei Der Verwendung von reinem Go aussehen würden, wobei die Pakete von作证vorgestellt werden, die bei Der zu erledigenden Aufgabe helfen können。Anschließend sehen Sie den Code, der sich nach der Anwendung von作证ergibt。Wir zeigen einige最佳实践,wie man断言durchführt und Mocks für Abhängigkeiten schreibt。
作证:Ein Top-Gopher
作证ist ein entwicklerfreundlicher Satz von Paketen mit über 11.000 Sternen auf GitHub und großer Unterstützung durch die社区。证明erweitert das schlanke测试-Framework von Go, um断言和Mock-Abhängigkeiten durchzuführen。
Diese Funktionen sowie das tägliche Vertrauen unseres Go-Community-Teams darauf sind ein groer Teil der Gründe, warum das作证模块“顶级地鼠”是GoCenter ausgezeichnet wurde.Wenn Sie sich die umfangreichen Metadaten von GoCenter für das witness - module ansehen,wissen Sie, warum:
- 死自述“你的auf eine umfangreche Dokumentation”。Mehr详细信息über den模块代码erfahren wir über dieGoDoc-Registerkarte,这是一种自动生成的信息系统,在Funktionen和mehr zeigt
- 死所使用的——和指标-Registerkartenvon GoCenter zeigen, dass dieses模块angesichts vieler下载,叉子,Mitwirkenden和under Verwendung durch andereGo-Module相信vertrauenswürdig ist。

- 死Security-Registerkartevon GoCenter zeigt auch, dass die aktuelle Version dieses modules und seiner Abhängigkeiten keine bekannten NVD-Schwachstellen aufweisen, was durch einenJFrog Xray-Tiefenscanbestatigt将。

Eine einfache GoLang-Unit
Um mit dem Schreiben der Unit-Tests zu began, benötigen wir zunächst eine zu prüfende Komponente。Für diese Übung werden wir die folgende Dienstdefinition verwenden:
类型Prohth华体会最新官方网站ductService接口{IsProductReservable(id int) (bool, error)}
Für日常生活定义与实施及日常生活测试。Die Implementierung verfügt über einige Business-Logik, um zu bestimmen, ob ein product reservierbar ist oder niht。模具实现hängt auch von einer数据访问对象- komponente ab, um信息über Die Produkte bereitzustellen。模具实施muss Die folgenden vereinfachten Testfälle
- 这是一种刺激,一种定义
- 生产,die vor mehr als 1 Jahr in den Katalog aufgenommen wurden, sind reservierbar
- Andere Produkte sind niht reservierbar
- 产品化,产品化,产品化,产品化,产品化,产品化,产品化,产品化
产品规格:
类型Prohth华体会最新官方网站ductServiceImpl结构{productDAO持久化。ProductDAO} //构造函数func NewProductSerhth华体会最新官方网站viceImpl(dao persistent .ProductDAO) *ProductServiceImpl {return &ProductServiceImpl{ProductDAO: dao,}} func (s *ProductServiceImpl) IsProductReservable(id int) (bool, error) {// productinformation aus Datenbank holen product, err:= s.c eproductdao . getproduct (id)如果err != nil{返回false, fmt. getproduct (id)如果product == nil{返回false, fmt. w . error ("failed to get product details: %w", err)}Errorf("product not found for id %v", id)} // Nur Produkte, die vor mehr als 1 Jahr in den Katalog aufgenommen wurden, können reserviert werden return product. createdat . before (time.Now()。AddDate(- 1,0,0)), nil}
verwenden作证
Da wir nun einen einfachen Dienst haben, können wir mit作证单元测试erstellen, die gewährleisten, dass er wie vorgesehen funktioniert。
断言ausfuhren
Die grundlegendsten Aufgaben, Die von Unit-Tests ausgeführt werden, sind断言。断言werden in der Regel verwendet,嗯zu überprüfen, ob die vom Test durchgeführten Aktionen unter Verwendung beestimter Eingaben das erwartete Ergebnis erzeeugen。Sie können auch verwendet werden, um zu prüfen, ob die Komponenten den gewünschten Designregeln entsprechen。
Wenn wir mit reinem Go die断言ausführen, die benötigt werden, um zu prüfen, ob der erste Testfall anerkannt und unsere Dienstimplementierung richtig initialisiert wid, erhalten wir den folgenden代码:
import ("service" "testing") func TestNewPrhth华体会最新官方网站oductServiceImpl(t *testing. t) {productDaoMock:= productDaoMock {} // Mock的ignoorieren productServiceImpl:= NewProductServiceImpl(&productDaoMock) // Sichert productServiceImpl zu implementiert ProductService。布里希特登编译ab, wenn er es niht tut。Var服务。hth华体会最新官方网站ProductService = productServiceImpl if productServiceImpl == nil {t.t fatal (" productdienst nicht initialisiert")} if productServiceImpl = productServiceImpl if productServiceImpl == nil {t.t fatal (" productdienst nicht initialisiert")}productDAO == nil {t.Fatal("Produktdienst-Abhängigkeit nicht initialisiert")}}
阿尔斯希尔夫für die断言enthält作证das Paketgithub.com/stretchr/testify/assert.diesel Paket bietet mehere Methoden, die helfen können, Werte mit erwarteten Ergebnissen zu vergleichen。Wenn wir unsere Vergleiche durch diese Methoden ersetzen, erhalten wir folgendes:
import (" github.com/stretchr/testify/assert" "service" "testing") func TestNewPhth华体会最新官方网站roductServiceImpl(t *testing. t){断言:= assert.New(t) productDaoMock:= productDaoMock {} // Mock vorerst ignoorieren productServiceImpl:= NewProductServiceImpl(&productDaoMock) if !断言。implements ((*service. productservice)(nil), new(productServiceImpl)) {t. fatal (" producktdienest - implementierung beachtet Dienstdefinition nicht")} if !断言。NotNil(hth华体会最新官方网站productServiceImpl, " productdienst nicht initialisiert") {t.Fatal(" productdienst nicht initialisiert")} if !productDAO, "Produktdienst-Abhängigkeit nicht initialisiert") {t.Fatal("Produktdienst-Abhängigkeit nicht initialisiert")}}
Neben der Hilfe bei den断言bieten die证词- pakete auch eine bessere Benachrichtigung, wenn eine diesel操作fehlschlägt。温恩泽贝斯皮埃尔·维格森哈本,达斯产品道-费尔德在德国的工业实践中,würden wiir den folgenden Testfehler erhalten:
=== RUN TestNewhth华体会最新官方网站ProductServiceImpl TestNewProductServiceImpl: product_service_impl_test。go:22:错误跟踪:product_service_impl_test。go:22错误:Erwarteter Wert darf nicht Null sein。Test: TestNewhth华体会最新官方网站ProductServiceImpl Meldungen: Produktdienst-Abhängigkeit nicht initialisiert TestNewProductServiceImpl: product_service_impl_test。go:23: Produktdienst-Abhängigkeit nicht initialisiert——FAIL: TestNewProhth华体会最新官方网站ductServiceImpl (0.00s)
Bisher konnten wir trotz besserem消息和bequemerer Methoden zur Ausführung der断言Umfang unseres测试。“如果-不-断言-中断”,das schwieriger machen kann, unseren Testcode zu lesen。嗯,大北祖helfen, enthält作证das Paketgithub.com/stretchr/testify/require.Dieses Paket verfügt über die gleichen断言- methoden wie das断言-Paket, bricht aber den Test sofort ab, wenn eine断言fehlschlägt。Wenn wir dieses Paket einführen, erhalten wir den folgenden kürzeren und leichter zu lesenden测试代码:
import ("github.com/stretchr/testify/require" "service" "testing") func TestNewPrhth华体会最新官方网站oductServiceImpl(t *testing. t){断言:= require.New(t) productDaoMock:= productDaoMock {} // Mock vorerst ignoorieren productServiceImpl:= NewProductServiceImpl(&productDaoMock)断言。implements ((*service. productservice)(nil), new(productServiceImpl), " producktdienest - implementierung beachtet Dienstdefinition nicht")断言。NotNil(hth华体会最新官方网站productServiceImpl, " productdienst nicht initialisiert")断言。productDAO, "Produktdienst-Abhängigkeit nicht initialisiert")}
Mocking-Abhangigkeiten
Wenn wir eine Komponente testen, wollen wir sie im Idealfall vollständig isolieren, um zu vermeiden, dass Fehler an anderer Stelle unsere Tests beeinträchtigen。die ist besonders schwierig, wenn die Komponente, die wir testen wollen, Abhängigkeiten zu anderen Komponenten aus verschiedenen Schichten unserer软件帽。在dem hier verwendeten Szenario hängt数据访问对象(DAO)-Schicht ab, um auf information über die Produkte zuzugreifen。
嗯die gewünschte隔离zu fördern, ist es üblich, dass Entwickler unechte, vereinfachte Implementierungen dieser Abhängigkeiten schreiben, die während der Tests verwendet werden。Diese unechten Implementierungen werden mock genant。
Wir können eine Mock-Implementierung des ProductDAO erellen, die in die dienstiplementierung für die Testausführung injiziert wid。模具产品dao - schnittstelle, Die unser Mock implementieren muss, sieht wie folgt aus:
类型ProductDAO接口{GetProduct(id int)(*模型。Product, error)}
嗯死Testausführung zu ermöglichen, ist es notwenddig, dass der Mock ein Verhalten zeigt, das mit allen Testfällen, die wir validieren wollen, kompatibel ist, da wir sonst niht die gewünschte Testabdeckung erreichen können。Mit reinem Go würde unser Testfall Mit dem Mock wie folgt aussehen:
import ("errors" "model" "persist" "testing" "time") type ProductDaoMock struct {} func (m *ProductDaoMock) GetProduct(id int) (*model.)产品,错误){开关id{案例1:返回&模型。产品{Id: 1,描述:“Produkt wurde vor 2 Jahren erstellt”,创建时间:时间。现在()。AddDate(- 2,0,0),}, nil情况2:返回&模型。产品{Id: 2,描述:" productkt kürzlich erstelt ", CreatedAt: time.Now(),}, nil情况999:返回nil,坚持。ErrProductNotFound}返回nil, nil} func TestProductShth华体会最新官方网站erviceImpl_IsProductReservable(t *testing.T) {testDataSet:= map[int]bool {1: true, 2: false,} productDaoMock:= productDaoMock {} productServiceImpl:= NewProductServiceImpl(&productDaoMock) for productId, expectedResult:= range testDataSet{可保留,err:= productserviceimp_isproductreservable (productId) if err != nil {t.Fatalf("Es konnte nicht geprüft werden, ob producdukt %v reservierbar ist: "= expectedResult {t.Fatalf("Falsche Reservierungsinfo für produckt - id erhalten %v. s", productId, err)} if reserved != expectedResult {t.Fatalf("Falsche Reservierungsinfo für produckt - id erhalten %v. s"预期:% v。get: %v", productId, expectedResult, reserable)}}} func TestProdhth华体会最新官方网站uctServiceImpl_IsProductReservable_NotFound(t *testing.T) {productDaoMock:= productDaoMock {} productServiceImpl:= NewProductServiceImpl(&productDaoMock) _, err:= productserviceimpt . isproductreservable (999) if !错误。Is(err, persist.ErrProductNotFound) {t.Fatalf("Unerwartetes Fehlerergebnis erhalten: %s", err)}}
as Hauptproblem mit obigen Ansatz ist, dass unsere Testfalllogik nun vertet ist。Ein Teil davon ist im Testfall selbst implementiert, wo wir Ereignisse an die getestete Komponente senden und Assertions mit den Ergebnissen ausführen, während der andere Teil im Mock implementiert ist。柴油wiederum muss in Verhalten bereitstellen, das mit des Testfalls kompatibel ist。Es ist leicht zu erkennen, wie unser Testfall jetzt abgebrochen werden könnte, und zwar niht wegen eines Problems im Test selbst, sondern weil der Mock niht die erforderlichen Daten zurückgibt。
在一个小问题上,在一个小问题上,在一个小问题上,在一个小问题上,在一个小问题上Testfälle椎体。Es ist möglich, dass Änderungen, die am Mock vorgenome werden, um die Erfordernisse eines Testfalls zu erfüllen, andere Testfälle zerstören。In unserem Szenario haben wir nur 3 Testfälle, um die wir uns kümmern。Sie können sich aber vorstellen, wie unübersichtlich es werden könnte, wenn wir komplexere Testfälle hätten。笑在梅尔的笑在梅尔的笑würde夜晚的unbedingt helfen, sondern könnte die Komplexität sogar noch erhöhen。Wenn sich unsere gemockte Schnittstelle ändert, müssten wir außerdem mehere Mocks aktualisieren, um sie kompatibel zu halten。
Was wir brauchen, ist, dass die Testfalllogik zentralisiert und unabhängig bleibt。嗯,大北祖helfen, enthält作证das Paketgithub.com/stretchr/testify/mock.Dieses Paket stellt Tools zur Verfügung,嗯Mocks zu erstellen, die es ermöglichen, das Verhalten zur Laufzeit zu injizieren。大北sorgt der Testfall selbst dafür, dass die gemockte Logik nahe and der Testlogik bleibt。
Durch die Verwendung des证言- mock - pakets zur Erstellung unseres dao - mock und das Verschieben der Initialisierung des Mock-Verhaltens in die Testfälle sowie das Hinzufügen des证言- require - pakets zur Ausführung unserer断言sieht unser Testcode wie folgt aus:
import ("github.com/stretchr/testify/require" "github.com/stretchr/testify/mock" "errors" "model" "persist" "testing" "time") type productdaotestfymock struct {mock. import ("github.com/stretchr/testify/require" "github.com/stretchr/testify/mock" "errors" "model" "persist" "testing" "time")Mock} func (m * productdaotestfymock) GetProduct(id int)(*模型。Product, error) {args:= m.Called(id) return args. get (0).(*model.Product), args. error (1)} func Teshth华体会最新官方网站tProductServiceImpl_IsProductReservable(t *testing.T){断言:= require.New(t) //注册测试模拟productDaoMock:= ProductDaoTestifyMock{} productDaoMock。(“GetProduct”,1).Return(模型。产品{Id: 1,描述:“Produkt wurde vor 2 Jahren erstellt”,创建时间:时间。现在()。AddDate(- 2,0,0),}, nil)(“GetProduct”,2).Return(模型。产品{Id: 2,描述:" productServiceImpl. isproductreservable ", CreatedAt: time.Now(),}, nil) testDataSet:= map[int]bool {1: true, 2: false,} produhth华体会最新官方网站ctServiceImpl:= NewProductServiceImpl(&productDaoMock) for productId, expectedResult:= range testDataSet {reserable, err:= productServiceImpl. isproductreservable (productId)断言。NoErrorf(err, "Konnte nicht prüfen, ob Produkt %v reservierbar ist: %s", productId, err)断言。Equalf(expectedResult, reserved, "Falsche Reservierungsinfo für produckt - id erhalten %v", productId)}} func TestPhth华体会最新官方网站roductServiceImpl_IsProductReservable_NotFound(t *testing.T){断言:= require.New(t) //注册测试模拟productDaoMock:= ProductDaoTestifyMock{} productDaoMock。On("GetProduct", 1). return ((*model.Product)(nil), persistn . errproductnotfohth华体会最新官方网站und) productServiceImpl:= NewProductServiceImpl(&productDaoMock) _, err:= productServiceImpl. isproductreservable (1) if !是(err, persist.ErrProductNotFound){断言。Failf("Got unexpected error result", "Unerwartetes fehlergebnis erhalten: %s", err)}}
在der obigen Implementierung sehen Sie, wie as Mock-Verhalten and die Testlogik inhalb des Testfalls centralisiert信德。Beachten Sie auch, dass das erfasste Scheinverhalten ausschließlich für den Testfall gilt, in dem es platziert ist, da es zu einer einzelnen Mock-Instanz gehört, die nicht von meherren Tests gemeinsam genutzt wd。模具测试erfassen ohne jegliche问题sogar unterschiedliche Verhaltensweisen für模具产品id 1。ProductDaoTestifyMock kann sicher zwischen meheren Testfällen wiederverwendet werden, da es kein Verhalten帽子。
Fazit
我的工作,你的工作nützliche信息在diesem Artikel gefunden和我的工作,达斯尔Ihnen helfen kann, bessere单位测试在Ihren项目的zu schreiben。Um prove mit Go-Modulen zu Ihrem Projekt hinzuzufügen und damit zu spielen, führen Sie einfach die folgenden Befehle aus:
$ export GOPROXY=https://gocenter。IO $ go得到github.com/stretchr/testify
Sehen Sie sich在gof center作证oder suchen Sie, um noch mehr tolle Go-Module zu entdecken。

