React Samples这个网站在实践编写测试代码方面非常有帮助

你是否了解以下的网站?

 

我记得我大约在2023年的某个时候发现了这个。
我不知道大家是通过怎样的途径来公开的,但是大家都在自己创建的网站上发布了一个简介页面和Github的存储库信息。

拿出了React示例

当个人想要加深对于测试代码的理解或者在工作现场需要编写代码时,但是项目规模太大而想要在小规模应用程序上进行测试时,我认为React Samples可能会有所帮助。

虽然有很多内容可以作为应用程序代码的参考,但这次,我觉得如果借用这个网站上的应用程序代码,就能够顺利地学习测试代码,所以我在这里做了总结。

React Testing Library和测试

 

这是一个在React测试实施中出现的库。

将用户在页面上实际执行的操作转化为代码,以确保功能的保证,也可以视为一个有帮助的库。(以下内容摘自上述链接。)

它所提供的主要功能包括查询 DOM 节点,以类似于用户在页面上查找元素的方式。通过这种方式,该库帮助确保您的测试能够让您对应用程序在真实用户使用时是否正常工作充满信心。

测试代码对于理解规格也很有帮助。
虽然与实现本身没有直接关系,但是查看测试代码有助于加深对规格的理解。
规格可以通过文档(维基?)或Excel来理解,但通过检查测试代码,可以更具体地看到在什么情况下会出现什么行为。
虽然我不完全依赖测试代码,但我认为它比可能出现的文档“更新遗漏”更可靠。

使用jest进行测试实施。

一旦实现了测试代码,就一定会使用到的库。

 

借用 Jest 核心团队的话。

Jest是一个为了保证JavaScript代码库的正确性而设计的JavaScript测试框架。它拥有友好易用且功能丰富的API,可以轻松编写测试并快速获得结果。Jest提供了丰富的文档,并且几乎不需要配置。此外,还可以根据需求进行扩展。Jest让测试变得有趣。

进行测试所需要的库

使用create-react-app创建项目时,package.json中已经预设了@testing-library/XXX库,该库是为了实现测试代码所需要的。

