目录
为什么要测试?
客户端测试
开始使用
选择测试库
使用 QUnit TDD 您的代码
使用Sinon.js模拟依赖关系
测试覆盖率
复杂性测试
结论
首页 web前端 js教程 请记住保护您的客户端!

请记住保护您的客户端!

Sep 07, 2023 pm 01:49 PM

通过确保您的应用程序经过测试,您可以减少代码中发现的错误数量,提高应用程序的可维护性,并设计结构良好的代码。

客户端单元测试提出了与服务器端测试不同的挑战。在处理客户端代码时,您会发现自己很难将应用程序逻辑与 DOM 逻辑分开,并且通常只是构建 JavaScript 代码。幸运的是,有很多很棒的客户端测试库可以帮助测试您的代码、创建有关测试覆盖率的指标以及分析其复杂性。


为什么要测试?

首先,单元测试通常是一种通过确保应用程序按预期运行来减少错误的方法。除此之外,还有测试驱动开发 (TDD) 和行为驱动开发 (BDD) 的概念。

这两种单元测试策略将帮助您在编写应用程序逻辑之前编写测试来设计应用程序。通过在编写代码之前编写测试,您将有机会仔细思考应用程序的设计。

发生这种情况是因为当您编写测试时,您基本上是在尝试设计与代码交互的 API,因此您可以更好地了解其设计。首先进行测试将很快显示出设计中存在的任何缺陷,因为您正在编写的测试代码本质上使用了您正在编写的代码!

TDD 是一个代码发现过程

您将了解到 TDD 可以帮助您在编写代码时发现代码。 TDD 很快就可以概括为“红、绿、重构”。这意味着,您编写一个测试,编写足够的代码首先使测试失败然后,您编写使测试通过的代码。之后,你仔细思考你刚刚写的内容并重构它。又好又简单。

BDD 与 TDD 略有不同,更多地基于业务需求和规范。


客户端测试

您应该测试客户端代码的原因有很多。如前所述,它将有助于减少错误,并帮助您设计应用程序。客户端测试也很重要,因为它使您有机会独立测试前端代码,远离演示文稿。换句话说,它的优点之一是您可以测试 JavaScript 代码,而无需实际启动应用程序服务器。您只需运行测试并确保功能正常运行,而无需四处点击并进行测试。在许多情况下,只要正确设置测试,您甚至不需要访问互联网。

随着 JavaScript 在现代 Web 开发中发挥如此重要的作用,学习如何测试代码并减少错误进入生产代码的机会非常重要。您的老板不喜欢这种情况发生,您也不应该!事实上,开始进行客户端测试的一个好地方是围绕错误报告编写测试。当您没有地方从头开始时,这将使您能够练习编写测试。

测试客户端代码的另一个原因是,一旦存在一套测试并且对您的代码有适当的覆盖,当您准备好向代码添加新功能时,您将能够添加新功能功能,重新运行您的测试,并确保您没有退化或破坏任何现有功能。


开始使用

如果您以前从未做过客户端测试,那么开始进行客户端测试可能会令人畏惧。客户端测试最困难的部分之一是找出将 DOM 与应用程序逻辑隔离的最佳方法。这通常意味着您需要对 DOM 进行某种抽象。实现这一目标的最简单方法是通过客户端框架,例如 Knockout.js、Backbone.js 或 Angular.js,仅举几例。

使用此类库时,您可以少考虑页面在浏览器中的呈现方式,而多考虑应用程序的功能。不过,用简单的 JavaScript 进行单元测试并不是不可能的。在这种情况下,如果您以 DOM 可以轻松抽象的方式设计代码,您的生活将会轻松得多。

选择测试库

有很多不同的测试库可供选择,尽管三个领先者往往是 QUnit、Mocha 和 Jasmine。

Jasmine 和 Mocha 都来自 BDD 单元测试学派,而 QUnit 只是它自己的一个单元测试框架。

在本文的其余部分中,我们将探讨使用 QUnit,因为它进入客户端测试的门槛非常低。查看 QUnit 的详细介绍以获取更多信息。

使用 QUnit TDD 您的代码

QUnit 入门非常简单。您只需要以下 HTML:

<!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>
登录后复制

对于接下来的几个示例,假设我们正在构建一个小部件,您可以在文本框中输入邮政编码,然后它会使用 Geonames 返回相应的城市、州和县值。它一开始只显示邮政编码,但一旦邮政编码有五个字符,它就会从 Geonames 检索数据。如果能够找到数据,它将显示更多包含结果城市、州和县信息的字段。我们还将使用 Knockout.js。第一步是编写失败的测试。

