Heim >Backend-Entwicklung >Golang >Warum Unit-Tests? Wie führt man den Test durch?

Warum Unit-Tests? Wie führt man den Test durch?

青灯夜游
青灯夜游nach vorne
2022-10-10 19:19:392288Durchsuche

Warum Unit-Tests? Wie führt man den Test durch?

Hier ist ein Link zu einem Video, in dem ich über dieses Thema spreche

Wenn Sie keine Videos mögen, finden Sie hier eine längere Version.

Software

Software kann geändert werden. Deshalb wird es „Software“ genannt. Seine Plastizität ist stärker als die von Hardware. Ein großartiges Team von Ingenieuren sollte für ein Unternehmen eine große Bereicherung sein und Systeme schreiben, die auch dann einen Mehrwert schaffen, wenn das Unternehmen wächst.

Warum sind wir darin so schlecht? Von wie vielen Projekten haben Sie gehört, die völlig gescheitert sind? Oder es wird „veraltet“ und muss komplett neu geschrieben werden (Umschreibungen schlagen normalerweise auch fehl!)

Wie „versagen“ Softwaresysteme? Kann es nicht geändert werden, bevor es korrekt ist? Das ist unser Versprechen!

Viele Leute entscheiden sich dafür, Systeme in Go zu erstellen, weil es viele Entscheidungen getroffen hat, von denen die Leute hoffen, dass sie es besser lesbar machen. [Verwandte Empfehlungen: Go-Video-Tutorial]

  • Im Vergleich zu meiner vorherigen Scala-KarriereIch beschreibe es so, dass man den Drang verspürt, sich aufzuhängen, Go hat nur 25 Schlüsselwörter und viele können vom Standard heruntergeladen werden Bibliothek und Ein System, das in einigen anderen kleinen Bibliotheken eingebaut ist. Die Vision ist, dass Sie mit Go Code schreiben und in 6 Monaten darauf zurückblicken können, und er immer noch Sinn ergibt.

  • Tools zum Testen, Benchmarking, semantischen Parsen und Laden sind im Vergleich zu den meisten Alternativen erstklassig.

  • Tolle Standardbibliothek.

  • Eine strenge Rückkopplungsschleife ermöglicht eine sehr schnelle Kompilierung.

  • Go verpflichtet sich zur Abwärtskompatibilität. Es sieht so aus, als würde Go in Zukunft Generika und andere Funktionen erhalten, aber die Designer haben versprochen, dass sogar Go-Code, den Sie vor 5 Jahren geschrieben haben, weiterhin erstellt werden kann. Ich habe ein paar Wochen damit verbracht, mein Projekt von Scala 2.8 auf 2.10 zu aktualisieren.

Trotz all dieser großartigen Eigenschaften können wir immer noch schlechte Systeme erstellen. Unabhängig davon, ob die von Ihnen verwendete Sprache großartig ist oder nicht, sollten Sie auf die frühere Softwareentwicklung zurückblicken und die gewonnenen Erkenntnisse verstehen.

