IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> react单元测试和集成测试 -> 正文阅读

[开发测试]react单元测试和集成测试

一、TDD(Test-Driven-Development)的认识

TDD简单点来说就是测试驱动开发,这里说的测试并不是指开发团队中的测试小组来驱动开发者写代码,而是开发人员根据业务场景编写的单元测试文件,通过单元测试文件来写业务代码,直到业务代码都通过单元测试为止。

整个环节可以拆分为:

在这里插入图片描述

二、自己手动实践一个TDD

  • 1、创建一个react项目

    npx create-react-app react-demo01 --template typescript
    
  • 2、可以将react配置映射出来,查看react的测试包,正常项目是不需要反射出来的

    "@testing-library/jest-dom": "^5.11.4",
    "@testing-library/react": "^11.1.0",
    "@testing-library/user-event": "^12.1.10",
    "@types/jest": "^26.0.15",
    
  • 3、这里需要安装依赖包来对ts的支持

    # 因为使用的是ts需要安装
    npm install @types/jest -D
    
  • 4、这里直接使用bootstrap来布局,最终要交付的产品如下图

    在这里插入图片描述

    根据业务来拆分组件

    • Message.tsx是一个列表值的组件
    • MessageList.tsx是全部列表组件的集合
    • MessageForm.tsx是下面表单提交的组件
    • MessageApp.tsx是一个整体组件
  • 5、先编写__tests__/Message.tsx单元测试

    业务 场景分析: 希望渲染一个li标签,标签的类名是list-group-item里面要有内容

    // src/__tests__/Message.tsx
    import React from 'react';
    // 可以先写一个空的组件,先把单元测试写完
    import Message from './../../components/Message';
    import { render, screen } from '@testing-library/react';
    
    describe('测试Message组件', () => {
      test('应该渲染一个li,类名是list-group-item', () => {
        const message: string = 'hello word';
        // 渲染组件
        render(<Message message={message} />);
        // 查询组件中是否有li标签
        expect(screen.getByRole('item').nodeName.toLocaleLowerCase()).toBe('li');
        expect(screen.getByRole('item').isEqualNode).toBeTruthy();
        // 判断类名
        expect(screen.getByRole('item').className).toBe('list-group-item');
        // 判断内容
        expect(screen.getByRole('item').textContent).toBe(message);
      });
    });
    
    
  • 6、根据测试用例编写组件

    // src/components/Message.tsx
    
    import React, { PropsWithChildren } from 'react';
    type IProps = PropsWithChildren<{
      message: string;
    }>;
    const Message: React.FC<IProps> = (props: IProps) => {
      const { message } = props;
      return <li className='list-group-item' role='item'>{message}</li>;
    };
    
    export default Message;
    

    在这里插入图片描述

  • 7、src/__tests__/MessageList.tsx组件

    // 测试用例
    import React from 'react';
    import { render, screen } from '@testing-library/react';
    import MessageList from './../../components/MessageList';
    
    describe('测试MessageList组件', () => {
      test('测试当前组件是否为ul,并且类名是list-group', () => {
        const messageList: string[] = ['hello', 'word'];
        render(<MessageList messageList={messageList} />);
        // 判断节点为ul
        expect(screen.getByRole('ul').nodeName.toLocaleLowerCase()).toBe('ul');
        // 判断当前节点的类名
        expect(screen.getByRole('ul').className).toBe('list-group');
      });
    
      test('测试当前组件下面是否为li', () => {
        const messageList: string[] = ['hello', 'word'];
        render(<MessageList messageList={messageList} />);
        // 当前的节点是ul
        expect(screen.getByRole('ul').className).toBe('list-group');
        // 下面有2个节点
        expect(screen.getByRole('ul').childNodes.length).toEqual(2);
        // 直接查询子节点判断
        expect(screen.getAllByRole('item').length).toEqual(2);
        // 判断节点是li
        expect(screen.getByRole('ul').lastChild.nodeName.toLocaleLowerCase()).toBe(
          'li'
        );
      });
    });
    
    // 组件代码
    import React, { PropsWithChildren } from 'react';
    import Message from './Message';
    type IProps = PropsWithChildren<{
      messageList: string[];
    }>;
    const MessageList: React.FC<IProps> = (props: IProps) => {
      const { messageList } = props;
      return (
        <ul className='list-group' role='ul' data-testid='list-group'>
          {messageList.map((item: string, index: number) => {
            return <Message key={index} message={item} />;
          })}
        </ul>
      );
    };
    
    export default MessageList;
    
  • 8、表单的

    import React from 'react';
    import { render, screen, fireEvent } from '@testing-library/react';
    import MessageForm from './../../components/MessageForm';
    
    describe('测试MessageForm组件', () => {
      test('测试当前是结构', () => {
        const addMessage = jest.fn();
        render(<MessageForm addMessage={addMessage} />);
        // 查询到节点看是不是这个
        expect(screen.getByTestId('form').nodeName.toLocaleLowerCase()).toBe(
          'form'
        );
        // 判断下面是否有节点input和button
        expect(screen.getByTestId('button')).toBeTruthy();
        expect(
          screen.getByTestId('form').getElementsByTagName('input')
        ).toBeTruthy();
        expect(
          screen.getByPlaceholderText('请输入留言内容').nodeName.toLocaleLowerCase()
        ).toBe('input');
      });
    
      test('测试表单的事件', () => {
        const addMessage = jest.fn();
        render(<MessageForm addMessage={addMessage} />);
        const message: string = 'hello word';
        // 给input赋值
        fireEvent.change(screen.getByTestId('message'), {
          target: { value: message },
        });
        // expect(screen.getByTestId('message').nodeValue).toBe(message);
        // 模拟点击事件
        fireEvent.click(screen.getByTestId('button'));
        // 期望这个函数被调用
        expect(addMessage).toBeCalled();
      });
    
    	test('测试表单内容为空的时候不提交', () => {
        const addMessage = jest.fn();
        render(<MessageForm addMessage={addMessage} />);
        const message: string = '';
        fireEvent.change(screen.getByTestId('message'), {
          target: { value: message },
        });
    		// 判断输入框的值
        (expect(screen.getByTestId('message')) as any).toHaveValue(message);
        fireEvent.click(screen.getByTestId('button'));
        expect(addMessage).not.toBeCalled();
      });
    });
    
    import React, { PropsWithChildren, useRef } from 'react';
    
    type IProps = PropsWithChildren<{
      addMessage: (message: string) => void;
    }>;
    const MessageForm: React.FC<IProps> = (props: IProps) => {
      const { addMessage } = props;
      const inputRef = useRef<HTMLInputElement | null>(null);
    
      const submitHandler = (
        event: React.MouseEvent<HTMLButtonElement, MouseEvent>
      ): void => {
        event.preventDefault();
        if (inputRef.current!.value) {
          addMessage(inputRef.current!.value);
          inputRef.current!.value = '';
        }
      };
      return (
        <form data-testid='form'>
          <div className='form-group'>
            <label htmlFor='message'>留言内容:</label>
            <input
              type='text'
              className='form-control'
              data-testid='message'
              ref={inputRef}
              placeholder='请输入留言内容'
            />
          </div>
          <button
            type='button'
            data-testid='button'
            className='btn btn-primary'
            style={{ width: '100%' }}
            onClick={submitHandler}
          >
            提交
          </button>
        </form>
      );
    };
    
    export default MessageForm;
    
  • 9、同理MessageApp.tsx的单元测试也类似

