Indem Sie sicherstellen, dass Ihre Anwendung getestet wird, können Sie die Anzahl der in Ihrem Code gefundenen Fehler reduzieren, die Wartbarkeit Ihrer Anwendung verbessern und gut strukturierten Code entwerfen.
Clientseitige Unit-Tests stellen andere Herausforderungen dar als serverseitige Tests. Wenn Sie mit clientseitigem Code arbeiten, fällt es Ihnen schwer, die Anwendungslogik von der DOM-Logik zu trennen und häufig nur JavaScript-Code zu erstellen. Glücklicherweise gibt es viele großartige clientseitige Testbibliotheken, die Ihnen dabei helfen können, Ihren Code zu testen, Metriken zur Testabdeckung zu erstellen und seine Komplexität zu analysieren.
Zuallererst sind Unit-Tests oft eine Möglichkeit, Fehler zu reduzieren, indem sichergestellt wird, dass sich die Anwendung wie erwartet verhält. Darüber hinaus gibt es noch die Konzepte Test Driven Development (TDD) und Behavior Driven Development (BDD).
Diese beiden Unit-Test-Strategien helfen Ihnen beim Entwerfen Ihrer Anwendung, indem Sie Tests schreiben, bevor Sie die Anwendungslogik schreiben. Indem Sie Tests schreiben, bevor Sie Code schreiben, haben Sie die Möglichkeit, sorgfältig über das Design Ihrer Anwendung nachzudenken.
Das liegt daran, dass Sie beim Schreiben von Tests grundsätzlich versuchen, die API zu entwerfen, mit der der Code interagiert, damit Sie deren Design besser verstehen können. Wenn Sie zunächst testen, werden alle Fehler im Design schnell aufgedeckt, da der Testcode, den Sie schreiben, im Wesentlichen den Code verwendet, den Sie schreiben!
TDD ist ein Code-Erkennungsprozess
Sie werden erfahren, dass TDD Ihnen hilft, Code zu entdecken, während Sie ihn schreiben. TDD kann schnell als „Rot, Grün, Refaktor“ zusammengefasst werden. Das bedeutet, dass Sie beim Schreiben eines Tests so viel Code schreiben, dass der Test überhaupt fehlschlägt. Dann schreiben Sie den Code, der den Test erfolgreich macht. Anschließend denkst du genau darüber nach, was du gerade geschrieben hast, und rekonstruierst es. Schön und einfach. BDD unterscheidet sich geringfügig von TDD und basiert eher auf Geschäftsanforderungen und -spezifikationen.
Kundentest
Da JavaScript in der modernen Webentwicklung eine so wichtige Rolle spielt, ist es wichtig zu lernen, wie Sie Ihren Code testen und die Wahrscheinlichkeit verringern, dass Fehler in den Produktionscode gelangen. Ihr Chef möchte nicht, dass so etwas passiert, und Sie sollten es auch nicht tun! Tatsächlich ist es ein guter Ausgangspunkt, mit clientseitigen Tests zu beginnen, wenn Sie Ihre Tests rund um die Fehlerberichterstattung verfassen. Auf diese Weise können Sie das Schreiben von Tests üben, wenn Sie nicht die Möglichkeit haben, bei Null anzufangen.
Ein weiterer Grund, Client-Code zu testen, besteht darin, dass Sie, sobald eine Reihe von Tests vorhanden ist und Sie über eine angemessene Abdeckung Ihres Codes verfügen, neue Funktionen hinzufügen und Ihre Tests erneut ausführen können, wenn Sie bereit sind, Ihrem Code neue Funktionen hinzuzufügen. und stellen Sie sicher, dass Sie keine vorhandenen Funktionen beeinträchtigen oder zerstören.
Loslegen Sie
Wenn Sie eine solche Bibliothek verwenden, müssen Sie weniger an die Darstellung Ihrer Seite im Browser denken und mehr an die Funktionalität Ihrer Anwendung. Unit-Tests mit einfachem JavaScript sind jedoch nicht unmöglich. In diesem Fall wird Ihr Leben viel einfacher, wenn Sie Ihren Code so entwerfen, dass das DOM leicht abstrahiert werden kann.
Testbibliothek auswählen
Jasmine und Mocha stammen beide von der BDD-Unit-Testing-Schule, während QUnit nur ein eigenes Unit-Testing-Framework ist.
Im Rest dieses Artikels werden wir uns mit der Verwendung von QUnit befassen, da die Hürde für den Einstieg in Kundentests sehr niedrig ist. Weitere Informationen finden Sie in der ausführlichen Einführung von QUnit.
Verwenden Sie QUnit TDD Ihren Code
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>QUnit Example</title> <link rel="stylesheet" href="qunit.css"> </head> <body> <div id="qunit"></div> <div id="qunit-fixture"></div> <script src="qunit.js"></script> <script src="../app/yourSourceCode.js"></script> <script src="tests.js"></script> </body> </html>
Nehmen wir für die nächsten Beispiele an, dass wir ein Widget erstellen, mit dem Sie eine Postleitzahl in ein Textfeld eingeben können und das mithilfe von Geonamen die entsprechenden Stadt-, Bundesland- und Kreiswerte zurückgibt. Es zeigt zunächst nur die Postleitzahl an, aber sobald die Postleitzahl fünf Zeichen hat, ruft es Daten von Geonames ab. Wenn die Daten gefunden werden können, werden weitere Felder mit den resultierenden Stadt-, Bundesland- und Kreisinformationen angezeigt. Wir werden auch Knockout.js verwenden. Der erste Schritt besteht darin, Tests zu schreiben, die fehlschlagen.
Denken Sie ein wenig über das Design nach, bevor Sie Ihren ersten Test schreiben. Sie benötigen wahrscheinlich mindestens zwei ViewModels, daher wäre dies ein guter Ausgangspunkt. Zuerst definieren wir ein QUnit-Modul und unseren ersten Test:
module("zip code retriever"); test("view models should exist", function() { ok(FormViewModel, "A viewModel for our form should exist"); ok(AddressViewModel, "A viewModel for our address should exist"); });
如果你运行这个测试,它会失败,现在你可以编写代码让它通过:
var AddressViewModel = function(options) { }; var FormViewModel = function() { this.address = new AddressViewModel(); };
这次您会看到绿色而不是红色。像这样的测试乍一看有点愚蠢,但它们很有用,因为它们迫使您至少思考设计的一些早期阶段。
我们将编写的下一个测试将适用于 AddressViewModel
的功能。从这个小部件的规范中我们知道,其他字段应该首先隐藏,直到找到邮政编码的数据。
module("address view model"); test("should show city state data if a zip code is found", function() { var address = new AddressViewModel(); ok(!address.isLocated()); address.zip(12345); address.city("foo"); address.state("bar"); address.county("bam"); ok(address.isLocated()); });
尚未编写任何代码,但这里的想法是 isLocated
将是一个计算的可观察值,仅当邮政编码、城市、州和县时才返回 true
都是实话。所以,这个测试一开始当然会失败,现在让我们编写代码让它通过。
var AddressViewModel = function(options) { options = options || {}; this.zip = ko.observable(options.zip); this.city = ko.observable(options.city); this.state = ko.observable(options.state); this.county = ko.observable(options.county); this.isLocated = ko.computed(function() { return this.city() && this.state() && this.county() && this.zip(); }, this); this.initialize(); };
现在,如果您再次运行测试,您将看到绿色!
这是最基本的,如何使用 TDD 编写前端测试。理想情况下,在每次失败的测试之后,您应该编写最简单的代码来使测试通过,然后返回并重构代码。不过,您可以了解更多有关 TDD 实践的知识,因此我建议您进一步阅读并研究它,但前面的示例足以让您考虑首先编写测试。
Sinon.js 是一个 JavaScript 库,提供监视、存根和模拟 JavaScript 对象的功能。编写单元测试时,您希望确保只能测试给定的代码“单元”。这通常意味着您必须对依赖项进行某种模拟或存根以隔离正在测试的代码。
Sinon 有一个非常简单的 API 可以完成此操作。 Geonames API 支持通过 JSONP 端点检索数据,这意味着我们将能够轻松使用 $.ajax
。
理想情况下,您不必在测试中依赖 Geonames API。它们可能会暂时关闭,您的互联网可能会中断,并且实际进行 ajax 调用的速度也会变慢。诗乃前来救援。
test("should only try to get data if there's 5 chars", function() { var address = new AddressViewModel(); sinon.stub(jQuery, "ajax").returns({ done: $.noop }); address.zip(1234); ok(!jQuery.ajax.calledOnce); address.zip(12345); ok(jQuery.ajax.calledOnce); jQuery.ajax.restore(); });
在此测试中,我们做了一些事情。首先,sinon.stub
函数实际上将代理 jQuery.ajax
并添加查看其被调用次数以及许多其他断言的功能。正如测试所示,“应该仅在有 5 个字符时尝试获取数据”,我们假设当地址设置为“1234
”时,尚未进行 ajax 调用,然后将其设置为“12345
”,此时应进行 ajax 调用。
然后我们需要将 jQuery.ajax
恢复到其原始状态,因为我们是单元测试的好公民,并且希望保持我们的测试原子性。保持测试的原子性非常重要,可以确保一个测试不依赖于另一测试,并且测试之间不存在共享状态。然后它们也可以按任何顺序运行。
现在测试已经编写完毕,我们可以运行它,观察它失败,然后编写向 Geonames 执行 ajax 请求的代码。
AddressViewModel.prototype.initialize = function() { this.zip.subscribe(this.zipChanged, this); }; AddressViewModel.prototype.zipChanged = function(value) { if (value.toString().length === 5) { this.fetch(value); } }; AddressViewModel.prototype.fetch = function(zip) { var baseUrl = "http://www.geonames.org/postalCodeLookupJSON" $.ajax({ url: baseUrl, data: { "postalcode": zip, "country": "us" }, type: "GET", dataType: "JSONP" }).done(this.fetched.bind(this)); };
在这里,我们订阅邮政编码的更改。每当它发生变化时,都会调用 zipChanged
方法。 zipChanged
方法将检查 zip 值的长度是否为 5
。当到达 5
时,将调用 fetch
方法。这就是Sinon 存根发挥作用的地方。此时,$.ajax
实际上是一个Sinon存根。因此,在测试中 CalledOnce
将是 true
。
我们将编写的最终测试是数据从 Geonames 服务返回时的情况:
test("should set city info based off search result", function() { var address = new AddressViewModel(); address.fetched({ postalcodes: [{ adminCode1: "foo", adminName2: "bar", placeName: "bam" }] }); equal(address.city(), "bam"); equal(address.state(), "foo"); equal(address.county(), "bar"); });
此测试将测试如何将来自服务器的数据设置到 AddressViewmodel
上。运行一下,看到一些红色。现在将其设为绿色:
AddressViewModel.prototype.fetched = function(data) { var cityInfo; if (data.postalcodes && data.postalcodes.length === 1) { cityInfo = data.postalcodes[0]; this.city(cityInfo.placeName); this.state(cityInfo.adminCode1); this.county(cityInfo.adminName2); } };
fetched方法只是确保从服务器传来的数据中有一个postalcodes
数组,然后在viewModel
上设置相应的属性。
看看这现在有多容易了吗?一旦你掌握了执行此操作的流程,你就会发现自己几乎不想再进行 TDD。您最终会得到可测试的漂亮小函数。您强迫自己思考代码如何与其依赖项交互。现在,当代码中添加其他新需求时,您可以运行一套测试。即使您错过了某些内容并且代码中存在错误,您现在也可以简单地向套件添加新测试,以证明您已经修复了错误!它实际上最终会让人上瘾。
测试覆盖率提供了一种简单的方法来评估单元测试测试了多少代码。达到 100% 的覆盖率通常很困难且不值得,但请尽您所能使其尽可能高。
更新且更简单的覆盖库之一称为 Blanket.js。将它与 QUnit 一起使用非常简单。只需从他们的主页获取代码或使用 Bower 安装即可。然后将毯子添加为 qunit.html
文件底部的库,然后将 data-cover
添加到您想要进行覆盖率测试的所有文件。
<script src="../app/yourSourceCode.js" data-cover></script> <script src="../js/lib/qunit/qunit/qunit.js"></script> <script src="../js/lib/blanket/dist/qunit/blanket.js"></script> <script src="tests.js"></script> </body>
完成。超级简单,现在您将在 QUnit 运行程序中获得一个用于显示覆盖范围的选项:
在此示例中,您可以看到测试覆盖率并不是 100%,但在这种情况下,由于代码不多,因此很容易提高覆盖率。您实际上可以深入了解尚未涵盖的确切功能:
在这种情况下,FormViewModel
从未在测试中实例化,因此缺少测试覆盖率。然后,您可以简单地添加一个新测试来创建 FormViewModel
的实例,并且可能编写一个断言来检查 address
属性是否存在并且是 instanceOf
AddressViewModel
。
然后您将很高兴看到 100% 的测试覆盖率。
随着您的应用程序变得越来越大,能够对 JavaScript 代码运行一些静态分析是件好事。 Plato 是一个在 JavaScript 上运行分析的好工具。
您可以通过 npm
安装来运行 plato
:
npm install -g plato
然后您可以在 JavaScript 代码目录上运行 plato
:
plato -r -d js/app reports
这将在位于“js/app
”的所有 JavaScript 上运行 Plato,并将结果输出到 reports
。 Plato 对您的代码运行各种指标,包括平均代码行数、计算的可维护性分数、JSHint、难度、估计错误等等。
在上一张图片中没有太多可看的内容,仅仅是因为对于我们一直在处理的代码来说,只有一个文件,但是当您开始使用具有大量文件的大型应用程序时,几行代码,您会发现它为您提供的信息非常有用。
它甚至会跟踪您运行它的所有时间,以便您可以查看统计数据如何随时间变化。
虽然测试客户端似乎是一个困难的提议,但现在有很多很棒的工具可以使用,使它变得超级简单。本文仅仅触及了当今所有使客户端测试变得容易的事情的表面。这可能是一项乏味的任务,但最终您会发现,拥有测试套件和可测试代码的好处远远超过它。希望通过此处概述的步骤,您将能够快速开始测试客户端代码。
Das obige ist der detaillierte Inhalt vonDenken Sie daran, Ihre Kunden zu schützen!. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!