入门javascript

tech2022-09-07  120

入门javascript

Functional programming and testing. Maybe you’ve given them a try in isolation, but somehow you never made either a part of your regular practice. They may sound innocent by themselves, but together testing and functional programming can create an irresistible temptation, almost compelling you to write cleaner, tighter, more maintainable code.

功能编程和测试。 也许您已经孤立地尝试了它们,但是以某种方式,您从未成为常规练习的一部分。 它们本身听起来可能很无辜,但是将测试和函数式编程结合在一起会产生难以抗拒的诱惑,几乎迫使您编写更简洁,更紧凑,更可维护的代码。

Well the good news is that working with both techniques together can offer some real advantages. In fact, once you’ve tasted how sweet this combination can be, you might find yourself as addicted to it as I am, and I’d be willing to bet that you’ll be coming back for more.

好消息是,同时使用这两种技术可以提供一些真正的优势。 实际上,一旦您尝到了这种组合的甜美,您可能会发现自己像我一样沉迷于此,我愿意打赌您会再来的。

In this article, I’ll introduce you to the principles of testing functional JavaScript. I’ll show you how to get up and running with the Jasmine framework and build out a pure function using a test-driven approach.

在本文中,我将向您介绍测试功能JavaScript的原理。 我将向您展示如何使用Jasmine框架启动和运行,以及如何使用测试驱动的方法构建纯函数。

为什么要测试? (Why Test?)

Testing is about making sure that the code in your application does what you expect it to do, and keeps doing what you expect it to do when you make changes so you have a working product when you’re done. You write a test that defines your expected functionality under a defined set of circumstances, run that test against the code, and if the result isn’t what the test says it should be, you get a warning. And you keep getting that warning until you’ve fixed your code.

测试是要确保应用程序中的代码能够实现您期望的功能,并在进行更改时继续执行期望的功能,以使您在完成操作后拥有正常的产品。 您编写了一个在定义的特定情况下定义预期功能的测试,并针对代码运行该测试,如果结果与测试所说的不符,则会收到警告。 而且,在修复代码之前,您会不断收到该警告。

Then you get the reward.

然后,您将获得奖励。

And yes, it will make you feel good.

是的,它将使您感觉良好。

Testing comes in a lot of flavors, and there is room for healthy debate about where the borders are drawn, but in a nutshell:

测试有很多风格,对于划定边界的位置,存在进行健康辩论的空间,但总而言之:

Unit tests validate the functionality of isolated code

单元测试验证隔离代码的功能 Integration tests verify the flow of data and the interaction of components

集成测试可验证数据流和组件之间的交互 Functional tests look at the behavior of the overall application

功能测试着眼于整个应用程序的行为

Note: Don’t get distracted by the fact that there’s a type of testing called functional testing. That’s not what we’ll be focusing on in this article about testing functional JavaScript. In fact, the approach you’ll use for functional testing of the overall behavior of an application probably won’t change all that much whether or not you’re using functional programming techniques in your JavaScript. Where functional programming really helps out is when you’re building your unit tests.

注意:不要因为存在一种称为功能测试的测试而分心。 这不是我们在测试功能JavaScript时将重点关注的内容。 实际上,无论您是否在JavaScript中使用功能编程技术,将用于对应用程序整体行为进行功能测试的方法都不会改变太多。 函数式编程真正帮助您的地方是在构建单元测试时。

You can write a test at any point in the coding process, but I’ve always found that its most efficient to write a unit test before writing the function you’re planning to test. This practice, known as test-driven development (TDD), encourages you to break down the functionality of your application before you start writing and determine what results you want from each section of code, writing the test first, then coding to produce that result.

您可以在编码过程中的任何时候编写测试,但是我一直发现,在编写打算测试的功能之前编写单元测试最有效。 这种做法称为测试驱动开发(TDD) ,它鼓励您在开始编写之前确定应用程序的功能,并从代码的每个部分确定所需的结果,首先编写测试,然后进行编码以产生该结果。

A side benefit is that TDD often forces you to have detailed conversations with the people who are paying you to write your programs, to make sure that what you’re writing is actually what they’re looking for. After all, it’s easy to make a single test pass. What’s hard is determining what to do with all the likely inputs you’re going to encounter and handle them all correctly without breaking things.

附带的好处是,TDD经常迫使您与付钱给您编写程序的人员进行详细的对话,以确保您所写的内容确实是他们所要的。 毕竟,通过一次测试很容易。 困难的是确定如何处理您将要遇到的所有可能的输入,并正确处理所有这些输入而又不中断任何事情。

为什么起作用? (Why Functional?)

As you can imagine, the way you write your code has a lot to do with how easy it is to test. There are some code patterns, such as tightly coupling the behavior of one function to another, or relying heavily on global variables, that can make code much more difficult to unit test. Sometimes you may have to use inconvenient techniques such as “mocking” the behavior of an external database or simulating a complicated runtime environment in order to establish testable parameters and results. These situations can’t always be avoided, but it is usually possible to isolate the places in the code where they are required so that the rest of the code can be tested more easily.

可以想象,编写代码的方式与测试的容易程度有很大关系。 有一些代码模式,例如将一个函数的行为紧密耦合到另一个函数,或者严重依赖全局变量,可能会使代码难以进行单元测试。 有时,可能需要使用一些不便的技术,例如“模拟”外部数据库的行为或模拟复杂的运行时环境,以便建立可测试的参数和结果。 不能总是避免这些情况,但是通常可以将代码中需要的位置隔离开来,以便可以更轻松地测试其余代码。

Functional programming allows you to deal with the data and the behavior in your application independently. You build your application by creating a set of independent functions that each work in isolation and don’t rely on external state. As a result, your code becomes almost self-documenting, tying together small clearly defined functions that behave in consistent and understandable ways.

函数式编程使您可以独立处理应用程序中的数据和行为。 您可以通过创建一组独立的函数来构建应用程序,每个函数可以独立工作并且不依赖外部状态。 结果,您的代码几乎变成了自我记录文件,将明确定义的小功能捆绑在一起,以一致且可理解的方式运行。

Functional programming is often contrasted against imperative programming and object-oriented programming. JavaScript can support all of these techniques, and even mix-and-match them. Functional programming can be a worthwhile alternative to creating sequences of imperative code that track the state of the application across multiple steps until a result is returned. Or building your application out of interactions across complex objects that encapsulate all of the methods that apply to a specific data structure.

函数式编程通常与命令式编程和面向对象的编程形成对比。 JavaScript可以支持所有这些技术,甚至可以混合和匹配它们。 函数式编程可以替代创建命令性代码序列的有价值的替代方法,该命令性代码跨多个步骤跟踪应用程序的状态,直到返回结果为止。 或者,通过复杂对象之间的交互来构建应用程序,这些对象封装了适用于特定数据结构的所有方法。

纯函数如何工作 (How Pure Functions Work)

Functional programming encourages you to build your application out of tiny, reusable, composable functions that just do one specific thing and return the same value for the same input every single time. A function like this is called a pure function. Pure functions are the foundation of functional programming, and they all share these three qualities:

函数式编程鼓励您从微小的,可重用的,可组合的函数中构建应用程序,这些函数只做一件特定的事情,并且每次都为相同的输入返回相同的值。 这样的函数称为纯函数 。 纯函数是函数式编程的基础,它们都具有以下三个特质:

Don’t rely on external state or variables

不要依赖外部状态或变量 Don’t cause side effects or alter external variables

不要引起副作用或更改外部变量 Always return the same result for the same input

对于相同的输入总是返回相同的结果

Another advantage of writing functional code is that it makes it much easier to do unit testing. The more of your code that you can unit test, the more comfortably you can count on your ability to refactor the code in the future without breaking essential functionality.

编写功能代码的另一个优点是,它使单元测试变得更加容易。 您可以进行单元测试的代码越多,您就越可以依靠自己将来在不破坏基本功能的情况下重构代码的能力。

是什么使功能代码易于测试? (What Makes Functional Code Easy to Test?)

If you think about the concepts that we just discussed, you probably already see why functional code is easier to test. Writing tests for a pure function is trivial, because every single input has a consistent output. All you have to do is set the expectations and run them against the code. There’s no context that needs to be established, there are no inter-functional dependencies to keep track of, there’s no changing state outside of the function that needs to be simulated, and there are no variable external data sources to be mocked out.

如果考虑一下我们刚刚讨论的概念,您可能已经知道为什么功能代码更易于测试。 为纯函数编写测试是微不足道的,因为每个输入都具有一致的输出。 您要做的就是设置期望,并根据代码运行它们。 没有需要建立的上下文,没有要跟踪的功能间依赖关系,没有需要模拟的功能外部状态变化,也没有可以模拟的可变外部数据源。

There are a lot of testing options out there ranging from full-fledged frameworks to utility libraries and simple testing harnesses. These include Jasmine, Mocha, Enzyme, Jest, and a host of others. Each one has different advantages and disadvantages, best use cases, and a loyal following. Jasmine is a robust framework that can be used in a wide variety of circumstances, so here’s a quick demonstration of how you might use Jasmine and TDD to develop a pure function in the browser.

有许多测试选项,从成熟的框架到实用程序库和简单的测试工具。 其中包括Jasmine , Mocha , Enzyme , Jest以及其他一些。 每个人都有不同的优缺点,最佳用例和忠实的追随者 。 Jasmine是一个健壮的框架,可以在多种情况下使用,因此这里快速演示了如何使用Jasmine和TDD在浏览​​器中开发纯函数。

You can create an HTML document that pulls in the Jasmine testing library either locally or from a CDN. An example of a page including the Jasmine library and test runner might look something like this:

您可以创建一个HTML文档,以从本地或从CDN提取Jasmine测试库。 包含Jasmine库和测试运行器的页面示例可能看起来像这样:

<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Jasmine Test</title> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.6.1/jasmine.min.css"> </head> <body> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.6.1/jasmine.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.6.1/jasmine-html.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/jasmine/2.6.1/boot.min.js"></script> </body> </html>

This brings in the Jasmine library, along with the Jasmine HTML boot script and styling. In this case the body of the document is empty, waiting for your JavaScript to test, and your Jasmine tests.

这引入了Jasmine库,以及Jasmine HTML启动脚本和样式。 在这种情况下,文档正文为空,等待JavaScript测试和Jasmine测试。

测试功能性JavaScript —我们的第一个测试 (Testing Functional JavaScript — Our First Test)

To get started, let’s write our first test. We can do this in a separate document, or by including it inside a <script> element on the page. We’re going to use the describe function defined by the Jasmine library to describe the desired behavior for a new function we haven’t written yet.

首先,让我们编写第一个测试。 我们可以在单独的文档中执行此操作,也可以将其包含在页面上的<script>元素中。 我们将使用Jasmine库定义的describe函数来描述尚未编写的新函数的所需行为。

The new function we’re going to write will be called isPalindrome and it will return true if the string passed in is the same forwards and backwards, and return false otherwise. The test will look like this:

我们将要编写的新函数称为isPalindrome ,如果传入的字符串前后相同,它将返回true ,否则返回false 。 测试将如下所示:

describe("isPalindrome", () => { it("returns true if the string is a palindrome", () => { expect(isPalindrome("abba")).toEqual(true); }); });

When we add this to a script in our page and load it a browser, we get a working Jasmine report page showing an error. Which is what we want at this point. We want to know that the test is being run, and that it’s failing. That way our approval-hungry brain knows that we have something to fix.

当我们将其添加到页面中的脚本并加载到浏览器中时,我们会得到一个有效的Jasmine报告页面,其中显示了错误。 这是我们现在想要的。 我们想知道测试正在运行,并且正在失败。 这样,渴望得到批准的大脑就知道我们有一些要解决的问题。

So let’s write a simple function in JavaScript, with just enough logic to get our test to pass. In this case it’s just going to be a function that makes our one test pass by returning the value expected.

因此,让我们用JavaScript编写一个简单的函数,只需足够的逻辑即可使测试通过。 在这种情况下,它将只是一个函数,它通过返回期望的值来通过一次测试。

const isPalindrome = (str) => true;

Yes, really. I know it looks ridiculous, but hang in there with me.

对真的。 我知道它看起来很荒谬,但是和我一起呆在那里。

When the test runner again, it passes. Of course. But obviously this simple code does not do what we might expect a palindrome tester to do. We’ve written the minimal amount of code that makes the test pass. But we know that our code would fail to evaluate palindromes effectively. What we need at this point are additional expectations. So let’s add another assertion to our describe function:

当测试跑步者再次通过时。 当然。 但是很显然,这个简单的代码并不能实现回文测试人员所期望的。 我们编写了使测试通过的最少代码。 但是我们知道我们的代码将无法有效地评估回文。 这时我们需要的是额外的期望。 因此,让我们在describe函数中添加另一个断言:

describe("isPalindrome", () => { it("returns true if the string is a palindrome", () => { expect(isPalindrome("abba")).toEqual(true); }); it("returns false if the string isn't a palindrome", () => { expect(isPalindrome("Bubba")).toEqual(false); }); });

Reloading our page now makes the test output turn red and fail. We get a messages saying what the problem is, and the test result turns red.

现在,重新加载页面会使测试输出变为红色并失败。 我们收到一条消息,说明问题出在哪里,测试结果变为红色。

Red!

红!

Our brains sense that there’s a problem.

我们的大脑感觉到有问题。

Of course there is. Now our simple isPalindrome function that just returns true every time has been demonstrated not to work effectively against this new test. So let’s update isPalindrome adding the ability to compare a string passed in forward and backward.

当然有。 现在,我们isPalindrome了简单的isPalindrome函数每次都返回true,已证明不能有效地对抗此新测试。 因此,让我们更新isPalindrome ,它可以比较向前和向后传递的字符串。

const isPalindrome = (str) => { return str .split("") .reverse() .join("") === str; };

测试令人上瘾 (Testing Is Addictive)

Green again. Now that’s satisfying. Did you get that little dopamine rush when you reloaded the page?

再次绿色。 现在很令人满意。 重新加载页面时,多巴胺催促了吗?

With these changes in place, our test passes again. Our new code effectively compares the forward and backward string, and returns true when the string is the same forward and backward, and false otherwise.

完成这些更改后,我们的测试再次通过。 我们的新代码有效地比较了前进和后退字符串,并且当字符串前后相同时返回true ,否则返回false 。

This code is a pure function because it is just doing one thing, and doing it consistently given a consistent input value without creating any side effects, making any changes to variables outside of itself, or relying on the state of the application. Every time you pass this function a string, it does a comparison between the forward and backward string, and returns the result regardless of when or how it is called.

这段代码是纯函数,因为它只是在做一件事,并且在给定一致的输入值的情况下以一致的方式执行此操作,而不会产生任何副作用,对其自身外部的变量进行任何更改或依赖于应用程序的状态。 每次向该函数传递一个字符串时,它都会在向前和向后字符串之间进行比较,并返回结果,而不管调用时间和方式如何。

You can see how easy that kind of consistency makes this this function to unit test. In fact, writing test-driven code can encourage you to write pure functions because they are so much easier to test and modify.

您可以看到这种一致性使此功能易于进行单元测试。 实际上,编写测试驱动的代码可以鼓励您编写纯函数,因为它们非常容易测试和修改。

And you want the satisfaction of a passing test. You know you do.

并且您希望通过测试的满意度。 你知道的

重构纯函数 (Refactoring a Pure Function)

At this point it’s trivial to add additional functionality, such as handling non-string input, ignoring differences between uppercase and lowercase letters, etc. Just ask the product owner how they want the program to behave. Since we already have tests in place to verify that strings will be handled consistently, we can now add error checking or string coercion or whatever behavior we like for non-string values.

在这一点上,添加其他功能很简单,例如处理非字符串输入,忽略大写和小写字母之间的差异等。只需询问产品所有者,他们就希望程序表现如何。 由于我们已经进行了测试以验证字符串将被一致地处理,因此我们现在可以添加错误检查或字符串强制或我们喜欢的非字符串值行为。

For example, let’s see what happens if we add a test for a number like 1001 that might be interpreted a palindrome if it were a string:

例如,让我们看看如果为一个像1001这样的数字添加一个测试,如果它是一个字符串,可能会被解释为回文,那么会发生什么:

describe("isPalindrome", () => { it("returns true if the string is a palindrome", () => { expect(isPalindrome("abba")).toEqual(true); }); it("returns false if the string isn't a palindrome", () => { expect(isPalindrome("Bubba")).toEqual(false); }); it("returns true if a number is a palindrome", () => { expect(isPalindrome(1001)).toEqual(true); }); });

Doing this gives us a red screen and a failing test again because our current is isPalindrome function doesn’t know how to deal with non-string inputs.

这样做会给我们带来红​​色屏幕,并再次导致测试失败,因为当前的isPalindrome函数不知道如何处理非字符串输入。

Panic sets in. We see red. The test is failing.

恐慌开始。我们看到红色。 测试失败。

But now we can safely update it to handle non-string inputs, coercing them into strings and checking them that way. We might come up with a function that looks a little bit more like this:

但是现在我们可以安全地对其进行更新,以处理非字符串输入,将其强制为字符串并以这种方式进行检查。 我们可能会想出一个看起来像这样的函数:

const isPalindrome = (str) => { return str .toString() .split("") .reverse() .join("") === str.toString(); };

And now all our tests pass, we’re seeing green, and that sweet, sweet dopamine is flooding into our our test-driven brains.

现在我们所有的测试都通过了,我们看到了绿色,那甜美的多巴胺正泛滥到我们测试驱动的大脑中。

By adding toString() to the evaluation chain, we’re able to accommodate non-string inputs and convert them to strings before testing. And best of all, because our other tests are still being run every time, we can be confident that we haven’t broken the functionality we got before by adding this new capability to our pure function. Here’s what we end up with:

通过将toString()添加到评估链中,我们可以容纳非字符串输入,并在测试之前将其转换为字符串。 最好的是,由于我们的其他测试每次仍在运行,因此我们可以确信,通过将这一新功能添加到纯函数中,我们并未破坏以前获得的功能。 这就是我们的最终结果:

See the Pen A Beginner’s Guide to Testing Functional JavaScript by SitePoint (@SitePoint) on CodePen.

见笔初学者的指南,以测试功能JavaScript由SitePoint( @SitePoint上) CodePen 。

Play around with this test, and start writing some of your own, using Jasmine or any other testing library that suits you.

进行此测试,然后使用Jasmine或其他适合您的测试库开始编写自己的一些测试。

Once you incorporate testing into your code design workflow, and start writing pure functions to unit test, you may find it hard to go back to your old life. But you won’t ever want to.

一旦将测试合并到代码设计工作流中,并开始编写用于单元测试的纯函数,您可能会发现很难回到过去。 但是您永远不会想要。

This article was peer reviewed by Vildan Softic. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

本文由Vildan Softic同行评审。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态!

翻译自: https://www.sitepoint.com/testing-functional-javascript/

入门javascript

最新回复(0)