当前位置: 首页 > 科技观察

前端测试有哪些类型?_0

时间:2023-03-18 02:21:21 科技观察

最近有很多朋友来找我聊测试相关的内容,发现他们对测试的分类有点迷茫。其实测试分为三种:E2E,集成,单体测试,其他功能测试,UI测试,界面测试只是其中一种。KentC.Dodds[1]在这篇文章《Static vs Unit vs Integration vs E2E Testing for Frontend Apps》[2]中也谈到了这三个测试的比较和区别。同学帮忙。所以今天把这篇文章分享给大家~我会尽量在翻译中使用更地道的语言,也就是在原文的基础上加一层Buf。如果您想阅读原文,请点击此处[3]。J.B.Rainsberger在我的采访中说了一个我非常喜欢的比喻:你可以把油漆扔到墙上,你可能会涂满大部分的墙,但除非你用刷子刷墙,否则你永远不会刷到角落。我喜欢用它来类比测试,因为做测试就像画一堵墙,你必须在开始之前选择正确的策略。你用小刷头刷墙吗?当然不是。那将需要很长时间,而且效果会参差不齐。你用滚筒来画一切吗?就像你曾曾祖母从200年前从某处带来的豪华家具?绝对不。不同的刷子适用于不同的情况,测试也是如此。这就是我构建这个测试模型[4]的原因。在这个模型中,有4个测试类别:端到端测试:使用行为像用户的机器人与App进行交互并验证功能是否正常。有时也称为“功能测试”或E2E。集成测试:验证多个单元是否协同工作。单元测试:验证单独的隔离部分是否正常工作。静态测试:在编写代码时发现拼写错误和类型错误。在这个模型中,每个测试类别的大小与你在测试时付出的注意力呈正相关(通常)。让我深入谈谈差异、含义以及如何优化这些类型的测试。测试类型让我们从上到下看几个这几种测试的例子:用户。以下示例是使用Cypresss实现的:=generate.user()consttodo=generate.todo()//在这里我们将遍历整个注册过程//我通常只写一个测试来做这个//其余的测试将直接通过HTTP请求发送实现注册功能//这样我们就可以跳过注册表单的交互过程cy.visitApp()cy.findByText(/register/i).click()cy.findByLabelText(/username/i).type(user.username)cy.findByLabelText(/password/i).type(user.password)cy.findByText(/login/i).click()cy.findByLabelText(/addtodo/i).type(todo.description)。类型('{enter}')cy.findByTestId('todo-0').should('have.value',todo.description)cy.findByLabelText('complete').click()cy.findByTestId('todo-0').should('have.class','complete')//等等...//我的E2E测试通常像真实用户一样编写//sometimesverylong})})集成测试集成测试背后的想法是尽可能少的模拟。我通常只mock以下两点:网络请求(使用MSW[5])实现动画组件(因为谁想在测试中等待)下面的测试用例将渲染整个应用程序。但这并不是集成测试的硬性要求,而且我编写的大多数集成测试都不会渲染整个应用程序。它们通常只渲染要在App中使用的Provider(这是render在test/app-test-utils伪模块中所做的):import*asReactfrom'react'import{render,screen,waitForElementToBeRemoved}from'测试/app-test-utils'从'@testing-library/user-event'导入用户事件从'@jackfranklin/test-data-bot'导入{rest}从'@jackfranklin/test-data-bot'导入{setupServer}从'msw/node'import{handlers}from'test/server-handlers'importAppfrom'../app'constbuildLoginForm=build({fields:{username:fake(f=>f.internet.userName()),password:fake(f=>f.internet.password()),},})//集成测试一般只使用MSW库来MockHTTP请求constserver=setupServer(...handlers)beforeAll(()=>server.listen())afterAll(()=>server.close())afterEach(()=>server.resetHandlers())test(`登录显示用户名`,async()=>{//这个自定义渲染会在App加载时返回一个Promise//(如果你使用服务器渲染,你可能不会tneedtodothis)//这个自定义渲染还允许你指定你的初始路由awaitrender(,{route:'/login'})const{用户名,密码}=buildLoginForm()userEvent.type(screen.getByLabelText(/用户名/i),用户名)'button',{name:/submit/i}))awaitwaitForElementToBeRemoved(()=>screen.getByLabelText(/loading/i))//检查用户是否登录expect(screen.getByText(username)).toBeInTheDocument()})对于这样的测试,一般我会做一些全局的处理,比如自动重置所有mocks。您可以在ReactTestingLibrarysetupdocs中了解有关上述测试工具功能的更多信息。Unittestimport'@testing-library/jest-dom/extend-expect'import*asReactfrom'react'//如果你的集成测试中有像上面这样的测试工具模块//那么不要使用@testing-library/react,直接用你的就可以了import{render,screen}from'@testing-library/react'importItemListfrom'../item-list'//有些人可能不把这样的测试称为单一测试,因为我们仍然需要使用React来渲染成DOM//他们也可能会告诉你使用浅渲染//当他们告诉你这个时,请把这个链接https://kcd.im/shallow扔到他们的脸上test('renders"没有项目”当项目列表为空时',()=>{render()expect(screen.getByText(/noitems/i)).toBeInTheDocument()})test('呈现列表中的项目',()=>{render()//注意:为了简化这个例如,快照是此处使用,但仅://1.快照非常小//2.我们使用toMatchInlineSnaphost//详细信息:阅读更多:https://kcd.im/snapshotsexpect(screen.getByText(/apple/i)).toBeInTheDocument()expect(screen.getByText(/orange/i)).toBeInTheDocument()expect(screen.getByText(/pear/i)).toBeInTheDocument()期望(screen.queryByText(/noitems/i)).not.toBeInTheDocument()})相信大家都知道下面肯定是单元测试://纯函数是单元测试的最佳选择,我也喜欢jest-in-case用于单元测试测试从'jest-in-case'导入案例从'../fizzbuzz'cases('fizzbuzz',({input,output})=>expect(fizzbuzz(input)).toBe(output),[[1,'1'],[2,'2'],[3,'Fizz'],[5,'Buzz'],[9,'Fizz'],[15,'FizzBu??zz'],[16,'16'],].map(([input,output])=>({title:`${input}=>${output}`,input,output})),)静态测试其实,这里更多的是使用TypeScript、ESLint等静态检查工具来发现代码问题)//你能发现下面的问题吗?//我相信ESLint的for-direction规则会比你的代码审查更快地发现这个问题