在编写第一个测试之前稍微思考一下设计,可能需要至少两个 viewModel,所以这将是一个很好的起点。首先,我们将定义一个 QUnit 模块和我们的第一个测试:

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模拟依赖关系

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、难度、估计错误等等。

请记住保护您的客户端!

在上一张图片中没有太多可看的内容,仅仅是因为对于我们一直在处理的代码来说,只有一个文件,但是当您开始使用具有大量文件的大型应用程序时,几行代码,您会发现它为您提供的信息非常有用。

它甚至会跟踪您运行它的所有时间,以便您可以查看统计数据如何随时间变化。

结论

虽然测试客户端似乎是一个困难的提议,但现在有很多很棒的工具可以使用,使它变得超级简单。本文仅仅触及了当今所有使客户端测试变得容易的事情的表面。这可能是一项乏味的任务,但最终您会发现,拥有测试套件和可测试代码的好处远远超过它。希望通过此处概述的步骤,您将能够快速开始测试客户端代码。

以上是请记住保护您的客户端!的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热门文章

<🎜>:泡泡胶模拟器无穷大 - 如何获取和使用皇家钥匙
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系统,解释
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆树的耳语 - 如何解锁抓钩
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

热门话题

Java教程
1666
14
CakePHP 教程
1425
52
Laravel 教程
1325
25
PHP教程
1273
29
C# 教程
1252
24
JavaScript引擎:比较实施 JavaScript引擎:比较实施 Apr 13, 2025 am 12:05 AM

不同JavaScript引擎在解析和执行JavaScript代码时,效果会有所不同,因为每个引擎的实现原理和优化策略各有差异。1.词法分析:将源码转换为词法单元。2.语法分析:生成抽象语法树。3.优化和编译:通过JIT编译器生成机器码。4.执行:运行机器码。V8引擎通过即时编译和隐藏类优化,SpiderMonkey使用类型推断系统,导致在相同代码上的性能表现不同。

Python vs. JavaScript:学习曲线和易用性 Python vs. JavaScript:学习曲线和易用性 Apr 16, 2025 am 12:12 AM

Python更适合初学者,学习曲线平缓,语法简洁;JavaScript适合前端开发,学习曲线较陡,语法灵活。1.Python语法直观,适用于数据科学和后端开发。2.JavaScript灵活,广泛用于前端和服务器端编程。

从C/C到JavaScript:所有工作方式 从C/C到JavaScript:所有工作方式 Apr 14, 2025 am 12:05 AM

从C/C 转向JavaScript需要适应动态类型、垃圾回收和异步编程等特点。1)C/C 是静态类型语言,需手动管理内存,而JavaScript是动态类型,垃圾回收自动处理。2)C/C 需编译成机器码,JavaScript则为解释型语言。3)JavaScript引入闭包、原型链和Promise等概念,增强了灵活性和异步编程能力。

JavaScript和Web:核心功能和用例 JavaScript和Web:核心功能和用例 Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

JavaScript在行动中:现实世界中的示例和项目 JavaScript在行动中:现实世界中的示例和项目 Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

了解JavaScript引擎:实施详细信息 了解JavaScript引擎:实施详细信息 Apr 17, 2025 am 12:05 AM

理解JavaScript引擎内部工作原理对开发者重要,因为它能帮助编写更高效的代码并理解性能瓶颈和优化策略。1)引擎的工作流程包括解析、编译和执行三个阶段;2)执行过程中,引擎会进行动态优化,如内联缓存和隐藏类;3)最佳实践包括避免全局变量、优化循环、使用const和let,以及避免过度使用闭包。

Python vs. JavaScript:社区,图书馆和资源 Python vs. JavaScript:社区,图书馆和资源 Apr 15, 2025 am 12:16 AM

Python和JavaScript在社区、库和资源方面的对比各有优劣。1)Python社区友好,适合初学者,但前端开发资源不如JavaScript丰富。2)Python在数据科学和机器学习库方面强大,JavaScript则在前端开发库和框架上更胜一筹。3)两者的学习资源都丰富,但Python适合从官方文档开始,JavaScript则以MDNWebDocs为佳。选择应基于项目需求和个人兴趣。

Python vs. JavaScript:开发环境和工具 Python vs. JavaScript:开发环境和工具 Apr 26, 2025 am 12:09 AM

Python和JavaScript在开发环境上的选择都很重要。1)Python的开发环境包括PyCharm、JupyterNotebook和Anaconda,适合数据科学和快速原型开发。2)JavaScript的开发环境包括Node.js、VSCode和Webpack,适用于前端和后端开发。根据项目需求选择合适的工具可以提高开发效率和项目成功率。

See all articles