Im Jahr 1974 schrieb ein intelligenter Softwareentwickler namens [Manny Lehman](https://en.wikipedia.org/wiki/Manny_Lehman...) „Lehmans Gesetz der Software-Evolution“ .

Diese Gesetze beschreiben das Gleichgewicht zwischen den Kräften, die neue Entwicklungen vorantreiben, und den Kräften, die den Fortschritt behindern.

Wenn wir nicht wollen, dass die Systeme, die wir entwickeln, zum Vermächtnis werden und immer wieder neu geschrieben werden, dann müssen wir uns auf diese Kräfte konzentrieren, um sie zu verstehen.

Das Gesetz der kontinuierlichen Veränderung

Die im wirklichen Leben verwendeten Softwaresysteme müssen sich ständig ändern, sonst werden sie von der allgemeinen Umgebung eliminiert

Natürlich muss sich ein System ständig ändern, sonst wird es sich ändern werden immer nutzloser, aber warum wird diese Situation oft ignoriert?

Weil viele Entwicklungsteams Prämien erhalten, wenn sie ein Projekt bis zu einem bestimmten Datum abliefern, und dann mit dem nächsten Projekt fortfahren. Wenn die Software „Glück“ hat, wird sie zumindest in irgendeiner Form an eine andere Gruppe von Leuten übergeben, um sie zu warten, aber sie werden sicherlich nicht weiter daran iterieren.

Menschen legen normalerweise Wert darauf, ein Framework zu wählen, das ihnen hilft, „schnell zu liefern“, anstatt sich auf die Entwicklung der Systempersistenz zu konzentrieren.

Selbst wenn Sie ein großartiger Softwareentwickler sind, kann es dennoch passieren, dass Sie die zukünftigen Anforderungen Ihres Systems nicht verstehen. Wenn sich Ihr Unternehmen verändert, ist der großartige Code, den Sie geschrieben haben, möglicherweise nicht mehr relevant.

Lehman war in den 1970er Jahren sehr erfolgreich, weil er uns eine weitere Regel gab, die es wert war, darüber nachzudenken.

Das Gesetz der zunehmenden Komplexität

Während sich das System weiterentwickelt, wird die Komplexität des Systems weiter zunehmen, sofern keine Maßnahmen ergriffen werden, um die Zunahme der Systemkomplexität zu verringern

Was er jetzt sagen wird: Wir können nicht zulassen, dass das Softwareteam zu einer reinen Funktionsfabrik wird und einfach immer mehr Funktionen in der Software konzentriert, um den langfristigen Betrieb des Systems zu ermöglichen.

Da sich unsere Wissenslandschaft verändert,

müssen

wir weiterhin die Komplexität unserer Systeme bewältigen.

Refactoring

Softwareentwicklung kann Software auf viele

Arten formbar halten, wie zum Beispiel:

Entwickler-Empowerment
  • im Allgemeinen „guter“ Code. Achten Sie auf die sinnvolle Trennung von Code usw.
  • Kommunikationsfähigkeiten
  • Architektur
  • Beobachtbarkeit
  • Einsatzfähigkeit
  • Automatisiertes Testen
  • Geschlossener Kreislauf
  • Das werde ich Der Schwerpunkt liegt auf Refactoring. Ein häufiger Refrain, den Entwickler an ihrem ersten Programmiertag hören, lautet: „Wir müssen das umgestalten.“
Dieser Satz kommt von und? Wie unterscheidet sich Refactoring vom Schreiben von Code?

Ich weiß, dass ich und viele andere dachten, dass wir umgestalten würden, aber wir haben uns geirrt.

Martin Fowler beschreibt, wie Menschen Fehler machen

Allerdings wird „Refactoring“ oft an unpassenden Stellen verwendet. Wenn jemand über ein System spricht, das während des Refactorings einige Tage lang nicht verfügbar war, können Sie sicher sein, dass es sich nicht um ein Refactoring handelte.

Was ist das?

Factoring

Als Sie in der Schule Mathematik gelernt haben, haben Sie wahrscheinlich etwas über Factoring gelernt. Hier ist ein sehr einfaches Beispiel

Berechnung von 1/2 + 1/41/2 + 1/4

为此,将分母 分解,将表达式转换为

2/4 + 1/4 你可以把它变成 3/4.

我们可以从中吸取一些重要的教训。当我们 分解表达式 时,我们没有改变表达式的含义。两者都等于 3/4,但我们通过将 1/2 变为 2/4 后,我们的工作变得更容易了;它更适合我们的「领域」。

当你重构代码时,你应该在「符合」你当前系统需求的情况下,尝试找到一个方法来使你的代码更容易理解。关键是你不应该改变代码原有的行为.

Go 的一个例子

下面这个方法是用特定的 language 问候 name

func Hello(name, language string) string {
    if language == "es" {
        return "Hola, " + name
    }

    if language == "fr" {

        return "Bonjour, " + name

    }

    // 想象一下更多的语言

    return "Hello, " + name

}

如果有几十个 if 语句会让人感觉不舒服,而且我们还要重复的使用特定的 language 去伴随着 , 问候 name。因此,我们来重构代码。

func Hello(name, language string) string {
      return fmt.Sprintf(
          "%s, %s",
          greeting(language),
          name,
      )
}

var greetings = map[string]string {
  es: "Hola",
  fr: "Bonjour",
  //等等...
}

func greeting(language string) string {
  greeting, exists := greetings[language]

  if exists {
     return greeting
  }

  return "Hello"
}

实际上,这个重构的本质并不重要,重要的是我们没有改变代码的行为。

当重构时,你可以做任何你喜欢的事情,添加接口,新类型,函数,方法等等。唯一的规则是你不能改变代码的行为。

重构代码时不要改变功能

这非常重要。如果你重构时改变功能,你相当于同时在做 件事。作为软件工程师,我们应该学习把系统分成不同的文件/包/功能/等等,因为我们知道试图理解一大块东西是困难的。

我们不要一次想很多事情,因为那会使我们犯错误。我目睹了许多重构工作的失败,因为开发人员贪多嚼不烂。

当我在数学课上用笔和纸做因式分解时,我必须手动检查我是否改变了头脑中表达式的意思。当我们重构代码时,尤其是在一个重要的系统上,我们如何知道我们是否改变了功能?

那些选择不编写测试的人通常依赖于手动测试。除非是一个小项目,要不然这将是一个巨大的时间消耗,并且从长远来看不利于系统将来的扩展。

为了安全地重构,您需要单元测试因为它提供了

  • 可以在不担心功能改变的情况下重构代码

  • 有助于开发人员编写关于系统应该如何运行的文档

  • 比手工测试更快更可靠的反馈

Go 的一个例子

我们有一个 Hello 方法的单元测试是这样的:

func TestHello(t *testing.T) {

    got := Hello("Chris", es)

    want := "Hola, Chris"

    if got != want {

        t.Errorf("got %q want %q", got, want)

    }

}

在命令行中,我们可以运行 go test

Dazu faktorisieren Sie den Nenner

und wandeln den Ausdruck in
  • 2/4 + 1 /4 um Sie können es in 3/4 ändern.

    Wir können daraus einige wichtige Lehren ziehen. Wenn wir einen Ausdruck
  • zerlegen
  • ,

    ändern wir nicht die Bedeutung

    des Ausdrucks. Beide sind gleich 3/4, aber unsere Arbeit wird einfacher, wenn wir 1/2 in 2/4 ändern "Feld".
  • Wenn Sie Ihren Code umgestalten, sollten Sie versuchen, einen Weg zu finden, Ihren Code verständlicher zu machen und gleichzeitig Ihren aktuellen Systemanforderungen „zu entsprechen“. Der Schlüssel ist: „Sie sollten das ursprüngliche Verhalten des Codes nicht ändern.“

    rrreee

    Es wäre unangenehm, wenn es Dutzende von if-Anweisungen gäbe und wir wiederholt eine bestimmte Sprache verwenden müssten, um den , begrüßt <code>name. Lassen Sie uns den Code umgestalten.
  • rrreee
Eigentlich ist die Art dieser Umgestaltung nicht wichtig, wichtig ist, dass wir das Verhalten des Codes nicht geändert haben.

Beim Refactoring können Sie tun, was Sie wollen, Schnittstellen, neue Typen, Funktionen, Methoden usw. hinzufügen. Die einzige Regel ist, dass Sie das Verhalten des Codes nicht ändern können.

Ändern Sie beim Refactoring von Code nicht die Funktionalität

Das ist sehr wichtig. Wenn Sie beim Refactoring die Funktionalität ändern, erledigen Sie zwei

Dinge gleichzeitig. Als Softwareentwickler sollten wir lernen, das System in verschiedene Dateien/Pakete/Funktionen usw. zu unterteilen, weil wir wissen, dass es schwierig ist, einen großen Teil von etwas zu verstehen.

Wir sollten nicht über zu viele Dinge auf einmal nachdenken, denn das führt dazu, dass wir Fehler machen. Ich habe viele Refactoring-Bemühungen scheitern sehen, weil die Entwickler mehr abgebissen haben, als sie ertragen konnten.

Wenn ich im Mathematikunterricht mit Stift und Papier faktorisiere, muss ich manuell prüfen, ob ich die Bedeutung des Ausdrucks in meinem Kopf geändert habe. Wenn wir Code umgestalten, insbesondere auf einem kritischen System, woher wissen wir dann, ob wir die Funktionalität geändert haben

Wer sich dafür entscheidet, keine Tests zu schreiben, verlässt sich oft auf manuelle Tests. Sofern es sich nicht um ein kleines Projekt handelt, wird dies sehr zeitaufwändig sein und auf lange Sicht einer zukünftigen Erweiterung des Systems nicht förderlich sein.

Für eine sichere Umgestaltung benötigen Sie Unit-Tests

, da diese Folgendes ermöglichen:

Code kann umgestaltet werden, ohne sich Gedanken über Funktionsänderungen machen zu müssen.

hilft Entwicklern, Code darüber zu schreiben, wie sich das System verhalten soll. Dokumentation.

🎜🎜 Schneller und zuverlässiger Feedback als manuelles Testen 🎜🎜🎜

Ein Beispiel in Go

🎜Wir haben einen Unit-Test für die Hello-Methode wie diesen: 🎜rrreee 🎜In der Befehlszeile können wir Gehen Sie zum Test und erhalten Sie sofortiges Feedback darüber, ob sich unsere Refactoring-Arbeit auf die Ausführung des ursprünglichen Programms auswirkt. Tatsächlich ist es besser zu lernen, wie man Tests im Editor/in der IDE ausführt. 🎜🎜Sie möchten einen laufenden Status Ihres Programms erhalten.🎜🎜🎜🎜Kleines Refactoring.🎜🎜🎜🎜Führen Sie den Test durch. 🎜🎜In einem Projekt werden alle Ihre Schlüsselverhaltensweisen einem Unit-Test unterzogen und Feedback wird innerhalb einer Sekunde gegeben. Dies ist ein sehr leistungsfähiges Sicherheitsnetz für mutiges Refactoring, wenn es nötig ist. Dies hilft uns, die wachsende Komplexität zu bewältigen, die Lehman beschreibt. 🎜🎜🎜Unit-Tests sind so toll, warum stößt man manchmal auf Widerstand, wenn man sie schreibt? 🎜🎜🎜Einerseits gibt es diejenigen (wie mich), die sagen, dass Unit-Tests für die langfristige Gesundheit Ihres Systems wichtig sind, weil sie sicherstellen, dass Sie mit Zuversicht mit dem Refactoring fortfahren können. 🎜🎜Andererseits sagen einige, dass Unit-Tests das Refactoring tatsächlich 🎜hindern🎜. 🎜🎜Fragen Sie sich, wie oft müssen Sie Tests beim Refactoring ändern? Ich war im Laufe der Jahre an vielen Projekten beteiligt, die eine sehr hohe Testabdeckung aufwiesen, aber die Ingenieure zögerten, ein Refactoring durchzuführen, weil sie dachten, die Tests zu ändern wäre mühsam. 🎜🎜Das widerspricht unserem Versprechen! 🎜🎜🎜Warum ist das so? 🎜🎜🎜Angenommen, Sie sollen ein Quadrat zeichnen. Wir glauben, dass es am besten ist, zwei Dreiecke zusammenzukleben. 🎜🎜🎜🎜🎜Zwei rechtwinklige Dreiecke ergeben ein Quadrat

Wir schreiben Unit-Tests rund um das Quadrat, um sicherzustellen, dass beide Seiten gleich sind, und schreiben dann einige Tests rund um das Dreieck. Wir möchten sicherstellen, dass unsere Dreiecke korrekt gerendert werden, also stellen wir sicher, dass die Summe der Winkel 180 Grad beträgt, wir führen zwei Tests zur Überprüfung durch usw. Die Testabdeckung ist sehr wichtig und das Schreiben dieser Tests ist so einfach. Warum nicht? Ein paar Wochen später trafen die geänderten Gesetze unser System und ein neuer Entwickler nahm einige Änderungen vor. Sie denkt nun, dass es besser wäre, ein Quadrat aus zwei Rechtecken zu machen, als aus zwei Dreiecken.

Zwei Rechtecke ergeben ein Quadrat

Er hat dieses Refactoring ausprobiert und einige Hinweise aus einigen fehlgeschlagenen Tests erhalten. Hat er wirklich wichtige Funktionen des Codes kaputt gemacht? Sie muss nun tiefer in die Prüfung dieser Dreiecke eintauchen und versuchen zu verstehen, was wirklich in ihrem Inneren vorgeht.

Es spielt eigentlich keine Rolle

, dass ein Quadrat aus Dreiecken besteht, aber unsere Tests haben fälschlicherweise die Bedeutung eines Implementierungsdetails hervorgehoben .

Testfunktionalität, keine Implementierungsdetails

Wenn ich höre, wie sich Leute über Unit-Tests beschweren, liegt das normalerweise daran, dass die Tests auf der falschen Abstraktionsebene sind. Sie alle testen Implementierungsdetails, beobachten wie besessen den Code ihrer Mitarbeiter und machen sich viel lustig.

Ich glaube, dass dieses Problem auf ihr Missverständnis von Unit-Tests und ihr Streben nach Metriken (Testabdeckung) zurückzuführen ist.

Wenn ich nur über das Testen der Funktionalität spreche, sollten wir dann nicht einfach System-/Black-Box-Tests schreiben? Diese Arten von Tests sind zwar von großem Nutzen für die Validierung wichtiger User Journeys, allerdings sind sie oft teuer in der Erstellung und langsam in der Ausführung. Aus diesem Grund helfen sie beim

Refactoring

nicht viel, da die Feedbackschleife langsam ist. Darüber hinaus hilft Black-Box-Tests im Vergleich zu Unit-Tests nicht viel bei der Lösung des zugrunde liegenden Problems. Was

ist

die richtige Abstraktionsebene?

Das Schreiben effektiver Unit-Tests ist eine Designfrage.

Vergessen Sie Tests für einen Moment. Es ist besser, eigenständige, entkoppelte „Einheiten“ in Ihr System aufzunehmen, die sich auf Schlüsselkonzepte in Ihrer Domäne konzentrieren.

Ich stelle mir diese Einheiten gerne als einfache Legosteine ​​mit konsistenten APIs vor, die ich mit anderen Steinen kombinieren kann, um größere Systeme zu bauen. Innerhalb dieser APIs können viele Dinge (Typen, Funktionen usw.) vorhanden sein, die zusammenarbeiten, damit sie nach Bedarf funktionieren.

Wenn Sie beispielsweise Go verwenden, um ein Bankensystem zu entwickeln, sollten Sie über ein „Konto“-Paket verfügen. Es wird eine API bereitgestellt, die keine Implementierungsdetails preisgibt und einfach zu integrieren ist.


Wenn Ihre Einheiten diese Eigenschaften erfüllen, können Sie Komponententests für ihre öffentlichen APIs schreiben.

Per Definition

können diese Tests nur nützliche Funktionen testen. Wenn diese Einheiten vorhanden sind, können wir bei Bedarf Umgestaltungen vornehmen, und Tests werden uns in den meisten Fällen nicht behindern.

Sind das Unit-Tests?

Ja

. Unit-Tests sind für das, was ich als „Einheiten“ bezeichne. Sie zielen nie nur auf eine Klasse/Funktion/was auch immer ab.

Kombination dieser Konzepte

Wir haben bereits über

    Refactoring
  • Unit-Tests
  • Unit-Design
  • Was wir sehen können, sind, dass diese Aspekte des Software-Designs ergänzen sich gegenseitig andere.

Refactoring

    Stellen Sie Signale für unsere Unit-Tests bereit. Wenn wir manuell prüfen müssen, brauchen wir mehr Tests. Wenn der Test fehlschlägt, befindet sich unser Test auf der falschen Abstraktionsebene (oder hat keinen Wert und sollte entfernt werden).
  • hilft uns, mit der Komplexität innerhalb und zwischen Einheiten umzugehen.
Unit-Tests

    bietet Sicherheitsschutz für Refactoring.
  • Überprüfen und dokumentieren Sie die Funktionalität unserer Geräte.
(Gut gestaltete) Einheiten

    Einfach zu schreibende
  • aussagekräftige

    Unit-Tests.

  • Einfach umzugestalten.
  • Gibt es einen Prozess, der uns hilft, den Code ständig umzugestalten, um die Komplexität zu verwalten und das System skalierbar zu halten?

Warum Test Driven Development (TDD)

Einige Leute sind möglicherweise verwirrt von Lehmans Idee, wie Software ständig zu ändern, führt zu Over-Engineering und verschwendet viel Zeit mit dem Versuch, im Voraus das „perfekte“ skalierbare System zu erstellen, nur um nichts zu erreichen.

In den schlechten alten Zeiten der Software verbrachte ein Team von Analysten sechs Monate damit, Anforderungsdokumente zu schreiben, ein Team von Architekten verbrachte sechs Monate damit, Entwürfe zu entwerfen, und ein paar Jahre später scheiterte das gesamte Projekt.

Ich sagte, die alten Zeiten waren schlecht, aber sie sind es immer noch!

Agile Entwicklung lehrt uns, dass wir iterativ arbeiten, klein anfangen und die Software kontinuierlich verbessern müssen, damit wir schnell Feedback zum Software-Design erhalten und wie das Software arbeitet mit echten Benutzern; TDD erzwingt diesen Ansatz.

TDD befasst sich mit den Gesetzen, über die Lehman spricht, und anderen historisch schwer zu erlernenden Lektionen, indem es einen Entwicklungsansatz mit ständigem Refactoring und iterativer Bereitstellung fördert.

Kleine Schritte

  • Schreiben Sie einen kleinen Test für eine kleine Funktion

  • Überprüfen Sie, ob der Test mit offensichtlichen Fehlern fehlschlägt (rot)

  • Schreiben Sie den Mindestcode, damit der Test erfolgreich ist (grün)

  • Refactor

  • Wiederholen Sie die oben genannten Schritte

Je kompetenter Sie werden, desto natürlicher wird dies für Sie und Ihre Arbeitseffizienz wird immer höher.

Sie beginnen zu hoffen, dass Sie Eine kleine Testeinheit braucht nicht allzu lange, um den gesamten Test abzuschließen, denn wenn Sie feststellen, dass sich Ihr Programm ständig in einem nicht „grünen“ Zustand befindet, ist das ein Zeichen dafür, dass Sie möglicherweise in kleinen Schwierigkeiten stecken.

Mit diesen Testrückmeldungen können Sie ganz einfach die Stabilität einiger kleiner Anwendungsfunktionen sicherstellen.

Zufriedenstellendes Ende

  • Der Vorteil der Software ist, dass ich sie nach Bedarf ändern kann. Im Laufe der Zeit müssen die meisten Softwareprogramme aus unvorhersehbaren Gründen je nach Bedarf entsprechende Änderungen vornehmen. Überarbeiten und entwerfen Sie jedoch nicht zu Beginn, da die Zukunft zu schwer vorherzusagen ist.

  • Um die oben genannten Anforderungen zu erfüllen, müssen wir unsere Software skalierbar halten. Andernfalls wird die Situation sehr schlimm, wenn unsere Software umgestaltet und aktualisiert werden muss.

  • Ein guter Unit-Test kann Ihnen dabei helfen, Ihr Projekt schnell und reibungslos umzugestalten.

  • Das Schreiben guter Unit-Tests ist eine Designfrage. Sie müssen sorgfältig darüber nachdenken, Ihren Code so zu strukturieren, dass jeder Ihrer Unit-Tests genauso interessant integriert werden kann wie das Zusammensetzen von Legoblöcken, um den Test des gesamten Projekts erfolgreich abzuschließen.

  • Testgetriebene Entwicklung (TDD Test-Driven Development) kann Ihnen helfen und Sie dazu veranlassen, iterativ eine gut gestaltete Software zu entwickeln, die als technische Unterstützung für Ihre zukünftige Arbeit von großem Nutzen sein wird.

Originaladresse: https://quii.gitbook.io/learn-go-with-tests/meta/why

Übersetzungsadresse: https://learnku.com/go/t/34095

Weitere Kenntnisse zum Thema Programmierung finden Sie unter: Programmiervideos! !

Das obige ist der detaillierte Inhalt vonWarum Unit-Tests? Wie führt man den Test durch?. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:learnku.com. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen