Vite已经成为前端工具链的关键。这个构建工具也被SvelteKit采用,它是Svelte的官方应用框架。因此,我认为可以这样说,Vite已经成为Svelte的首选开发工具。
Vitest是一个相对较新的、以Vite为基础的单元测试框架。它承诺成为Vite的理想测试伙伴,但它能实现吗?
在这篇文章中,我将探讨以下问题。
- 使用Vite和Svelte的现有测试框架会遇到什么问题?
- 用Vitest编写测试有多容易?
- 在现有项目上从Jest切换到Vitest是怎样的体验?
- Vitest的一些功能表现如何,它真的可以用于生产吗?
- SvelteKit怎么样?将Vitest与SvelteKit一起使用,是否需要额外的东西?
- Vitest的相关集成的工作情况如何(Web UI、IDE扩展)?
Svelte和Vite的测试框架
目前,Svelte并不推荐特定的单元测试框架,也不主张采用特定的测试策略。官方网站提供了一些基本建议。理论上,你可以使用任何你喜欢的JavaScript单元测试库进行测试,而你的前端工具链最终会吐出一个虚无的JavaScript捆绑包,对吗?
好吧,这就是问题的关键所在;你正在处理与JavaScript相关的不同的语法和格式的排列组合。这些格式会被你的工具链转化为另一种格式。当工具链中的工具不使用相同的语法,并且合作不力时,将它们集成到一个工具链中是很困难的。
Vite通过使用本地ECMAScript模块(ESM)提供按需的文件服务,提供了一个快速的开发环境。它可以对Svelte进行即时的原子式翻译,并将其翻译成JavaScript。许多单元测试框架都是使用CommonJS模块构建的,这是一个备用的模块标准。使用Vite来管理文件的翻译,然后将它们传递给用不同标准构建的测试框架,会产生摩擦。
Vite的单元测试故事还不清楚。现有的选项,如Jest,是在不同的环境下创建的。Jest和Vite之间有很多重复的地方,迫使用户配置两个不同的管道。
Jest可能是最受欢迎的单元测试框架;它在2021年的JS状态调查中名列前茅。Jest与Vite的集成是不完善的。有一个名为Vite-jest的库,旨在为Jest提供一流的Vite集成;然而,它目前是一项正在进行的工作。
Vite-jest没有提到Svelte,也不可能与Svelte一起工作。对于SvelteKit,有一个实验性的库叫svelte-add-jest。底线是,目前还没有一个明确、可靠的方法将Jest与Vite和Svelte一起使用。
在任何情况下,正如Vitest团队所提到的,Jest和Vite之间的工作是重复的。你最终会有一个用于开发的管道和一个用于测试的管道。对于Jest来说,你可能会使用Babel和一个插件来把Svelte翻译成JavaScript。然后,你需要把一些配套的部分拼接起来,使其发挥作用。这就有点像Rube Goldberg机器。
我在另一篇文章《用Jest测试Svelte应用程序》中提到了这一点。我创建了一个启动模板,将Vite、Svelte、Jest和Svelte测试库放在一起使用。它需要大约10个依赖项,以及两个额外的配置文件(.babelrc 和jest.config.json )来创建测试管道。
拥有这样的设置是很脆弱的,特别是当你不完全了解这些工具的内部工作原理时。我会祈祷这些依赖关系中的一个变化不会破坏这个链条!更多的环节,更多的失败的可能性如果你成为一个项目的维护者,这种感觉可不好。
图片来源:XKCD
总而言之,一个更加集成的解决方案是最好的。一个Vite-native的解决方案甚至更好。如果有一个与Jest兼容的API的Vite-native解决方案,那就更好了。在一个单一的配置文件中完成这一切将是涅槃。这有可能是Vitest所能提供的。
首先,让我们浏览一下API,看看我们的测试会是什么样子。
用Vitest编写测试
你可以阅读Vitest的API文档以获得更详细的信息,但为了回顾关键点,我将提供一个快速的API概述。
默认情况下,Vitest寻找以.spec.js 或.test.js 结尾的文件名。如果你愿意,你可以对它进行不同的配置。我把我的测试文件命名为<component-name>.spec.js ,并把它们与我的组件文件放在一起。
该API与Chaiassertions和Jest expect兼容。如果你以前使用过这些,这将是很熟悉的。关键的部分是。
describe块:用于将相关的测试组合成一个测试套件;一个套件可以让你组织你的测试,所以报告是清晰的;如果你想做进一步的聚合,你也可以嵌套它们test或 块:用于创建一个单独的测试itexpect语句:当你写测试时,你需要检查值是否符合某些条件--这些被称为断言; 函数提供了对几个expect"匹配器 "函数的访问,让你验证不同类型的东西(例如, , )。toBeNulltoBeTruthy
下面是一个基本的骨架,即对我们的Todo 组件进行测试的测试套件的样子。
import {describe, expect, it} from 'vitest';
import Todo from "./Todo.svelte";
describe("Todo", () => {
let instance = null;
beforeEach(() => {
//create instance of the component and mount it
})
afterEach(() => {
//destory/unmount instance
})
test("that the Todo is rendered", () => {
expect(instance).toBeDefined();
})
})
虽然你可以单独使用Vitest来运行测试,但你必须创建一个组件的实例,然后将其安装到文档中。我们可以在beforeEach 函数中这样做。你可能还需要在afterEach 中销毁或卸载这个实例。这很麻烦;这是最好避免的那种繁文缛节。
更常见的是使用Svelte测试库,它有助于推动你实现良好的测试实践。它有一个render 函数,为我们处理组件的渲染,并使用jsdom在内存中完成。你还可以通过jest-dom库获得更方便的匹配器函数,如toBeInTheDocument() 。
首先,你要用这个命令安装这些库。
npm i -D @testing-library/svelte jest-dom jsdom
我并不完全确定你是否需要自己安装jsdom。我可能被提示要安装它;我不记得确切的情况了!"。
现在,我们可以专注于编写测试。你可以看到,我们把我们的组件名称和道具传递给render 函数来渲染我们的组件。然后,我们可以通过一个隐含的screen 变量来访问HTML输出。我们可以运行查询方法来找到我们想测试的页面元素。
import { render, screen } from "@testing-library/svelte";
import Todo from "./Todo.svelte";
describe("Todo", () => {
const todoDone = { id: 1, text: "buy milk", done: true };
const todoNotDone = { id: 2, text: "do laundry", done: false };
test("shows the todo text when rendered", () => {
render(Todo, { props: { todo: todoDone } });
expect(screen.getByLabelText("Done")).toBeInTheDocument(); // checkbox
expect(screen.getByText(todoDone.text)).toBeInTheDocument();
});
});
将一个项目从Jest迁移到Vitest
在Vitest网站上有一个简短的迁移指南。
(深呼吸)
我们走吧!
我将分叉一个我之前制作的Todo应用,然后用Jest和Svelte测试库进行测试。我在测试中使用了<component_name>.spec.js 命名规则,与配套的组件一起。它的覆盖率为98.07%。
Todo应用程序有以下功能。
- 列出todos。当列表上没有项目时,应用程序会显示 "恭喜你,全部完成!"的信息。
- 记录进度。用户可以标记已完成的todos,并取消标记仍需关注的todos;已完成的todos的风格不同,有灰色文本和删除线装饰。
- 添加新的todos。用户可以添加一个新的待办事项,但该应用程序禁止添加一个空的待办事项。
下面是组件的概述。
安装
首先,我们安装Vitest。我是用npm安装的,但你也可以用你选择的软件包管理器。
# with npm
npm i -D vitest
# or with yarn
yarn add -D vitest
# or with pnpm
pnpm add -D vitest
接下来,让我们检查一下我们是否有所有东西的最新版本。Vitest需要Vite v2.7.10+和Node v14+。
对于Vite,我使用的是v2.6.4,所以我需要更新它。最快的方法是运行:npm i -D vite 。
我使用的Node是v14.18.1,所以那里不需要更新。如果你需要更新Node,你可以遵循这个指南。
配置
根据Vitest的迁移指南。
Jest默认启用了他们的globals API。Vitest则没有。你可以通过配置设置启用globals
[globals](https://vitest.dev/config/#globals)配置设置启用globals,或者更新你的代码,使用从vitest模块导入的方式。
让我们启用 [globals](https://vitest.dev/config/#globals)选项。下面是我们的vite.config.js 文件在启用了globals选项之后的样子。
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte()],
test: {
globals: true,
},
});
现在,让我们试着运行Vitest;命令:npx vitest run 将运行一次测试。
运行这个命令后,所有的16个测试都失败了!
有两种类型的错误。
- 无法找到
@testing-library/svelteNode模块。这是我们正在使用的Svelte测试库;我们可以再来看看这个 ReferenceError: 没有定义文件。document对象是在jsdom中定义的;这个是用来模拟DOM API的
我猜想,环境默认设置为Node,这与Jest最近的举动相同。让我们把环境变量改为jsdom 。
export default defineConfig({
plugins: [svelte()],
test: {
globals: true,
environment: "jsdom",
},
});
这样就解决了文档错误的问题。现在,有四个测试通过了。但是,我们仍然有七个失败。
其余的错误是不同的;它们被归类为无效的ChaipropertyError 。例如,无效的Chai属性:toBeInTheDocument 。
toBeInTheDocument 和toBeEnabled 函数来自jest-dom库,该库是 Svelte 测试库的一个配套。我们之前在测试文件中不需要导入语句,因为我们将Jest配置为包括jest-dom。下面是我们的Jest配置文件中的选项(jest.config.json)。
"setupFilesAfterEnv": ["@testing-library/jest-dom/extend-expect"]
为了在Vitest中做同样的事情,我们可以配置一个设置文件,在每个测试文件之前运行。我们可以通过 [setupFiles](https://vitest.dev/config/#setupfiles)选项来设置。
我们将创建一个src/setuptest.js 文件,其中包括以下import 语句。
import "@testing-library/jest-dom";
我们可以在vite.config.js 文件中更新我们的test 对象,使它看起来像这样。
export default defineConfig({
plugins: [svelte()],
test: {
globals: true,
environment: "jsdom",
setupFiles: ["src/setupTest.js"],
},
});
现在,有11个测试通过了,只有一个没有通过!我们几乎成功了!
FAIL src/components/Todo.spec.js [ src/components/Todo.spec.js ]
Error: Error: Cannot find module @testing-library/svelte/node_modules/@testing-library/dom imported from file:///home/rob/programming/workspace/js/svelte/svelte-todo-with-tests-(vitest), file:///home/rob/programming/workspace/js/svelte/svelte-todo-with-tests-(vitest)/node_modules
❯ MessagePort.[nodejs.internal.kHybridDispatch] internal/event_target.js:399:24
最后一个错误与Todo.spec.js 文件中的第二个import 语句有关。
import { render, screen } from "@testing-library/svelte";
import { fireEvent } from "@testing-library/svelte/node_modules/@testing-library/dom";
我不知道为什么我有第二个这样的import 语句!fireEvent 与第一条语句属于同一个库,所以我们应该可以把两条语句都浓缩到这样的一个import 。
import { render, screen, fireEvent } from "@testing-library/svelte";
我们好了吗?
是的!所有16个测试都通过了!
现在,让我们整理一下我们在package.json 文件中的scripts 。
scripts: {
"test": "npx vitest",
"coverage": "npx vitest run --coverage"
}
覆盖率报告需要一个额外的包,c8。让我们安装该软件包并运行覆盖率。
npm i -D c8
npm run coverage
下面是输出结果。
我们现在已经升级到了100%的覆盖率(在Jest中是98.07%)。奖励积分!
最后,我们可以删除Jest相关的依赖和配置。下面是我用来清理的命令。
rm jest.config.json .babelrc
npm uninstall -D @babel/preset-env babel-jest jest jest-transform-stub svelte-jester
我们删除了两个配置文件和五个依赖项。我感觉我在漂浮!
这里是这个项目的GitHub repo。
对Vitest迁移经验的看法
我给Vitest的迁移体验打7分(满分10分)。如果你使用的是jsdom、Svelte测试库或jest-dom,那么迁移指南就会让你感到不足。这是几个额外的、简短的步骤,可能会让你抓耳挠腮。如果该文件得到改进,我会给它打九分。能够在不编辑测试文件的情况下让一切都正常工作,这是一个令人惊喜的事情。
Vitest的功能表现
到目前为止,我们已经证实了Vitest的几个功能是可以工作的。
现在,让我们用我们的Todo项目来看看Vitest的其他一些重要功能。我正在尝试衡量Vitest是否可以投入生产。目前,Vitest的版本是0.14.1,所以我猜它仍被认为是测试版。
智能和即时观察模式
就像Vite在浏览器中的工作方式一样,Vitest也知道你的模块的图形。这使得Vitest能够进行智能检测,只重新运行与变化有关的测试。根据Vitest团队的说法,它"......感觉几乎就像HMR,不过是为了测试"。
让我们在观察模式下运行Vitest与npm run test ,并改变其中一个组件文件。
让我们打开AddTodo.svelte 文件,做一个突破性的改变。我将从button 中删除disabled 的属性,这应该会触发一个失败的测试。
Vitest只重新运行相关的测试套件(AddTodo 和App ),我们得到了一个失败的测试案例,正如预期的那样运行测试套件需要446ms,而运行所有的测试套件至少需要几秒钟!这个改进对生产力来说是非常好的。这一改进对于生产力来说是非常好的。
并发测试
我们可以将.concurrent 添加到一个套件或单个测试中,以并行运行它们。
import { render, screen, fireEvent } from "@testing-library/svelte";
import App from "./App.svelte";
describe.concurrent("App", () => {
/* all tests run in parallel */
})
让我们看看这是否加快了测试的速度!理论上,我的应用程序应该能够并发地运行所有的测试。
作为一个基准,从冷启动开始,运行我的四个测试套件需要5.11秒。将测试套件改为并发运行后,运行时间为3.97秒。它缩短了一秒多的时间
内建的TypeScript支持
测试一个TypeScript-Svelte应用程序似乎工作得很好。Johnny Magrippis有一个关于这个主题的详尽视频教程。他用SvelteKit制作了一个小型的货币仪表盘,并用Vitest进行测试。这些代码可以在这个GitHub repo中找到。
测试过滤。在命令行上定位测试
在命令行上锁定的测试文件可以通过传递一个名称/模式作为参数进行过滤。例如,下面的命令将只运行包含List 的文件。
npx vitest List
在我们的例子中,只有TodoList.spec 文件被运行。
跳过套件和测试
你也可以在describe 或test 函数中添加.skip 来避免运行某些套件或测试。
在这里,我通过在describe 函数中添加.skip 来跳过Todo 测试套件。正如你在上面看到的,输出告诉你哪些测试套件和测试被跳过。而且,颜色编码让你很容易发现它们。
在SvelteKit中使用Vitest
要在SvelteKit中使用Vitest,你需要添加测试依赖项。
npm i -D vitest @testing-library/svelte jest-dom jsdom
接下来,我们需要添加我之前在Jest到Vitest迁移示例中分享的相同配置。但是,我们到底该把配置放在哪里呢?
快速和肮脏的方法
我采取的方法是把我的配置放在项目根部的一个叫做vitest.config.js 的文件中。这个文件包含以下代码。
import { defineConfig } from "vite";
import { svelte } from "@sveltejs/vite-plugin-svelte";
// https://vitejs.dev/config/
export default defineConfig({
plugins: [svelte({ hot: !process.env.VITEST })],
test: {
globals: true,
environment: "jsdom",
setupFiles: ["src/setupTest.js"],
},
});
我还添加了导入jest-dom 的src/setupTest.js 文件。这使我们不必在每个文件中添加import 语句。src/setupTest.js 文件有以下内容。
import "@testing-library/jest-dom";
这很有效,但我是不是做了什么可疑的事情?
我不确定。但是,我看到有人提到另一种方法。
正确的方法是什么?
SvelteKit的配置存在于 [svelte.config.js](https://kit.svelte.dev/docs/configuration)文件中,该文件位于项目根部。有一个Vite选项,以Vite配置对象为其值。如果能在这里添加与Vite相关的选项就更好了,这样我们就能在一个单一的配置中获得所有的东西。我试过了,但没有成功。
我注意到Johnny Magrippis添加了一个名为vitest-svelte-kit的库,以便将Vitest相关的选项直接添加到 [svelte.config.js](https://kit.svelte.dev/docs/configuration)文件中。然而,这并不能自动工作。要使用这种技术,你需要做以下工作。
首先,安装vitest-svelte-kit。
npm i -D vitest-svelte-kit
接下来,将测试相关的东西添加到你的svelte.config.js 文件中的vite 对象中。
import adapter from "@sveltejs/adapter-auto"
import preprocess from "svelte-preprocess"
/** @type {import('@sveltejs/kit').Config} */
const config = {
// Consult https://github.com/sveltejs/svelte-preprocess
// for more information about preprocessors
preprocess: preprocess(),
kit: {
adapter: adapter(),
vite: {
test: {
environment: "jsdom",
globals: true,
setupFiles: 'src/setupTests.ts',
},
},
},
}
export default config
然后,创建一个vitest.config.js 文件,从库中暴露一个函数。
import { extractFromSvelteConfig } from "vitest-svelte-kit"
export default extractFromSvelteConfig()
我想vitest-svelte kit还没有完全解决所有的问题,但就我使用它的情况来看,它对我来说是很好的。
以后,我希望能有一个加法器。添加器是向SvelteKit项目添加集成的一种简单方式。一个添加器将使你在命令行上创建新的应用程序时包含Vitest成为可能。这将提供一个成熟的路径。所以,我们还没有完全达到使用单一配置文件的目的。
使用Vitest与相关的集成
我很惊讶地看到Vitest在浏览器和你的IDE中的测试已经得到了很好的支持。现在,让我们来看看Vitest是如何与Web UI和IDE整合的。
网页界面的集成
你可以在Web UI中使用Vitest。它需要一个额外的软件包,而且你应该用--ui 标志来运行它。
npm i -D @vitest/ui
接下来,你可以通过传递--ui 标志来启动Vitest。
npx vitest --ui
然后,你可以访问Vitest的用户界面,网址是 [http://localhost:51204/__vitest__/](http://localhost:51204/__vitest__/).
然而,我在Ubuntu的任何浏览器中都没有看到结果!我只看到一条细细的绿线。 我只看到一条细细的绿线!
集成开发环境整合
你也可以在IDE中使用Vitest。有一个VS Code的扩展和一个JetBrains产品的插件。
我把VS Code的扩展拿出来转了转,效果不错。它提供了一个侧边栏视图,你可以在那里运行测试。它可以启动一个调试会话,把你带到失败的测试的代码中。
总结
我对Vitest印象深刻。它的速度很快,智能手表模式很好,比竞争对手更容易配置,而且它融合了其他框架的最佳实践,提供了一种熟悉的测试体验。
能够将一个现有的项目从Jest迁移到Vitest,而不必改变测试文件,这是一个巨大的胜利。我认为将Vitest与Svelte和SvelteKit一起使用几乎是没有问题的。
虽然我把Vitest用于一个小的应用程序,但我不能说在一个更大的项目上工作时是否有任何问题,也不能说它如何管理更复杂的测试案例。我想,如果你把它用在一个生产应用上,你就会处于一个开拓性的空间;所以这里面有一些风险因素。
如果你已经在你的项目中使用了Jest,你可以在Jest旁边试用Vitest,而不需要去管你的测试文件。如果你在这个阵营中,这个策略将使你能够减轻风险。
总的来说,我建议将Vitest与Svelte一起使用。它有资金支持、全职团队成员和一个强大的社区,我期待Vitest有一个光明的未来!
The postTesting a Svelte app with Vitestappeared first onLogRocket Blog.