{
  "name": "airbnb-clone",
  "version": "0.1.0",
  "private": true,
  "dependencies": {
    "@testing-library/jest-dom": "^5.16.5",
    "@testing-library/react": "^13.4.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
/*このあたりは省略*/
}

我来写一下测试

我尝试用以下的三个步骤编写测试代码。

    • 表示文字列のテスト

 

    • Eventを用いたテスト

 

    TODOアプリのテスト
你实施了测试代码,确实进行了验证吗?有没有遗漏?
有时候思考这个问题很困难,但如果一开始就考虑这个问题,进展可能会很慢。
但首先还是试着写下来,即使只是进入到觉得可能已经能够测试这个行为的阶段,这也算是一种进步吧!
让我们轻松地开始吧!

文字列测试

我认为这个内容在测试代码中属于初级部分,可以比较直观地进行实现。
启动应用程序,测试用例是在“当屏幕上显示一定文字内容时,期望该文字被正确显示”的情况下。

 

image.png

我将上述画面的应用程序代码和测试代码排列在一起。

import React from "react";
import photoGrid from "../img/photoGrid.png"

export default function Hero() {
    return (
        <section className="hero">
            <img src={photoGrid} className="hero--photo" alt="Photo Grid"></img>
            <h1 className="hero--title">Online Experiences</h1>
            <p className="hero--subtitle">Join unique interactive activities led by one-of-a-kind hosts—all without leaving home.</p>
        </section>
    )
}
import { render, screen } from '@testing-library/react';
import Hero from './Hero';

test('Hero', () => {
    render(<Hero />);
    screen.debug(); // ※1
    const displayedImage = document.querySelector("img");
    expect(displayedImage.src).toContain("photoGrid"); // ※2
    const linkElement = screen.getByText(/Online Experiences/i);
    expect(linkElement).toBeInTheDocument(); // ※3
});

※1 screen.debug():显示执行时的DOM。

控制台内容

Photo Grid

在线体验

参加由独特主持人带领的独特互动活动,而不用离开家。

 

※2 我使用了这里提供的方法来进行有关图像文件的测试实施。

如果在`expect(linkElement).toBeInTheDocument();` 進行測試時發生錯誤,表示你所編寫的測試程式碼無法正常運作,會顯示以下錯誤訊息。

TypeError: expect(...).toBeInTheDocument is not a function

由于缺少导入语句,导致发生错误。
如果在使用expect(…).toBeInTheDocument()的文件中添加import ‘@testing-library/jest-dom/extend-expect’;,问题将得到解决。
但是,由于在每个测试用例都需要处理期望值相关的操作,所以必须在所有文件中都添加这个导入语句。。。

在src文件夹中创建一个名为setupTests.js的文件,并在其中定义import语句。这样一来,每个文件都不需要再写import语句了。

import '@testing-library/jest-dom/extend-expect';

使用Event进行测试

突然測試ToDo應用程式?這不難嗎?如果覺得困難,建議只實現Click事件和useState功能,例如計數器等。這個是我創建的,與React示例代碼無關。

import { useState } from "react"

export default function Counter() {
    const [count, setCount] = useState(0)
    const increment = () => {
        setCount(count => count + 1)
    }

    return (
        <>
            {count}
            <button onClick={increment}>Increment</button>
        </>
    )
}
import { render, screen, fireEvent } from '@testing-library/react';
import Counter from './Counter';

test('Counter', () => {
    render(<Counter />);
    const count = screen.getByText(/0/i);
    // 初期表示では0であること
    expect(count).toBeInTheDocument();
    const buttonElement = screen.getByRole("button");
    fireEvent.click(buttonElement)
    const countUpdate = screen.getByText(/1/i);
    // 1に更新されること
    expect(countUpdate).toBeInTheDocument();
});

测试待办事项应用

我使用了以下文章中提到的应用程序代码。

 

示威活动

 

在Next.js中
本次使用的TODO应用程序不是用React,而是用Next.js制作的。
通过create-react-app创建的应用程序默认安装了@testing-library,但在Next.js中有所不同,需要从设置中进行安装。

 

参考以下存储库的package.json来确认所需的库信息。

 

npm i -D @testing-library/jest-dom @testing-library/react @testing-library/user-event jest jest-environment-jsdom
const nextJest = require('next/jest')

const createJestConfig = nextJest({
    // Provide the path to your Next.js app to load next.config.js and .env files in your test environment
    dir: './',
})

// Add any custom config to be passed to Jest
const customJestConfig = {
    setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
    testEnvironment: 'jest-environment-jsdom',
}

// createJestConfig is exported this way to ensure that next/jest can load the Next.js config which is async
module.exports = createJestConfig(customJestConfig)
// Optional: configure or set up a testing framework before each test.
// If you delete this file, remove `setupFilesAfterEnv` from `jest.config.js`

// Used for __tests__/testing-library.js
// Learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom/extend-expect'

以下是关于两个案例的测试代码介绍。

    • テキストボックスに値を入力して、タスクを追加

 

    追加したタスクに対してチェックをつける/外す

我的其他测试代码已经在以下存储库中公开。

 

在文本框中输入数值,并添加任务。

应用程序代码如下。

 

import Home from '@/app/page'
import { fireEvent, render, screen } from '@testing-library/react'

describe('Home', () => {
    it('Allタブを表示しているとき、タスク追加ができること', () => {
        render(<Home />)

        const inputElement = screen.getByPlaceholderText(/add details/i) as HTMLInputElement;

        fireEvent.change(inputElement, {
            target: { value: 'Learn Testing Library' },
        });
        // テキストボックスの値が更新できること
        expect(inputElement.value).toBe('Learn Testing Library');

        const onButtonElement = screen.getByRole('button', { name: 'Add' });
        fireEvent.click(onButtonElement);
        //タスクが追加が行えること
        const helloTask = screen.getByText('Learn Testing Library')
        expect(helloTask).toBeInTheDocument();

    })
})

使用fireEvent来执行与界面相关的事件,但似乎有时候还可以使用userEvent。(本次没有进行此验证)

 

在追加的任务上勾选/取消勾选

应用程序代码如下。

 

import Todo from '@/components/Todo'
import { fireEvent, render, screen } from '@testing-library/react'

describe('Home', () => {

    // 親コンポーネントの関数(deleteTodo)のモック
    const mockFunction = jest.fn();
    // useStateのモック
    const mockSetXXX = jest.fn();

    jest.mock('../app/page', () => ({
        default: () => ({
            deleteTodo: mockFunction,
            setReload: mockSetXXX,
        }),
    }));

    it('renders task and check task', () => {
        // サンプルタスクを設定
        render(<Todo
            menuActive={'all'}
            todo={{
                id: '1',
                text: 'Sample Task',
                checked: false
            }}
            reload={false}
            setReload={mockSetXXX}
            deleteTodo={mockFunction} />
        )

        const sampleTaskCheckBox = screen.getByRole('checkbox') as HTMLInputElement;
        // チェックボックスを更新
        fireEvent.click(sampleTaskCheckBox);
        expect(sampleTaskCheckBox.checked).toBe(true);
        // チェックボックスを更新
        fireEvent.click(sampleTaskCheckBox);
        expect(sampleTaskCheckBox.checked).toBe(false);
    })
})

在这个样本中,可能有以下类似的测试案例。

    • AllからActiveやCompletedタブに切り替えたときの表示内容

 

    • Completedタブ:ごみ箱アイコンクリック

 

    Completedタブ:delete allボタンをクリック
当涉及到Todo应用的规模,实施方法可能会产生差异时,我认为测试代码的精度就很重要。
根据实施方法的不同,编写测试代码的难易程度也可能会有所变化。
到底该如何编写应用代码才能得到易于编写的测试代码呢?我们实施的测试代码是否真的合适呢?
个人判断的能力是有限的,为了创建更精良的测试形式,我们可能需要其他人的意见。
这次主要是关于React Samples应用代码作为素材,以便于编写测试的讨论。
当然,我本人也尽量按照能够验证测试代码的实现方式进行了实施,但也有可能存在写作时产生了其他更好的方式的部分。

Jest的测试编写方式可以参考以下文章。

 

此外,这次的示例只是一个应用程序,仅限于根路径。但是,也可能存在从某个路径跳转到另一个路径的情况。

 

总结

我們討論了根據React Samples網站上的代碼為基礎撰寫測試代碼的事情。

这次我们使用React测试库进行了讨论,但应该也有使用Vite和vitest进行结构布置的选项。我打算也去看一下那个部分。

虽然我在现场也有一些实施测试代码,但还有很多不足,我会一边增加知识,一边实施。我打算每天努力进取。

非常感谢你。

广告
将在 10 秒后关闭
bannerAds