三、BDD(Behavior Driven Development)行为驱动测试

BDDTDD的一般技术和原理与领域驱动设计(DDD)的想法相结合。 BDD是一个设计活动,您可以根据预期行为逐步构建功能块。简单来说就是你根据项目需求来先写摸测试用例的模板,然后撸代码,代码撸了完了,在根据测试用例模板来完善单元测试,最后用单元测试来验证你写的代码。

在这里插入图片描述

  • 1、测试代码和上面的类似,只是顺序有所不一样

四、组件中有接口请求的测试

  • 1、页面打开就加载数据

    import React, { useState, useEffect } from 'react';
    import axios from 'axios';
    
    export interface IUser {
      login: string;
      id: number;
      node_id: string;
      avatar_url: string;
      gravatar_id: string;
      url: string;
      html_url: string;
      followers_url: string;
      following_url: string;
      gists_url: string;
      starred_url: string;
      subscriptions_url: string;
      organizations_url: string;
      repos_url: string;
      events_url: string;
      received_events_url: string;
      type: string;
      site_admin: boolean;
    }
    const GitHubUser = () => {
      const [userList, setUserList] = useState<IUser[]>([]);
      const [loading, setLoading] = useState<boolean>(true);
    
      useEffect(() => {
        axios.get('https://api.github.com/users?since=1').then((res) => {
          setLoading(false);
          setUserList(res.data);
        });
      }, []);
      return (
        <>
          <div data-testid='loading'>
            <span>{loading ? 'loading....' : ''}</span>
          </div>
          <ul>
            {userList.map((item: IUser) => {
              return <li key={item.id}>{item.login}</li>;
            })}
          </ul>
        </>
      );
    };
    
    export default GitHubUser;
    
    // 单元测试代码
    import React from 'react';
    import { render, screen } from '@testing-library/react';
    import axios from 'axios';
    import GitHubUser from './../../components/GitHubUser';
    
    jest.mock('axios');
    const mockedAxios = axios as jest.Mocked<typeof axios>;
    
    describe('测试axios请求', () => {
      test('测试发起axios请求', async () => {
        // 模拟发送请求数据
        mockedAxios.get.mockResolvedValueOnce({
          data: [
            { id: 1, login: 'hello' },
            { id: 2, login: 'word' },
          ],
        });
        // 渲染组件
        render(<GitHubUser />);
        // 判断节点内容
        const loadingText = await screen.findByTestId('loading');
        (expect(loadingText) as any).toHaveTextContent('');
        const userGroup = await screen.findByTestId('user-group');
        (expect(userGroup) as any).not.toHaveTextContent();
      });
    });
    
  • 2、模拟按钮点击去获取数据

    import React, { useState } from 'react';
    import axios from 'axios';
    
    export interface IUser {
      login: string;
      id: number;
    }
    const GitHubUser = () => {
      const [userList, setUserList] = useState<IUser[]>([]);
      const [loading, setLoading] = useState<boolean>(false);
    
      const getData = () => {
        setLoading(true);
        axios.get('https://api.github.com/users?since=1').then((res) => {
          console.log(res.data, '测试请求结果');
          setLoading(false);
          setUserList(res.data);
        });
      };
      return (
        <>
          <button onClick={getData} data-testid='button'>
            点击获取数据
          </button>
          <div data-testid='loading'>
            <span>{loading ? 'loading....' : ''}</span>
          </div>
          <ul data-testid='user-group'>
            {userList.map((item: IUser) => {
              return (
                <li key={item.id} data-testid='item'>
                  {item.login}
                </li>
              );
            })}
          </ul>
        </>
      );
    };
    
    export default GitHubUser;
    
    import React from 'react';
    import { render, screen, fireEvent, act } from '@testing-library/react';
    import userEvent from '@testing-library/user-event';
    import axios from 'axios';
    import GitHubUser from './../../components/GitHubUser';
    
    jest.mock('axios');
    const mockedAxios = axios as jest.Mocked<typeof axios>;
    
    describe('测试axios请求', () => {
    
      test('模拟点击获取数据', async () => {
        const data = [
          { id: 1, login: 'hello' },
          { id: 2, login: 'word' },
        ];
        // 模拟发送请求数据
        mockedAxios.get.mockImplementationOnce(() => Promise.resolve({ data }));
        // 因为这里是异步的,所以要使用act包裹下
        await act(async () => {
          render(<GitHubUser />);
          // 模拟点击事件
          userEvent.click(screen.getByTestId('button'));
        });
        expect(screen.queryAllByTestId('item').length).toBe(2);
        expect(screen.queryAllByTestId('item')[1]).toHaveTextContent('word');
      });
    });
    
  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-08-03 11:30:55  更:2021-08-03 11:31:19 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 20:28:03-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码