精华内容
下载资源
问答
  • @unclebobmartin 100%的代码覆盖率无济于事,可以使您更安全,而事实并非如此。 @nicolas_frankel 100%的代码覆盖率不是成就,而是最低要求。 如果编写一行代码,则最好对其进行测试。 ...

    分支覆盖率 代码覆盖率

    本文的基础是我和罗伯特·马丁先生(2011年4月8日)之间的一系列推文:

    1. 如果覆盖率达到100%,则不知道系统是否正常运行,但是您确实知道编写的每一行都按您的预期工作。
    2. @unclebobmartin 100%的代码覆盖率无济于事,可以使您更安全,而事实与事实没有什么不同。
    3. @nicolas_frankel 100%的代码覆盖率不是成就,它是最低要求。 如果编写一行代码,则最好对其进行测试。
    4. @unclebobmartin我可以得到100%的代码覆盖率,并且不进行任何测试,因为如果没有断言。 停止将覆盖范围作为目标,这只是一种方法!

    这让我有些无语,甚至更多来自像马丁先生那样受人尊敬的人。 由于我的论点远远超出了140个字符的限制,因此,我准备了一个完整的文章。

    代码覆盖率没有任何测试

    让我们从M. Martin的基本断言开始,即100%的代码覆盖率确保每条代码行的行为均符合预期。 没有东西会离事实很远!

    代码覆盖率只是一种跟踪测试执行过程中流经过的位置的方法。 由于覆盖范围是通过检测实现的,因此代码覆盖范围也可以在执行标准应用程序期间进行测量。

    但是,测试是通过提供输入并验证输出是否是所需的代码来执行一些代码。 在Java应用程序中,这是在诸如JUnit和TestNG之类的框架的帮助下在单元级别完成的。 两者都使用assert()方法检查输出。

    因此, 代码覆盖率和测试是完全不同的东西 极端地讲,我们可以代码覆盖整个应用程序,即达到100%,而不必测试任何一行,因为我们没有断言!

    100%覆盖意味着测试所有内容

    并非我们所有的代码都不是关键也不复杂。 实际上,有些甚至可以说是彻头彻尾的琐事。 任何疑问? 然后考虑吸气剂和二传手。 即使任何值得一试的IDE都能为我们忠实地生成它们,是否应该对其进行测试? 如果答案是肯定的,则还应该考虑测试所使用的框架 ,因为与生成的getter和setter相比,它们是bug的来源更大。

    目标与实现方式之间的混淆

    M. Martin的第二个主张是要求代码覆盖率达到100%。 我希望有所不同,但是我使用“要求”一词具有不同的含义。 业务分析师将业务需求转换为功能需求,而架构师将其转换为非功能需求。

    代码覆盖率和单元测试只是减少bug发生率的一种方式,而并非必需条件。 实际上,只要软件满足他们的需求,用户就不会在乎平均代码。 只有IT部门,并且只有在应用程序寿命长的情况下,才可能对高代码覆盖率感兴趣 ,甚至没有100% 的兴趣

    成本效益

    我了解到,在某些特定情况下,软件不会失败:在外科手术或航空航天领域,生命受到威胁,而在金融应用程序中,单次失败可能造成数百万甚至数十亿美元的损失。

    但是,到目前为止,我微不足道的经验告诉我,无论我们是否想要,钱都占了上风。 等式非常简单:测试的成本是多少,错误的成本是什么,其可能性如何。 如果错误的代价是人类的生命,并且可能性很高,那么最好像地狱般对应用程序进行测试。 相反,如果错误的成本为半个工日,且可能性很低,为什么我们要花1个工日来进行提前纠正呢? 技术债务的观点有助于我们回答这个问题。 而且,这是经理必须在IT的帮助下做出的决策。

    重点是, 在大多数软件中 ,实现100%测试(而不是100%覆盖率)是过头的

    为了它

    最后但并非最不重要的一点是,我在我的工作中看到了一些有趣的异常行为。

    首先是质量是为了质量。 我是一名技术人员,我必须承认:“哇,如果我对此进行测试,我可以获得1%的代码覆盖率!” 有必要吗? 更重要的是,它增加了可维护性或降低了错误的可能性吗? 在任何情况下,您都应该问自己这些问题。 如果两个答案都不都是,那就算了。

    团队之间的挑战稍有不同。 挑战是好的,因为它们可以创建一个模拟并可以使每个人都变得更好,但是挑战的对象应该带来一些附加值,而原始代码覆盖率却没有。

    最后,我已经对此进行了讨论 ,但是那里的一些自负者只是在写简历而做事,100%的覆盖率只是其中之一。 这很不好,如果您对这些人有任何影响,则应努力使他们朝着更面向项目的目标(例如100%确定接受测试)进行定向。

    结论

    通过上述论点,我毫无疑问地证明了诸如“我们必须实现100%的代码覆盖率”或“这是要求”之类的断言不能被视为一般规则,并且在没有适当上下文的情况下完全是胡说八道。

    对于我自己,在开始项目之前,要做的事情之一就是与所有相关团队成员(主要是QA,PM和开发人员)就一定程度的代码覆盖率达成共识。 然后,我非常谨慎地解释说,此度量标准不是一成不变的,我列出了获取方法/设置方法,也没有断言是人为地增加它的方法。 代码越关键和越复杂,它应该具有的覆盖范围就越大。 如果可以选择的话,我宁愿没有达到约定的数量,也要经过严格的测试。

    对于团队来说,拥抱质量是一个值得追求的目标。 如果我们设定SMART目标,则甚至可以实现:100%的代码覆盖率仅是可衡量的,这就是为什么最好忘记它,越早越好,并专注于更多面向业务的目标的原因。

    翻译自: https://blog.frankel.ch/100-code-coverage/

    分支覆盖率 代码覆盖率

    展开全文
  • 分支覆盖率 代码覆盖率 (由Adobe Stock Photo许可) 互联网上现在有很多建议说100%的覆盖率不是一个值得的目标。 我非常不同意。 通常,难以测试的代码是需要重构的信号。 我知道了。 几年前,我很讨厌...

    分支覆盖率 代码覆盖率

    (由Adobe Stock Photo许可)

    互联网上现在有很多建议说100%的覆盖率不是一个值得的目标。

    我非常不同意。

    通常,难以测试的代码是需要重构的信号。

    我知道了。 几年前,我很讨厌测试。 我以为这只会让我移动得更慢。

    当我开始编码时,这并不是人们经常做的事情。 如果是这样,通常是由一个单独的质量检查小组负责。 几年前,尽管它成为一个真正的热门话题。 面试开始期望候选人知道如何编写测试,并且越来越多的组织将其作为质量计划从上至下进行推广。

    我一直努力在比赛中处于领先地位,因此我决定走进面试,并说“测试并不是我真正的强项”不再是一个好看的样子,所以我决定我将获得所有比赛的100%覆盖率从那时起我的测试。

    当时,我不确定自己会从中获得什么好处,或者是否真的有任何好处。

    现在, 老实说 ,我不会回去。 当某件事破坏了覆盖率100%的代码库时,您的测试很可能会告诉您确切的位置和方式。

    这并不是说单元测试就是您所需要的。 不是。 但是在我看来,未经测试的代码也不是一个好选择。

    回到我身边,回到我也不相信测试覆盖范围的好处的时代。

    第1部分:学习Lingo

    当时,交易工具是摩卡咖啡西农柴的结合体 Mocha是测试运行程序,sinon提供了创建“模拟”和“间谍”的功能,而chai是一个声明库,因此您可以以人类语言友好的方式键入声明。

    (这是间谍吗?)

    我基本上不知道这意味着什么。 在我变得有效之前,首先要做的就是学习语言。

    所以,第一件事是第一位- 地狱是间谍还是嘲笑?

    尽管首先想到的是詹姆斯·邦德或伊桑·亨特。 尽管这不是一个可怕的隐喻,但这绝对不是我们在这里谈论的内容。

    在阅读了一些文档之后,我最终了解到间谍是一种功能,已通过测试框架进行了修改,以提供有关其用法的元信息。 它在监视。 Kinda喜欢人们如何利用Apple最近的FaceTime Bug监视您。 有点像詹姆斯·邦德。

    模拟与间谍相似,但经过了更多修改。 除了提供并跟踪如何使用特定功能外,它还将其行为更改为可预测的。

    我还了解到有几种测试类型。 不限于三种最常见的:单元测试,集成测试和E2E测试。

    当我们进行“单元测试”时,这意味着我们需要能够将代码分解为各个单元。 该特定单元之外的任何内容都是要被嘲弄的候选对象,例如其他功能或整个模块。 开玩笑是我选择单元测试的工具。 单元测试是衡量覆盖率的唯一测试类型。

    当我们进行集成测试时,我们正在测试我们的软件与其他软件的集成,例如通过卡夫卡传递一条消息的测试,该消息应该让我们的服务接收到该消息,然后可以在数据库中找到该消息的结果。 在创建集成测试时,我通常也喜欢Jest。

    端到端测试有点像使用您的应用程序的机器人。 您可以对其进行编程,以将其加载到浏览器中的网站,单击内容,并确保一切都能从用户角度正常运行。 赛普拉斯是我在该领域最喜欢的工具,但是当我学习时,这种工具就不存在了。 Selenium是当今的佼佼者,老实说,这是一个足够大的领域,我很乐意让QA自动化工程师来处理这一部分。

    现在掌握了新知识就成了困难的部分:将其付诸实践。

    我花了几个月的时间来确保我编写的每段代码都具有测试覆盖率。 我承认,起初很难。 我在StackOverflow上花费了大量时间来查找模拟和间谍示例。 到最后,我发现自己对代码的信心大大提高了。

    另一个好处是,如果发生故障,我的测试通常会告诉我确切的位置。 当其他工程师对我所做的代码进行更改时,我可以更快地对其进行检查。 当重要的API发生更改时,测试失败会通知人们,并Swift对其进行了更新或重新考虑了他们的更改。

    不仅如此,我开始编写更好的代码。 我了解到,通常情况下,如果难以测试或难以完全覆盖某些内容,通常意味着我编写的代码不太好,并且可以对其进行重构,从而获得更加可维护和灵活的API。 为此,尝试达到100%的覆盖率鼓励了我将匿名函数提取到命名函数中,并了解许多重构中的部分应用程序和依赖项注入。

    在完成集成测试后,我甚至放弃了GitFlow进行基于主干的开发。 几年前,我以为精通大师是一件疯狂的事情,现在我每天要由近15名工程师组成的团队来做。

    相关阅读我有一个自白。 我致力于掌握

    第2部分:以身作则

    大约在我对新的测试堆栈充满信心的时候,另一种工具被推向市场,许多人声称这使单元测试变得更加简单:Jest。

    Jest是Facebook率先推出的自动测试框架。

    Jest将我以前使用的库浓缩到一个统一的框架(该框架是一个测试运行器)以及一组用于模拟,间谍和断言的API方面,做得非常出色。 除了为您的所有单元测试需求提供单个库之外,Jest在简化某些概念和模式以及强大而简单的模拟方面也做得很出色。

    因为我认为Jest更易于使用和理解,所以我将继续以Jest为例。

    如果您只是和我一起写这篇文章,那很好-到目前为止您所读的内容都将独立存在。 但是,我一直在记录使用带有Streaming SSR的Parcel构建React应用程序的过程,本文将在最后一部分继续。

    在下面链接的上一篇文章中,我展示了如何设置具有代码覆盖率的Jest,并在下一篇文章中说,我将展示如何使覆盖率达到100%。

    相关阅读增强Node.js的代码质量

    我认为证明100%覆盖率的最佳方法是展示如何到达那里。 在整个过程中,我们可能会发现几个可以重构代码以使其更具可测试性的地方。 因此,我将继续我的工作,将这个项目的覆盖率提高到100%,并说明要进行哪些重构,在哪里使用部分应用程序和依赖项注入以及在难以获得覆盖率的过程中要模拟什么。

    所以...让我们开始吧。 这是我将要从事的项目:

    GitHub链接patrickleet / streaming-ssr-react-styled-components

    该项目在app文件夹中有一个react app,在服务器文件夹中有SSR逻辑。 让我们从应用程序测试开始。

    应用测试

    上一篇文章中 ,在配置了Jest之后,我开始了对一个简单组件的简单测试。 我有几个同样简单的React组件。

    这是功能组件真正强大的原因之一。 函数比类容易测试。 他们没有状态,而是有输入和输出。 给定输入X,它们的输出为Y。存在状态时,可以将其存储在组件的外部。

    在这方面,新的React Hooks API很不错,因为它鼓励制作功能组件,并具有易于模拟的机制来为组件提供状态。 Redux在测试方面提供了相同的好处。

    让我们从淘汰其余的简单组件开始。 我们基本上只需要渲染它们,也许检查是否渲染了一些重要的信息。

    我通常将代码内联在文章中,但是这些测试中并没有什么新内容,因此我决定链接到实际的提交,只显示一个完整的示例:

    让我们看一下“关于”页面:

    import React from 'react'
    import Helmet from 'react-helmet-async'
    import Page from '../components/Page'
    const About = () => (
      < Page >
         <Helmet>
          <title>About Page</title>
        </Helmet>
        <div>This is the about page</div> 
      </ Page >
    )
    export default About

    它是测试:

    import React from 'react'
    import { shallow } from 'enzyme'
    import About from 'app/pages/About.jsx'
    describe( 'app/pages/About.jsx' , () => {
      it( 'renders About page' , () => {
        expect(About).toBeDefined()
        const tree = shallow( < About /> )
        expect(tree.find('Page')).toBeDefined()
        expect(
          tree
            .find('Helmet')
            .find('title')
            .text()
        ).toEqual('About Page')
        expect(tree.find('div').text()).toEqual('This is the about page')
      })
    })

    以下提交中的所有测试都非常相似:

    如您所见,只需确保我们的组件渲染足以使这些组件获得100%的覆盖率即可。 更详细的交互最好留给E2E测试,这不在本文的讨论范围之内。

    下一个组件app/App.jsx稍微复杂一些。 编写渲染测试后,您会注意到路由器中仍然存在无法访问的匿名函数,用于渲染“关于”页面。

    为了访问和测试它,我们想做一个小的重构,将函数提取为命名函数,以便我们可以导出它并进行测试。

    现在很容易测试:

    因为上面的“关于”页面有另一组测试,所以我们将保留其更具体的测试,并且只需要检查它是否在此处呈现即可。

    这样,在我们的应用程序中剩下要测试的唯一文件是app/client.js ,然后我们可以继续进行服务器端测试。

    让我们看一下代码:

    import React from 'react'
    import ReactDOM from 'react-dom'
    import { HelmetProvider } from 'react-helmet-async'
    import { BrowserRouter } from 'react-router-dom'
    import { rehydrateMarks } from 'react-imported-component'
    import importedComponents from './imported' // eslint-disable-line
    import App from './App'
    const element = document .getElementById( 'app' )
    const app = (
      < HelmetProvider >
         <BrowserRouter>
          <App />
        </BrowserRouter> 
      </ HelmetProvider >
    )
    // In production, we want to hydrate instead of render
    // because of the server-rendering
    if (process.env.NODE_ENV === 'production' ) {
      // rehydrate the bundle marks
      rehydrateMarks().then( () => {
        ReactDOM.hydrate(app, element)
      })
    } else {
      ReactDOM.render(app, element)
    }
    // Enable Hot Module Reloading
    if ( module .hot) {
      module .hot.accept()
    }

    我注意到的第一件事是依赖全局变量documentprocessmodule 第二件事是什么也没有导出,因此使用不同的输入可能很难多次运行。

    我们可以通过一些重构来解决这个问题:

    1. 将所有逻辑包装到我们可以导出的函数中。 此函数将接受带有所有依赖项的options对象。 这称为依赖注入 如果选择的话,这将使我们能够轻松地传递一堆东西的模拟版本。
    2. 重新水化后,我们在生产模式下有一个匿名函数,应将其提取为命名函数。

    我们还将要模拟一些外部模块: react-domreact-imported-componentapp/imported.js 模块本身就是依赖注入的一种形式。

    首先,这里是新重构的文件,其中的更改以粗体显示:

    import React from 'react'
    import ReactDOM from 'react-dom'
    import { HelmetProvider } from 'react-helmet-async'
    import { BrowserRouter } from 'react-router-dom'
    import { rehydrateMarks } from 'react-imported-component'
    import importedComponents from './imported' // eslint-disable-line
    import App from './App'
    // use "partial application" to make this easy to test
    export const hydrate = ( app, element ) => () => {
      ReactDOM.hydrate(app, element)
    }
    export const start = ({ 
      isProduction,
      document ,
      module ,
      hydrate
     }) => {
      const element = document .getElementById( 'app' )
      const app = (
        < HelmetProvider >
           <BrowserRouter>
            <App />
          </BrowserRouter> 
        </ HelmetProvider >
      )
      // In production, we want to hydrate instead of render
      // because of the server-rendering
      if (isProduction) {
        // rehydrate the bundle marks from imported-components, 
        // then rehydrate the react app
        rehydrateMarks().then(hydrate(app, element))
      } else {
        ReactDOM.render(app, element)
      }
      // Enable Hot Module Reloading
      if ( module .hot) {
        module .hot.accept()
      }
    }
    const options = {
      isProduction : process.env.NODE_ENV === 'production' ,
      document : document ,
      module : module ,
      hydrate
    }
    start(options)

    现在,我们实际上可以使用各种选项访问和测试启动,以及独立于启动逻辑测试水合物。

    测试时间有点长,所以我在网上添加了注释以解释发生了什么。 这是对该文件的测试:

    import React from 'react'
    import fs from 'fs'
    import path from 'path'
    import { start, hydrate } from 'app/client'
    import { JSDOM } from "jsdom"
    jest.mock( 'react-dom' )
    jest.mock( 'react-imported-component' )
    jest.mock( 'app/imported.js' )
    // mock DOM with actual index.html contents
    const pathToIndex = path.join(process.cwd(), 'app' , 'index.html' )
    const indexHTML = fs.readFileSync(pathToIndex).toString()
    const DOM = new JSDOM(indexHTML)
    const document = DOM.window.document
    // this doesn't contribute to coverage, but we
    // should know if it changes as it would
    // cause our app to break
    describe( 'app/index.html' , () => {
      it( 'has element with id "app"' , () => {
        const element = document .getElementById( 'app' )
        expect(element.id).toBe( 'app' )
      })
    })
    describe( 'app/client.js' , () => {
      // Reset counts of mock calls after each test
      afterEach( () => {
        jest.clearAllMocks()
      })
      describe( '#start' , () => {
        it( 'renders when in development and accepts hot module reloads' , () => {
          // this is mocked above, so require gets the mock version
          // so we can see if its functions are called
          const ReactDOM = require ( 'react-dom' )
          
          // mock module.hot
          const module = {
            hot : {
              accept : jest.fn()
            }
          }
          // mock options
          const options = {
            isProduction : false ,
            module ,
            document
          }
          start(options)
          expect(ReactDOM.render).toBeCalled()
          expect( module .hot.accept).toBeCalled()
        })
        
        it( 'hydrates when in production does not accept hot module reloads' , () => {
          const ReactDOM = require ( 'react-dom' )
          const importedComponent = require ( 'react-imported-component' )
          importedComponent.rehydrateMarks.mockImplementation( () => Promise .resolve())
          // mock module.hot
          const module = {}
          // mock rehydrate function
          const hydrate = jest.fn()
          // mock options
          const options = {
            isProduction : true ,
            module ,
            document ,
            hydrate
          }
          start(options)
          expect(ReactDOM.render).not.toBeCalled()
          expect(hydrate).toBeCalled()
        })
      })
      describe( '#hydrate' , () => {
        it( 'uses ReactDOM to hydrate given element with an app' , () => {
          const ReactDOM = require ( 'react-dom' )
          const element = document .getElementById( 'app' )
          const app = ( < div > </ div > )
          const doHydrate = hydrate(app, element)
          expect( typeof doHydrate).toBe( 'function' )
          doHydrate()
          expect(ReactDOM.hydrate).toBeCalledWith(app, element)
        })
      })
    })

    现在,当我们运行测试时,除了应生成的文件app/imported.js之外,我们应该对app文件夹具有100%的覆盖率,并且进行测试没有意义,因为它在将来的版本中可能会有所不同。

    让我们更新jest配置,以从coverage统计信息中忽略它,并检查结果。

    jest.config添加:

    "coveragePathIgnorePatterns" : [
      "<rootDir>/app/imported.js" ,
      "/node_modules/"
    ]

    现在,当我们运行npm run test我们得到以下结果。

    GitHub链接test:client.js tests·patrickleet / streaming-ssr-react-styled-components @ c5fcfe9

    我想指出的是,在开发测试时,我通常使用“监视”模式来执行此操作,因此在更改测试时,它们会自动重新运行。

    完成应用程序测试后,让我们继续进行服务器。

    服务器测试

    在上一篇文章中,我为一个应用程序文件和一个服务器文件编写了测试,因此我们已经server/index.js 进行了测试 现在我们需要测试server/lib剩余的三个文件。

    让我们从server/lib/client.js

    import fs from 'fs'
    import path from 'path'
    import cheerio from 'cheerio'
    export const htmlPath = path.join(process.cwd(), 'dist' , 'client' , 'index.html' )
    export const rawHTML = fs.readFileSync(htmlPath).toString()
    export const parseRawHTMLForData = ( template, selector = '#js-entrypoint' ) => {
      const $template = cheerio.load(template)
      let src = $template(selector).attr( 'src' )
      return {
        src
      }
    }
    const clientData = parseRawHTMLForData(rawHTML)
    const appString = '<div id="app">'
    const splitter = '###SPLIT###'
    const [startingRawHTMLFragment, endingRawHTMLFragment] = rawHTML
      .replace(appString, ` ${appString} ${splitter} ` )
      .split(splitter)
    export const getHTMLFragments = ( { drainHydrateMarks } ) => {
      const startingHTMLFragment = ` ${startingRawHTMLFragment} ${drainHydrateMarks} `
      return [startingHTMLFragment, endingRawHTMLFragment]
    }

    首先,我注意到有一个很大的代码块,以前的废弃策略中甚至没有在项目中使用过。 export const parseRawHTMLForDataconst clientData

    我将从删除它开始。 代码越少,错误可以存在的位置就越少。 还有一些我从未使用过的导出可以对模块保持私有。

    这是更新的文件:

    import fs from 'fs'
    import path from 'path'
    const htmlPath = path.join(process.cwd(), 'dist' , 'client' , 'index.html' )
    const rawHTML = fs.readFileSync(htmlPath).toString()
    const appString = '<div id="app">'
    const splitter = '###SPLIT###'
    const [startingRawHTMLFragment, endingRawHTMLFragment] = rawHTML
      .replace(appString, ` ${appString} ${splitter} ` )
      .split(splitter)
    export const getHTMLFragments = ( { drainHydrateMarks } ) => {
      const startingHTMLFragment = ` ${startingRawHTMLFragment} ${drainHydrateMarks} `
      return [startingHTMLFragment, endingRawHTMLFragment]
    }

    看起来可能需要为此进行一项测试。 但是,该计划中有一个小问题:该文件取决于之前运行的构建,因为它读取生成的构建。

    从技术上讲,这是有道理的,因为您永远都不会尝试在没有内置应用程序呈现的情况下在服务器上呈现应用程序。

    考虑到该约束,我认为这是可以的,并且由于我们只能确保在测试之前就建立了管道调用,因此不值得进行重构。 如果我们想要真正的纯单元隔离,我们可能会考虑重构一些,因为从技术上讲整个应用程序都是SSR的依赖项,因此可以对其进行模拟。 另一方面,使用实际构建可能反而更有用。 在编写测试的整个过程中,您经常会遇到这样的折衷。

    话虽如此,这是测试以获得该模块的完整介绍:

    import { getHTMLFragments } from 'server/lib/client.js'
    describe( 'client' , () => {
      it( 'exists' , () => {
        const drainHydrateMarks = '<!-- mock hydrate marks -->'
        const [start, end] = getHTMLFragments({ drainHydrateMarks })
        expect(start).toContain( '<head>' )
        expect(start).toContain(drainHydrateMarks)
        expect(end).toContain( 'script id="js-entrypoint"' )
      })
    })

    并提交: 修复:删除未使用的代码来分析模板测试:服务器/库/客户端测试

    接下来, server/lib/server.js很小,所以让我们把它淘汰掉。 以下是其代码以刷新您的记忆,或者如果您现在才加入我们:

    import express from 'express'
    export const server = express()
    export const serveStatic = express.static

    和测试:

    import express from 'express'
    import { server, serveStatic } from 'server/lib/server.js'
    describe( 'server/lib/server' , () => {
      it( 'should provide server APIs to use' , () => {
        expect(server).toBeDefined()
        expect(server.use).toBeDefined()
        expect(server.get).toBeDefined()
        expect(server.listen).toBeDefined()
        expect(serveStatic).toEqual(express.static)
      })
    })

    似乎我们基本上只是在放任所有快递责任,而我们希望快递提供这份合同,我们只需确保它能做到,而超出此范围则没有任何意义。

    最后,我们只有一个文件要测试: server/lib/ssr.js

    这是我们的ssr模块:

    import React from 'react'
    import { renderToNodeStream } from 'react-dom/server'
    import { HelmetProvider } from 'react-helmet-async'
    import { StaticRouter } from 'react-router-dom'
    import { ServerStyleSheet } from 'styled-components'
    import { printDrainHydrateMarks } from 'react-imported-component'
    import log from 'llog'
    import through from 'through'
    import App from '../../app/App'
    import { getHTMLFragments } from './client'
    // import { getDataFromTree } from 'react-apollo';
    export default (req, res) => {
      const context = {}
      const helmetContext = {}
      const app = (
        < HelmetProvider context = {helmetContext} >
           <StaticRouter location={req.originalUrl} context={context}>
            <App />
          </StaticRouter> 
        </ HelmetProvider >
      )
      try {
        // If you were using Apollo, you could fetch data with this
        // await getDataFromTree(app);
        const sheet = new ServerStyleSheet()
        const stream = sheet.interleaveWithNodeStream(
          renderToNodeStream(sheet.collectStyles(app))
        )
        if (context.url) {
          res.redirect( 301 , context.url)
        } else {
          const [startingHTMLFragment, endingHTMLFragment] = getHTMLFragments({
            drainHydrateMarks : printDrainHydrateMarks()
          })
          res.status( 200 )
          res.write(startingHTMLFragment)
          stream
            .pipe(
              through(
                function write ( data )  {
                  this .queue(data)
                },
                function end ()  {
                  this .queue(endingHTMLFragment)
                  this .queue( null )
                }
              )
            )
            .pipe(res)
        }
      } catch (e) {
        log.error(e)
        res.status( 500 )
        res.end()
      }
    }

    它有点长,并且有一些执行路径。 我确实想做一些小的重构,这将使隔离更加容易,例如提取逻辑以将应用程序生成为单独的函数,并使用部分应用程序能够注入应用程序流渲染器,以便我们可以轻松地进行模拟一些重定向。

    另外,写入和结束也有些困难,因此我们也可以使用部分应用程序将它们拉高。

    这是更新的版本:

    import React from 'react'
    import { renderToNodeStream } from 'react-dom/server'
    import { HelmetProvider } from 'react-helmet-async'
    import { StaticRouter } from 'react-router-dom'
    import { ServerStyleSheet } from 'styled-components'
    import { printDrainHydrateMarks } from 'react-imported-component'
    import log from 'llog'
    import through from 'through'
    import App from '../../app/App'
    import { getHTMLFragments } from './client'
    // import { getDataFromTree } from 'react-apollo';
    const getApplicationStream = ( originalUrl, context ) => {
      const helmetContext = {}
      const app = (
        < HelmetProvider context = {helmetContext} >
           <StaticRouter location={originalUrl} context={context}>
            <App />
          </StaticRouter> 
        </ HelmetProvider >
      )
      const sheet = new ServerStyleSheet()
      return sheet.interleaveWithNodeStream(
        renderToNodeStream(sheet.collectStyles(app))
      )
    }
    export function write ( data )  {
      this .queue(data)
    }
    // partial application with ES6 is quite succinct
    // it just means a function which returns another function
    // which has access to values from a closure
    export const end = endingHTMLFragment =>
      function end ()  {
        this .queue(endingHTMLFragment)
        this .queue( null )
      }
    export const ssr = getApplicationStream => (req, res) => {
      try {
        // If you were using Apollo, you could fetch data with this
        // await getDataFromTree(app);
        const context = {}
        const stream = getApplicationStream(req.originalUrl, context)
        if (context.url) {
          return res.redirect( 301 , context.url)
        }
        const [startingHTMLFragment, endingHTMLFragment] = getHTMLFragments({
          drainHydrateMarks : printDrainHydrateMarks()
        })
        res.status( 200 )
        res.write(startingHTMLFragment)
        stream.pipe(through(write, end(endingHTMLFragment))).pipe(res)
      } catch (e) {
        log.error(e)
        res.status( 500 )
        res.end()
      }
    }
    const defaultSSR = ssr(getApplicationStream)
    export default defaultSSR

    以下是查看Github中差异的链接: chore:重构ssr以使其分解/使其更易于阅读 ,以及chore:重构ssr more

    现在让我们编写一些测试。 我们需要为此文件专门为节点设置jest-environment,否则styled-components部分将不起作用。

    /**
     * @jest-environment node
     */
    import defaultSSR, { ssr, write, end } from 'server/lib/ssr.js'
    jest.mock( 'llog' )
    const mockReq = {
      originalUrl : '/'
    }
    const mockRes = {
      redirect : jest.fn(),
      status : jest.fn(),
      end : jest.fn(),
      write : jest.fn(),
      on : jest.fn(),
      removeListener : jest.fn(),
      emit : jest.fn()
    }
    describe( 'server/lib/ssr.js' , () => {
      describe( 'ssr' , () => {
        it( 'redirects when context.url is set' , () => {
          const req = Object .assign({}, mockReq)
          const res = Object .assign({}, mockRes)
          const getApplicationStream = jest.fn( ( originalUrl, context ) => {
            context.url = '/redirect'
          })
          const doSSR = ssr(getApplicationStream)
          expect( typeof doSSR).toBe( 'function' )
          doSSR(req, res)
          expect(res.redirect).toBeCalledWith( 301 , '/redirect' )
        })
        it( 'catches error and logs before returning 500' , () => {
          const log = require ( 'llog' )
          const req = Object .assign({}, mockReq)
          const res = Object .assign({}, mockRes)
          const getApplicationStream = jest.fn( ( originalUrl, context ) => {
            throw new Error ( 'test' )
          })
          const doSSR = ssr(getApplicationStream)
          expect( typeof doSSR).toBe( 'function' )
          doSSR(req, res)
          expect(log.error).toBeCalledWith( Error ( 'test' ))
          expect(res.status).toBeCalledWith( 500 )
          expect(res.end).toBeCalled()
        })
      })
      describe( 'defaultSSR' , () => {
        it( 'renders app with default SSR' , () => {
          const req = Object .assign({}, mockReq)
          const res = Object .assign({}, mockRes)
          defaultSSR(req, res)
          expect(res.status).toBeCalledWith( 200 )
          expect(res.write.mock.calls[ 0 ][ 0 ]).toContain( '<!DOCTYPE html>' )
          expect(res.write.mock.calls[ 0 ][ 0 ]).toContain(
            'window.___REACT_DEFERRED_COMPONENT_MARKS'
          )
        })
      })
      describe( '#write' , () => {
        it( 'write queues data' , () => {
          const context = {
            queue : jest.fn()
          }
          const buffer = new Buffer.from( 'hello' )
          write.call(context, buffer)
          expect(context.queue).toBeCalledWith(buffer)
        })
      })
      describe( '#end' , () => {
        it( 'end queues endingFragment and then null to end stream' , () => {
          const context = {
            queue : jest.fn()
          }
          const endingFragment = '</html>'
          const doEnd = end(endingFragment)
          doEnd.call(context)
          expect(context.queue).toBeCalledWith(endingFragment)
          expect(context.queue).toBeCalledWith( null )
        })
      })
    })

    由于此文件比其他文件复杂一些,因此需要进行更多测试才能打到所有分支。 为了清楚起见,每个函数都包装在其自己的describe块中。

    这是对Github的提交: test:ssr单元测试

    现在,当我们运行测试时,我们具有100%的覆盖率!

    最后,在整理内容之前,我将对jest.config进行一些小的更改以强制执行100%的覆盖率。 与第一次接触相比,保持覆盖范围要容易得多。 我们测试的许多模块几乎不会改变。

    "coverageThreshold" : {
        "global" : {
          "branches" : 100 ,
          "functions" : 100 ,
          "lines" : 100 ,
          "statements" : 100
        }
      },

    并做了! 这是Github上的提交: 杂项:要求100%覆盖率

    结论

    我在本文中的目的是演示能够重构代码或使用模拟和依赖注入来隔离单元的技术,以使难以测试的代码难以实现,并讨论达到100%覆盖率的某些优点。 此外,从起点开始使用TDD会容易得多。

    我坚信,如果100%的覆盖率很难达到,那是因为代码需要重构。

    在许多情况下,端到端测试对于某些方面来说将是更好的测试。 基于此的Cypress.io套件可以加载应用程序并单击,这将大大提高我们的信心。

    我相信,在覆盖率达到100%的代码库中,可以极大地提高您对每个发行版的信心,从而提高做出和检测重大更改的速度,从而发挥了很大作用。

    与往常一样,如果您发现这很有用,请鼓掌,关注我, 在GitHub项目上留下星星,和/或在社交网络上分享!

    在接下来的部分中,即将到来的是,我们将添加可用于生产的Dockerfile,并探索如何仅使用另一个Dockerfile,或者将我们的应用程序打包为Nginx服务的静态站点,以及这两种方法之间的权衡。

    最好,
    帕特里克·李·斯科特

    查看本系列的其他文章! 这是第4部分。

    翻译自: https://hackernoon.com/the-100-code-coverage-myth-900b83d20d3d

    分支覆盖率 代码覆盖率

    展开全文
  • 分支覆盖率 代码覆盖率 公共代码基金会致力于为国际公共组织(例如地方政府)启用开放和协作的公共目的软件。 为此,我们通过代码库管理在代码库级别支持软件。 我们还发布了公共代码标准 (在撰写本文时,其草稿...

    分支覆盖率 代码覆盖率

    公共代码基金会致力于为国际公共组织(例如地方政府)启用开放和协作的公共目的软件。 为此,我们通过代码库管理在代码库级别支持软件。 我们还发布了公共代码标准 (在撰写本文时,其草稿版本为0.1.4),这有助于开源代码库社区构建可被其他组织成功重用的解决方案。 它包括针对决策者,经理,开发人员,设计师和供应商的指南。

    除其他外,该标准解决了代码覆盖率或运行自动化测试套件时执行了多少代码。 这是一种衡量代码包含未检测到的软件错误的可能性的方法。 在标准的“使用持续集成”要求中 ,它说:“ 应该监视源代码测试和文档覆盖范围。” 此外, 检查此要求的指南指出:“代码覆盖率工具检查覆盖率是否为代码的100%”。

    在我从事超过二十年的软件开发生涯中,我从事大型和小型代码库的工作,其中一些代码覆盖率很高。 但是我贡献的所有重要代码库都没有报告100%的测试覆盖率。 这使我怀疑是否将遵循“ 检查覆盖率是否为100% ”的指导。

    以前,我认为100%的测试覆盖率是值得追求的,但是在大多数代码库中可能不值得花这笔钱,而且在少数代码库中可能并不现实。

    随着时间的推移,覆盖率工具变得越来越智能和可调。 语言变得越来越轻,库变得越来越容易模拟和测试。 那么,今天100%的功能覆盖范围有多不合理?

    资源枯竭

    我贡献的高质量但测试覆盖率较低的代码库恰巧是用C或C ++编写的。 快速浏览一下这些代码库,就会发现一类常见的低覆盖率情况,在资源耗尽的情况下,我会把它们搞混:内存不足,磁盘空间不足等。

    这是一个简单的代码示例,它不检查资源是否耗尽。 在这种情况下,内存分配失败:

    char * buf = malloc ( 80 ) ;
    sprintf ( buf, "hello, world" ) ;

    此示例代码需要分配一个小缓冲区,因此调用malloc(80) ,并且malloc通常返回一个指向80字节内存的指针……但这会失败。 malloc返回NULL的情况(不太可能)的情况下,上面的代码将继续使用NULL指针调用sprintf ,这会导致崩溃。 在C代码中,典型的做法是执行以下操作:

    char * buf = malloc ( 80 ) ;
    if ( buf == NULL ) {
        fprintf ( stderr , "malloc returned NULL for 80 bytes? \n " ) ;
        return NULL ;
    }
    sprintf ( buf, "hello, world" ) ;

    此代码防止malloc返回NULL ,这是更好的选择。 但是,面对这种资源枯竭而创建正确行为的测试可能非常困难。 当然,这并非不可能,并且有多种方法。 许多方法导致脆弱的测试,随着时间的流逝需要大量的维护,而这些测试一开始就非常耗时。

    勘探

    考虑到这一点,我决定进行一个小实验,看看是否可以从这个严格的100%标准中了解成本和后果。

    由于我进行了一些嵌入式系统开发,因此我多年来在嵌入式项目中开发并重用了一些C库。 我决定查看其中的一些库,看看将它们提高到100%代码覆盖率将是多么困难。 在此过程中,我注意了对代码清晰度,代码结构和性能的影响。

    具有预先存在的依赖项注入的库

    第一步是通过将代码覆盖率添加到代码库中来进行度量。 由于这是C语言,因此默认情况下, gcc使用--coverage选项提供了很多功能 ,而lcov (使用genhtml )可以很好地完成报告; 因此,此步骤很容易。 我希望开始的覆盖范围会不错-确实如此,但是它有一些未经测试的分支,以及围绕错误情况和错误报告的预计差距。

    我使错误报告可插入,因此在以前未经测试的分支中,更容易捕获错误消息并对其进行断言。

    由于此代码已经允许mallocfree的可插入实现,因此可以编写一些可以将内存分配失败注入到其中的malloc和free包装器非常简单。 一两个小时之内,就覆盖了。

    在此过程中,我意识到,从调用客户端代码的角度来看,有一种情况不可能区分发生错误的情况和NULL是有效返回值的情况。 对于您的C程序员,它基本上类似于以下内容:

    /* stashes a copy of the value
     * returns the previously stashed value */

    char * foo_stash ( foo_s * context,
                    char * stash_me,
                    size_t stash_me_len )
    {
        char * copy = malloc ( stash_me_len ) ;
        if ( copy == NULL ) {
            return NULL ;
        }
        memcpy ( copy, stash_me, stash_me_len ) ;
        char * previous = context - > stash ;
        context - > stash = copy ;
        /* previous may be NULL */
        return previous ;
    }

    我调整了API,以允许显式提供错误信息。 如果您是C开发人员,那么您知道可以通过多种方法来实现。 我选择了类似的方法:

    /* stashes a copy of the value
     * returns the previously stashed value
     * on error, the 'err' pointer is set to 1 */

    char * foo_stash2 ( foo_s * context,
                    char * stash_me,
                    size_t stash_me_len,
                    int * err )
    {
        char * copy = malloc ( stash_me_len ) ;
        if ( copy == NULL ) {
            * err = 1 ;
            return NULL ;
        }
        memcpy ( copy, stash_me, stash_me_len ) ;
        char * previous = context - > stash ;
        context - > stash = copy ;
        /* previous may be NULL */
        return previous ;
    }

    如果没有测试资源枯竭,我可能要花很长时间才能注意到API的这个(现在很明显)缺点。

    为了使lcov报告100%的测试覆盖率,我不得不告诉编译器不要内联任何代码 ,即使在优化级别为零时,我也知道它可以执行此操作。

    当嵌入到实际固件中时,编译器可以优化未使用的间接寻址。 因此,在源代码中添加的间接寻址不会对编译后的固件造成实际性能损失。

    当然,这是简单的库。

    一个更典型的图书馆

    一旦确定了一种在测试中注入内存分配失败的方法,我便决定移至另一个库,但是malloc和free尚未可插入。 我有问题。 这将对代码库造成多大的影响? 它会使代码混乱,使其不清楚吗? 会花多长时间?

    尽管我并不总是记录覆盖率指标,但是我坚信测试:20多年前,我了解到,如果我在实现代码之前编写测试和客户端代码,我的代码就会有所改善,而且我一直以这种方式工作以来。 (在“ 测试驱动开发:示例”中 ,您可以在确认中找到我的名字。)但是,当我向第二个库添加代码覆盖率报告时,我很惊讶地发现(过去的某个时候)我添加了库中有一对函数,而无需为其添加测试。 毫无疑问,其他未经测试的领域是用于处理内存分配失败的代码。

    当然,为这对未经测试的功能编写测试非常容易。 覆盖率工具还显示,我有一个带有未经测试的代码分支的函数,只看一眼便发现其中包含一个错误。 修复很简单,但是考虑到我使用该库的不同项目,我惊讶地发现了一个错误。 尽管如此,这还是一个令人谦卑的提醒,即错误经常隐藏在未经测试的代码中。

    接下来是更具挑战性的内容:资源枯竭测试。 我首先介绍了一些用于malloc / free函数指针的全局变量,以及一个用于保存内存跟踪对象的变量。 一旦可行,我将这些变量从全局范围移到已经存在的上下文参数中。 重构代码以允许必要的间接访问仅花费了几个小时(比我预期的要少的时间),并且添加的复杂性可以忽略不计。

    感言

    我从第一个库中得出的结论是,这是值得的。 现在,代码更加灵活,调用者的API也更加完整,编写故障注入工具非常容易。

    从第二个库中,我想起了即使可插拔较少的代码也可以进行测试,而不会增加不必要的复杂性。 代码得到了改进,我修复了一个错误,并且我对代码更有信心。 同样,能够插入备用内存分配器的附加模块化功能是将来可能会更有价值的功能。

    排除注释是lcov的一项功能,可导致覆盖率报告忽略代码块。 有趣的是,我认为在两个库中都不需要使用排除注释。

    我比以往任何时候都可以肯定的是,通过投资于测试覆盖范围,即使是非常好的代码也可以得到改善。

    这两个代码库都很小,已经具有一定的模块化,从良好的测试角度出发,都是单线程的,并且不包含图形UI代码。 如果我想在我贡献的更大,更单一的代码库之一中解决这个问题,那将变得更加困难并且需要更多的时间投入。 在代码的某些部分中,我可能仍会得出结论,最好的办法是通过调整工具来“作弊”以不报告某些代码部分。

    就是说,我估计达到报告100%的代码覆盖率所需的时间大大少于我在进行此探索之前的估计时间。

    如果您碰巧是C程序员,并且想看一个运行示例,包括gcov / lcov用法,我提取了内存不足的注入代码,并将其放在示例存储库中

    您是否已通过测试将代码库的覆盖率提高到100%,还是尝试过? 你的经验是什么? 请在评论中分享。

    翻译自: https://opensource.com/article/20/4/testing-code-coverage

    分支覆盖率 代码覆盖率

    展开全文
  • 100代码覆盖率神话

    2019-11-26 05:21:05
    (由Adobe Stock Photo许可) 互联网上现在有很多建议说100%的覆盖率不是一个值得的目标。 我非常不同意。 通常,难以测试的代码是需要重构的信号。 我知道了。 几年前,我很讨厌测试。 我以为这只会让我移动得更慢...

    (由Adobe Stock Photo许可)

    互联网上现在有很多建议说100%的覆盖率不是一个值得的目标。

    我非常不同意。

    通常,难以测试的代码是需要重构的信号。

    我知道了。 几年前,我很讨厌测试。 我以为这只会让我移动得更慢。

    当我开始编码时,这并不是人们经常做的事情。 如果是这样,通常是由一个单独的质量检查小组负责。 几年前,尽管它成为一个真正的热门话题。 面试开始期望候选人知道如何编写测试,并且越来越多的组织将其作为质量计划从上至下进行推广。

    我一直努力在比赛中处于领先地位,因此我决定走进面试,并说“测试并不是我真正的强项”不再是一个好看的样子,所以我决定我将获得所有比赛的100%覆盖率从那时起我的测试。

    当时,我不确定自己会从中获得什么好处,或者是否真的有任何好处。

    现在, 老实说 ,我不会回去。 当某件事破坏了覆盖率为100%的代码库时,您的测试很可能会告诉您确切的位置和方式。

    这并不是说单元测试就是您所需要的。 不是。 但是在我看来,未经测试的代码也不是一个好选择。

    回到我身边,回到我也不相信测试覆盖范围的好处的时代。

    第1部分:学习Lingo

    当时,交易工具是摩卡咖啡西农 咖啡 奶酪的组合。 Mocha是测试运行程序,sinon提供了创建“模拟”和“间谍”的功能,而chai是一个断言库,因此您可以以人类语言友好的方式键入断言。

    (这是间谍吗?)

    我基本上不知道这意味着什么。 在我变得有效之前,首先要做的就是学习语言。

    因此,第一件事是第一位- 地狱是间谍还是嘲笑?

    尽管首先想到的是詹姆斯·邦德或伊桑·亨特。 尽管这不是一个可怕的隐喻,但这绝对不是我们在这里谈论的内容。

    在阅读了一些文档之后,我最终了解到,间谍程序是一种功能,已通过测试框架进行了修改,以提供有关其用法的元信息。 间谍。 Kinda喜欢人们如何利用Apple最近的FaceTime Bug监视您。 有点像詹姆斯·邦德。

    模拟与间谍相似,但经过了更多修改。 除了提供并跟踪如何使用特定功能外,它还将其行为更改为可预测的。

    我还了解到有几种测试类型。 不限于三种最常见的:单元测试,集成测试和E2E测试。

    当我们进行“单元测试”时,这意味着我们需要能够将代码分解为各个单元。 该特定单元之外的任何内容都是要被嘲弄的候选对象,例如其他功能或整个模块。 开玩笑是我选择单元测试的工具。 单元测试是衡量覆盖率的唯一测试类型。

    当我们进行集成测试时,我们正在测试我们的软件与其他软件的集成,例如,通过Kafka传递一条消息的测试,该消息应该由我们的服务接收,然后可以在数据库中找到结果。 在创建集成测试时,我通常也喜欢Jest。

    端到端测试有点像使用您的应用程序的机器人。 您可以对其进行编程,以将其加载到浏览器中的网站,单击内容,并确保一切都能从用户角度正常运行。 赛普拉斯是我在该领域最喜欢的工具,但在我学习时还不存在。 Selenium是当今的佼佼者,老实说,这是一个足够大的领域,我很乐意让QA自动化工程师来处理这一部分。

    现在掌握了新知识就成了困难的部分:将其付诸实践。

    我花了几个月的时间来确保我编写的每段代码都具有测试覆盖率。 我承认,起初很难。 我在StackOverflow上花费了大量时间来查找模拟和间谍示例。 到最后,我发现我对代码的信心大大提高了。

    另一个好处是,如果发生故障,我的测试通常会告诉我确切的位置。 当其他工程师对我所做的代码进行更改时,我可以更快地对其进行检查。 当重要的API发生更改时,会通过失败的测试向人们发出警报,并迅速对其进行更新或重新考虑他们的更改。

    不仅如此,我开始编写更好的代码。 我了解到,通常情况下,如果难以测试或难以完全覆盖某些内容,通常意味着我编写的代码不太好,并且可以对其进行重构,从而获得更加可维护和灵活的API。 为此,尝试达到100%的覆盖率鼓励了我将匿名函数提取到命名函数中,并了解许多重构中的部分应用程序和依赖项注入。

    在完成集成测试之后,我甚至放弃了GitFlow来进行基于主干的开发。 几年前,我以为要成为大师是一件疯狂的事情,现在我每天要由近15名工程师组成的团队来做。

    相关阅读我有一个自白。 我致力于掌握

    第2部分:以身作则

    大约在我对新的测试堆栈充满信心的时候,另一种工具被推向市场,许多人声称它使单元测试变得更加简单:Jest。

    Jest是Facebook率先推出的自动化测试框架。

    Jest的工作非常出色,将我以前使用的库浓缩到一个统一的框架中,该框架是一个测试运行器,并且提供了一组用于模拟,监视和断言的API。 除了为您的所有单元测试需求提供单个库之外,Jest在简化某些概念和模式以及强大而简单的模拟方面也做得很出色。

    因为我认为Jest更易于使用和理解,所以我将继续以Jest为例。

    如果您只是和我一起写这篇文章,那很好-到目前为止,您所读的内容都将独立存在。 但是,我一直在记录使用Parcel和Streaming SSR来构建React应用程序的过程,本文将在最后一部分继续。

    在下面链接的上一篇文章中,我展示了如何设置具有代码覆盖率的Jest,并在下一篇文章中说,我将展示如何使覆盖率达到100%。

    相关阅读增强Node.js的代码质量

    我认为证明100%覆盖率的最佳方法是展示如何到达那里。 在整个过程中,我们可能会发现可以重构代码以使其更具可测试性的几个地方。 因此,我将继续进行我离开的地方,并使该项目的覆盖率达到100%,并说明要进行哪些重构,在何处使用部分应用程序和依赖项注入,以及在难以获得覆盖率的过程中要模拟的内容。

    所以...让我们开始吧。 这是我将要从事的项目:

    GitHub链接patrickleet / streaming-ssr-react-styled-components

    该项目在app文件夹中有一个react app,在服务器文件夹中有SSR逻辑。 让我们从应用程序测试开始。

    应用测试

    上一篇文章中 ,在配置了Jest之后,我开始了对一个简单组件的简单测试。 我有几个同样简单的React组件。

    这是功能组件真正强大的原因之一。 函数比类容易测试。 他们没有状态,而是有输入和输出。 给定输入X,它们具有输出Y。存在状态时,可以将其存储在组件的外部。

    新的React Hooks API在这方面很不错,因为它鼓励制作功能组件,并具有易于模拟的机制来为组件提供状态。 Redux在测试方面提供了相同的好处。

    让我们从淘汰其余的简单组件开始。 我们基本上只需要渲染它们,并可能检查是否渲染了一些重要的信息。

    我通常将代码内联在文章中,但是这些测试中并没有什么新内容,因此我决定链接到实际的提交,只显示一个完整的示例:

    让我们看一下About页面:

    import React from 'react'
    import Helmet from 'react-helmet-async'
    import Page from '../components/Page'
    const About = () => (
      < Page >
         <Helmet>
          <title>About Page</title>
        </Helmet>
        <div>This is the about page</div> 
      </ Page >
    )
    export default About

    它是测试:

    import React from 'react'
    import { shallow } from 'enzyme'
    import About from 'app/pages/About.jsx'
    describe( 'app/pages/About.jsx' , () => {
      it( 'renders About page' , () => {
        expect(About).toBeDefined()
        const tree = shallow( < About /> )
        expect(tree.find('Page')).toBeDefined()
        expect(
          tree
            .find('Helmet')
            .find('title')
            .text()
        ).toEqual('About Page')
        expect(tree.find('div').text()).toEqual('This is the about page')
      })
    })

    以下提交中的所有测试都非常相似:

    如您所见,只需确保我们的组件渲染足以使这些组件获得100%的覆盖率即可。 更详细的交互最好留给E2E测试,这不在本文的讨论范围之内。

    下一个组件app/App.jsx稍微复杂一些。 编写渲染测试后,您会注意到路由器中仍然存在无法访问的匿名函数,用于渲染“关于”页面。

    为了访问和测试它,我们想做一个小的重构,将函数提取到一个命名函数,以便我们可以导出它并进行测试。

    现在很容易测试:

    因为上面的“关于”页面有另一组测试,所以我们将保留其更具体的测试,并且只需要检查它是否在此处呈现即可。

    这样,在我们的应用程序中剩下要测试的唯一文件是app/client.js ,然后我们可以继续完成服务器端测试。

    让我们看一下代码:

    import React from 'react'
    import ReactDOM from 'react-dom'
    import { HelmetProvider } from 'react-helmet-async'
    import { BrowserRouter } from 'react-router-dom'
    import { rehydrateMarks } from 'react-imported-component'
    import importedComponents from './imported' // eslint-disable-line
    import App from './App'
    const element = document .getElementById( 'app' )
    const app = (
      < HelmetProvider >
         <BrowserRouter>
          <App />
        </BrowserRouter> 
      </ HelmetProvider >
    )
    // In production, we want to hydrate instead of render
    // because of the server-rendering
    if (process.env.NODE_ENV === 'production' ) {
      // rehydrate the bundle marks
      rehydrateMarks().then( () => {
        ReactDOM.hydrate(app, element)
      })
    } else {
      ReactDOM.render(app, element)
    }
    // Enable Hot Module Reloading
    if ( module .hot) {
      module .hot.accept()
    }

    我注意到的第一件事是依赖全局变量documentprocessmodule 第二件事是什么也没有导出,因此可能很难用不同的输入多次运行。

    我们可以通过一些重构来解决这个问题:

    1. 将所有逻辑包装到我们可以导出的函数中。 此函数将接受具有所有依赖项的options对象。 这称为依赖注入 如果选择的话,这将使我们能够轻松地传递一堆东西的模拟版本。
    2. 重新水化后,我们在生产模式下有一个匿名函数,应将其提取为命名函数。

    我们还将要模拟一些外部模块: react-domreact-imported-componentapp/imported.js 模块本身就是依赖注入的一种形式。

    首先,这里是新重构的文件,其中的更改以粗体显示:

    import React from 'react'
    import ReactDOM from 'react-dom'
    import { HelmetProvider } from 'react-helmet-async'
    import { BrowserRouter } from 'react-router-dom'
    import { rehydrateMarks } from 'react-imported-component'
    import importedComponents from './imported' // eslint-disable-line
    import App from './App'
    // use "partial application" to make this easy to test
    export const hydrate = ( app, element ) => () => {
      ReactDOM.hydrate(app, element)
    }
    export const start = ({ 
      isProduction,
      document ,
      module ,
      hydrate
     }) => {
      const element = document .getElementById( 'app' )
      const app = (
        < HelmetProvider >
           <BrowserRouter>
            <App />
          </BrowserRouter> 
        </ HelmetProvider >
      )
      // In production, we want to hydrate instead of render
      // because of the server-rendering
      if (isProduction) {
        // rehydrate the bundle marks from imported-components, 
        // then rehydrate the react app
        rehydrateMarks().then(hydrate(app, element))
      } else {
        ReactDOM.render(app, element)
      }
      // Enable Hot Module Reloading
      if ( module .hot) {
        module .hot.accept()
      }
    }
    const options = {
      isProduction : process.env.NODE_ENV === 'production' ,
      document : document ,
      module : module ,
      hydrate
    }
    start(options)

    现在,我们实际上可以使用各种选项访问和测试启动,以及独立于启动逻辑测试水合物。

    测试时间有点长,所以我在网上添加了注释以解释发生了什么。 这是对该文件的测试:

    import React from 'react'
    import fs from 'fs'
    import path from 'path'
    import { start, hydrate } from 'app/client'
    import { JSDOM } from "jsdom"
    jest.mock( 'react-dom' )
    jest.mock( 'react-imported-component' )
    jest.mock( 'app/imported.js' )
    // mock DOM with actual index.html contents
    const pathToIndex = path.join(process.cwd(), 'app' , 'index.html' )
    const indexHTML = fs.readFileSync(pathToIndex).toString()
    const DOM = new JSDOM(indexHTML)
    const document = DOM.window.document
    // this doesn't contribute to coverage, but we
    // should know if it changes as it would
    // cause our app to break
    describe( 'app/index.html' , () => {
      it( 'has element with id "app"' , () => {
        const element = document .getElementById( 'app' )
        expect(element.id).toBe( 'app' )
      })
    })
    describe( 'app/client.js' , () => {
      // Reset counts of mock calls after each test
      afterEach( () => {
        jest.clearAllMocks()
      })
      describe( '#start' , () => {
        it( 'renders when in development and accepts hot module reloads' , () => {
          // this is mocked above, so require gets the mock version
          // so we can see if its functions are called
          const ReactDOM = require ( 'react-dom' )
          
          // mock module.hot
          const module = {
            hot : {
              accept : jest.fn()
            }
          }
          // mock options
          const options = {
            isProduction : false ,
            module ,
            document
          }
          start(options)
          expect(ReactDOM.render).toBeCalled()
          expect( module .hot.accept).toBeCalled()
        })
        
        it( 'hydrates when in production does not accept hot module reloads' , () => {
          const ReactDOM = require ( 'react-dom' )
          const importedComponent = require ( 'react-imported-component' )
          importedComponent.rehydrateMarks.mockImplementation( () => Promise .resolve())
          // mock module.hot
          const module = {}
          // mock rehydrate function
          const hydrate = jest.fn()
          // mock options
          const options = {
            isProduction : true ,
            module ,
            document ,
            hydrate
          }
          start(options)
          expect(ReactDOM.render).not.toBeCalled()
          expect(hydrate).toBeCalled()
        })
      })
      describe( '#hydrate' , () => {
        it( 'uses ReactDOM to hydrate given element with an app' , () => {
          const ReactDOM = require ( 'react-dom' )
          const element = document .getElementById( 'app' )
          const app = ( < div > </ div > )
          const doHydrate = hydrate(app, element)
          expect( typeof doHydrate).toBe( 'function' )
          doHydrate()
          expect(ReactDOM.hydrate).toBeCalledWith(app, element)
        })
      })
    })

    现在,当我们运行测试时,除了应生成的文件app/imported.js之外,我们应该对app文件夹具有100%的覆盖率,并且进行测试没有意义,因为它在将来的版本中可能会有所不同。

    让我们更新jest配置,以从覆盖率统计信息中忽略它,并检查结果。

    jest.config添加:

    "coveragePathIgnorePatterns" : [
      "<rootDir>/app/imported.js" ,
      "/node_modules/"
    ]

    现在,当我们运行npm run test我们得到以下结果。

    GitHub链接test:client.js tests·patrickleet / streaming-ssr-react-styled-components @ c5fcfe9

    我想指出的是,在开发测试时,我通常使用“监视”模式来执行此操作,因此在更改测试时,它们会自动重新运行。

    完成应用程序测试后,让我们继续进行服务器。

    服务器测试

    在上一篇文章中,我为一个应用程序文件和一个服务器文件编写了测试,因此我们已经server/index.js 进行了测试 现在我们需要测试server/lib剩余的三个文件。

    让我们从server/lib/client.js

    import fs from 'fs'
    import path from 'path'
    import cheerio from 'cheerio'
    export const htmlPath = path.join(process.cwd(), 'dist' , 'client' , 'index.html' )
    export const rawHTML = fs.readFileSync(htmlPath).toString()
    export const parseRawHTMLForData = ( template, selector = '#js-entrypoint' ) => {
      const $template = cheerio.load(template)
      let src = $template(selector).attr( 'src' )
      return {
        src
      }
    }
    const clientData = parseRawHTMLForData(rawHTML)
    const appString = '<div id="app">'
    const splitter = '###SPLIT###'
    const [startingRawHTMLFragment, endingRawHTMLFragment] = rawHTML
      .replace(appString, ` ${appString} ${splitter} ` )
      .split(splitter)
    export const getHTMLFragments = ( { drainHydrateMarks } ) => {
      const startingHTMLFragment = ` ${startingRawHTMLFragment} ${drainHydrateMarks} `
      return [startingHTMLFragment, endingRawHTMLFragment]
    }

    首先,我注意到有一个很大的代码块,以前的废弃策略中甚至没有在项目中使用过。 export const parseRawHTMLForDataconst clientData

    我将从删除它开始。 代码越少,错误可以存在的位置就越少。 还有一些我从未使用过的导出可以对模块保持私有。

    这是更新的文件:

    import fs from 'fs'
    import path from 'path'
    const htmlPath = path.join(process.cwd(), 'dist' , 'client' , 'index.html' )
    const rawHTML = fs.readFileSync(htmlPath).toString()
    const appString = '<div id="app">'
    const splitter = '###SPLIT###'
    const [startingRawHTMLFragment, endingRawHTMLFragment] = rawHTML
      .replace(appString, ` ${appString} ${splitter} ` )
      .split(splitter)
    export const getHTMLFragments = ( { drainHydrateMarks } ) => {
      const startingHTMLFragment = ` ${startingRawHTMLFragment} ${drainHydrateMarks} `
      return [startingHTMLFragment, endingRawHTMLFragment]
    }

    看起来可能需要为此进行一项测试。 但是,该计划中有一个小问题:此文件取决于之前运行的构建,因为它读取生成的构建。

    从技术上讲,这是有道理的,因为如果没有内置的应用程序可以渲染,则永远不会尝试在服务器上渲染应用程序。

    考虑到该约束,我认为这是可以的,并且由于我们只能确保在测试之前就建立了管道调用,因此可能不值得进行重构。 如果我们想要真正纯净的单元隔离,则可以考虑重构一些,因为从技术上讲整个应用程序都是SSR的依赖项,因此可以对其进行模拟。 另一方面,无论如何,使用实际构建可能更有用。 在编写测试的整个过程中,您经常会遇到这样的折衷。

    话虽如此,这是测试以获得该模块的完整介绍:

    import { getHTMLFragments } from 'server/lib/client.js'
    describe( 'client' , () => {
      it( 'exists' , () => {
        const drainHydrateMarks = '<!-- mock hydrate marks -->'
        const [start, end] = getHTMLFragments({ drainHydrateMarks })
        expect(start).toContain( '<head>' )
        expect(start).toContain(drainHydrateMarks)
        expect(end).toContain( 'script id="js-entrypoint"' )
      })
    })

    并提交: 修复:删除未使用的代码来分析模板测试:服务器/库/客户端测试

    接下来, server/lib/server.js很小,所以让我们把它淘汰掉。 这是它的代码,可以刷新您的记忆,或者如果您现在才加入我们:

    import express from 'express'
    export const server = express()
    export const serveStatic = express.static

    和测试:

    import express from 'express'
    import { server, serveStatic } from 'server/lib/server.js'
    describe( 'server/lib/server' , () => {
      it( 'should provide server APIs to use' , () => {
        expect(server).toBeDefined()
        expect(server.use).toBeDefined()
        expect(server.get).toBeDefined()
        expect(server.listen).toBeDefined()
        expect(serveStatic).toEqual(express.static)
      })
    })

    似乎我们基本上只是在履行快递的全部责任,而我们希望快递提供这份合同,我们只需简单地确定它就可以了,而超出此范围并没有任何意义。

    最后,我们只有一个文件要测试: server/lib/ssr.js

    这是我们的ssr模块:

    import React from 'react'
    import { renderToNodeStream } from 'react-dom/server'
    import { HelmetProvider } from 'react-helmet-async'
    import { StaticRouter } from 'react-router-dom'
    import { ServerStyleSheet } from 'styled-components'
    import { printDrainHydrateMarks } from 'react-imported-component'
    import log from 'llog'
    import through from 'through'
    import App from '../../app/App'
    import { getHTMLFragments } from './client'
    // import { getDataFromTree } from 'react-apollo';
    export default (req, res) => {
      const context = {}
      const helmetContext = {}
      const app = (
        < HelmetProvider context = {helmetContext} >
           <StaticRouter location={req.originalUrl} context={context}>
            <App />
          </StaticRouter> 
        </ HelmetProvider >
      )
      try {
        // If you were using Apollo, you could fetch data with this
        // await getDataFromTree(app);
        const sheet = new ServerStyleSheet()
        const stream = sheet.interleaveWithNodeStream(
          renderToNodeStream(sheet.collectStyles(app))
        )
        if (context.url) {
          res.redirect( 301 , context.url)
        } else {
          const [startingHTMLFragment, endingHTMLFragment] = getHTMLFragments({
            drainHydrateMarks : printDrainHydrateMarks()
          })
          res.status( 200 )
          res.write(startingHTMLFragment)
          stream
            .pipe(
              through(
                function write ( data )  {
                  this .queue(data)
                },
                function end ( )  {
                  this .queue(endingHTMLFragment)
                  this .queue( null )
                }
              )
            )
            .pipe(res)
        }
      } catch (e) {
        log.error(e)
        res.status( 500 )
        res.end()
      }
    }

    它有点长,并且有一些执行路径。 我确实想进行几个小的重构,使隔离稍微容易一些,例如提取逻辑以将应用程序生成为单独的函数,并使用部分应用程序能够注入应用程序流渲染器,以便我们轻松进行模拟一些重定向。

    另外,写入和结束也有些困难,因此我们也可以使用部分应用程序将它们拉高。

    这是更新的版本:

    import React from 'react'
    import { renderToNodeStream } from 'react-dom/server'
    import { HelmetProvider } from 'react-helmet-async'
    import { StaticRouter } from 'react-router-dom'
    import { ServerStyleSheet } from 'styled-components'
    import { printDrainHydrateMarks } from 'react-imported-component'
    import log from 'llog'
    import through from 'through'
    import App from '../../app/App'
    import { getHTMLFragments } from './client'
    // import { getDataFromTree } from 'react-apollo';
    const getApplicationStream = ( originalUrl, context ) => {
      const helmetContext = {}
      const app = (
        < HelmetProvider context = {helmetContext} >
           <StaticRouter location={originalUrl} context={context}>
            <App />
          </StaticRouter> 
        </ HelmetProvider >
      )
      const sheet = new ServerStyleSheet()
      return sheet.interleaveWithNodeStream(
        renderToNodeStream(sheet.collectStyles(app))
      )
    }
    export function write ( data )  {
      this .queue(data)
    }
    // partial application with ES6 is quite succinct
    // it just means a function which returns another function
    // which has access to values from a closure
    export const end = endingHTMLFragment =>
      function end ( )  {
        this .queue(endingHTMLFragment)
        this .queue( null )
      }
    export const ssr = getApplicationStream => (req, res) => {
      try {
        // If you were using Apollo, you could fetch data with this
        // await getDataFromTree(app);
        const context = {}
        const stream = getApplicationStream(req.originalUrl, context)
        if (context.url) {
          return res.redirect( 301 , context.url)
        }
        const [startingHTMLFragment, endingHTMLFragment] = getHTMLFragments({
          drainHydrateMarks : printDrainHydrateMarks()
        })
        res.status( 200 )
        res.write(startingHTMLFragment)
        stream.pipe(through(write, end(endingHTMLFragment))).pipe(res)
      } catch (e) {
        log.error(e)
        res.status( 500 )
        res.end()
      }
    }
    const defaultSSR = ssr(getApplicationStream)
    export default defaultSSR

    以下是查看Github中差异的链接: chore:重构ssr以使其分解/使其更易于阅读 ,以及chore:重构ssr more

    现在让我们编写一些测试。 我们需要为此文件专门设置节点的jest-environment,否则styled-components部分将不起作用。

    /**
     * @jest-environment node
     */
    import defaultSSR, { ssr, write, end } from 'server/lib/ssr.js'
    jest.mock( 'llog' )
    const mockReq = {
      originalUrl : '/'
    }
    const mockRes = {
      redirect : jest.fn(),
      status : jest.fn(),
      end : jest.fn(),
      write : jest.fn(),
      on : jest.fn(),
      removeListener : jest.fn(),
      emit : jest.fn()
    }
    describe( 'server/lib/ssr.js' , () => {
      describe( 'ssr' , () => {
        it( 'redirects when context.url is set' , () => {
          const req = Object .assign({}, mockReq)
          const res = Object .assign({}, mockRes)
          const getApplicationStream = jest.fn( ( originalUrl, context ) => {
            context.url = '/redirect'
          })
          const doSSR = ssr(getApplicationStream)
          expect( typeof doSSR).toBe( 'function' )
          doSSR(req, res)
          expect(res.redirect).toBeCalledWith( 301 , '/redirect' )
        })
        it( 'catches error and logs before returning 500' , () => {
          const log = require ( 'llog' )
          const req = Object .assign({}, mockReq)
          const res = Object .assign({}, mockRes)
          const getApplicationStream = jest.fn( ( originalUrl, context ) => {
            throw new Error ( 'test' )
          })
          const doSSR = ssr(getApplicationStream)
          expect( typeof doSSR).toBe( 'function' )
          doSSR(req, res)
          expect(log.error).toBeCalledWith( Error ( 'test' ))
          expect(res.status).toBeCalledWith( 500 )
          expect(res.end).toBeCalled()
        })
      })
      describe( 'defaultSSR' , () => {
        it( 'renders app with default SSR' , () => {
          const req = Object .assign({}, mockReq)
          const res = Object .assign({}, mockRes)
          defaultSSR(req, res)
          expect(res.status).toBeCalledWith( 200 )
          expect(res.write.mock.calls[ 0 ][ 0 ]).toContain( '<!DOCTYPE html>' )
          expect(res.write.mock.calls[ 0 ][ 0 ]).toContain(
            'window.___REACT_DEFERRED_COMPONENT_MARKS'
          )
        })
      })
      describe( '#write' , () => {
        it( 'write queues data' , () => {
          const context = {
            queue : jest.fn()
          }
          const buffer = new Buffer.from( 'hello' )
          write.call(context, buffer)
          expect(context.queue).toBeCalledWith(buffer)
        })
      })
      describe( '#end' , () => {
        it( 'end queues endingFragment and then null to end stream' , () => {
          const context = {
            queue : jest.fn()
          }
          const endingFragment = '</html>'
          const doEnd = end(endingFragment)
          doEnd.call(context)
          expect(context.queue).toBeCalledWith(endingFragment)
          expect(context.queue).toBeCalledWith( null )
        })
      })
    })

    由于此文件比其他文件要复杂一些,因此需要进行更多测试才能打到所有分支。 为了清楚起见,每个函数都包装在自己的describe块中。

    这是在Github上的提交: test:ssr单元测试

    现在,当我们运行测试时,我们具有100%的覆盖率!

    最后,在整理之前,我将对jest.config进行一些小的更改以强制100%覆盖。 与第一次接触相比,保持覆盖范围要容易得多。 我们测试的许多模块几乎不会改变。

      "coverageThreshold" : {
        "global" : {
          "branches" : 100 ,
          "functions" : 100 ,
          "lines" : 100 ,
          "statements" : 100
        }
      },

    并做了! 这是Github上的提交: 杂项:要求100%覆盖率

    结论

    我在本文中的目标是演示能够重构代码或使用模拟和依赖注入来隔离单元以使难以测试的代码易于达到并讨论达到100%覆盖率的某些优点所需的技术。 此外,从起点开始使用TDD会容易得多。

    我坚信,如果100%的覆盖率很难达到,那是因为代码需要重构。

    在许多情况下,端到端测试对于某些方面来说将是更好的测试。 基于此的Cypress.io套件可以加载应用程序并单击,这将大大提高我们的信心。

    我相信,在覆盖率为100%的代码库中,在提高您对每个发行版的信心以及因此提高进行和检测重大更改的速度方面所做的工作非常出色。

    与往常一样,如果您发现这很有用,请鼓掌,关注我, 在GitHub项目上留下星星,和/或在社交网络上分享!

    在下一部分中,即将到来的是,我们将添加一个可用于生产环境的Dockerfile,并探索如何仅使用另一个Dockerfile,或者将我们的应用程序打包为Nginx服务的静态站点,以及这两种方法之间的权衡。

    最好,
    帕特里克·李·斯科特

    查看本系列的其他文章! 这是第4部分。

    From: https://hackernoon.com/the-100-code-coverage-myth-900b83d20d3d

    展开全文
  • 绝大多数验证工程师会将覆盖率分为两大类:代码覆盖率和功能覆盖率。本文主要介绍有关代码覆盖率的概念。 代码覆盖率 代码覆盖率是衡量验证进展的最简易的方式。它的作用是检查代码是否冗余,设计要点是否遍历,被...
  • 代码覆盖率

    2015-05-01 15:42:39
    首先,我们需要明确一个问题,什么是代码覆盖率?基本上可以理解为测试过程中运行代码的行数与总行数的比率。代码覆盖程度的度量方式有很多种,比如语句覆盖,判定覆盖,条件覆盖,路经覆盖,这四种基本的覆盖方式...
  • python统计代码覆盖率

    2019-09-19 18:37:10
    1 覆盖率即统计在时间段内哪些代码被执行和调用了,如有100代码覆盖率统计就是从开始统计时间到结束时间执行了多少的代码的比例。 2 为什么要用,补充测试case。接到一个测试需求,按业务等设计了测试case,但心里...
  • 代码覆盖率分析

    千次阅读 2016-08-22 09:49:21
    通过分析来提高代码覆盖率 利用VectorCAST/CBA,用户可以标记出经过分析覆盖的代码。这样,高安全行业的开发工程师就能够通过覆盖率分析来提高测试所达到的覆盖率了,使覆盖率按照规定达到100%。 利用VectorCAST...
  • 代码覆盖度指标 上周,我与我的长期... 因此,达到80%(或100%)的代码覆盖率并对其吹牛是与吹牛一样有用。 可以肯定的是,在Twitter上进行基于事实的辩论非常困难,因为140个字符对任何论点都施加了严格的限制。 ...
  • 100代码覆盖率的悲剧

    千次阅读 2017-05-23 07:20:43
    原文:http://labs.ig.com/code-coverage-100-percent-tragedy 作者:Daniel Lebrero 翻译:Aladdin 本文Daniel Lebrero在大数据团队担任IG的技术架构师。拥有超过15年的Java经验和4年的Clojure经验,他现在是...
  • 在软件测试中,有一个重要的概念叫做代码覆盖率,一般在单元测试中作为测试充分性的重要衡量指标,那么代码覆盖率达到100%是否就算覆盖全了?答案显然是否定的,博客园中有篇博文《代码覆盖率浅谈》已经说明的很详细...
  • 代码覆盖率测试

    2008-12-04 17:40:00
    代码覆盖率测试常用的统计数据: 1. 行覆盖率 2. 分支覆盖率 代码覆盖率的作用: 1. 清晰的知道哪些代码未被测试过,和developer商量在未被测试的代码中哪些需要进行测试 2. 衡量测试用例质量的重要标准...
  • 关于测试代码覆盖率

    千次阅读 2014-03-07 09:16:01
    关于测试代码覆盖率 我读了软件测试代码覆盖率的讨论。讨论主要是由哪些代码覆盖率数字单元测试或整体测试是正确的或所需。40%、60%、80%、甚至100%?一些声称,谷歌只需要60%。一些问我在微软代码覆盖数要求是什么...
  • 前言我们在做测试的时候,经常遇到领导的灵魂拷问:你的测试用例覆盖率是多少,达到100%了么?你如何保证你的测试质量?测试用例的覆盖率如何统计呢,如何知道开发的代码,我们都测到了,不会存在漏测的情况。pytest...
  • 什么叫代码覆盖率

    千次阅读 2013-03-30 19:46:31
    什么叫代码覆盖率 什麼叫代碼覆蓋率?它的作用是什麼?在測試流程過程中,它什麼時候做?另外,有什麼工具可以實現該功能?它與開發代碼中的代碼覆蓋率有什麼區別沒? 在測試中的代碼覆蓋率是指,你运行测试用例后,...
  • <div><p>虽然,100% 的代码覆盖率也说明不了什么。 但是,没有覆盖率的代码也容易出现隐患。 希望可以添加代码覆盖率的 badge。</p><p>该提问来源于开源项目:nntaoli-project/goex</p></div>
  • 上周,我与我的长期朋友... 因此,达到80%(或100%)的代码覆盖率并自吹自bra与吹拂风一样有用。 当然,在Twitter上进行基于事实的辩论非常困难,因为140个字符对任何论点都施加了严格限制。 本文试图在无限的空间...
  • 合理的代码覆盖率

    2013-01-25 08:20:00
    每当谈及代码覆盖率时,总有人会问“覆盖率的标准应当是多少?”应该是100%,90%,还是80%? 答案是,看情况。选择覆盖率的标准时,应该考虑所用的技术、语言以及开发工具等,还要考虑其他许多因素。java及J2EE项目...
  • 圈复杂度和代码覆盖率

    千次阅读 2010-03-09 09:40:00
    圈复杂度和代码覆盖率(转载请注明来源于金庆的专栏)100%代码覆盖率的单元测试并不代表是足够的测试,下面是一个例子:int foo(bool isOK){ const int ZERO = 0; int* pInt = NULL; if (isOk) { pInt = &ZERO;...
  • 1、 学习单元测试和代码覆盖率工具的使用 (1)写一个程序,用于分析一个字符串中...(3)用ElcEmma查看代码覆盖率,要求覆盖率达到100%。 2、 学习单元测试代码覆盖率工具的使用 (1)把一个英语句子中的单词...
  • (1)通过分析来提高代码覆盖率  利用VectorCAST/CBA,用户可以标记出经过分析覆盖的代码。这样,高安全行业的开发工程师就能够通过覆盖率分析来提高测试所达到的覆盖率了,使覆盖率按照规定达到100%。...
  • 利用VectorCAST/CBA,用户可以标记出经过...高安全行业的开发工程师必须对测试过程中未执行到的代码进行分析,并将分析记录为需求的一部分,从而使结构覆盖率达到100%。 这种分析通常只用于一小部分难以甚至无法...
  • 软件测试是软件开发的组成部分。开发和执行代码的每个行业都可以从使用结构化代码覆盖率测试工具中受益...在嵌入式安全性和安全性至关重要的系统中获得100%的代码覆盖率指标可以达到多种目的。主要目的是符合DO-178...
  • 我最近写过一篇关于陷入代码覆盖率百分比陷阱的文章,这引发了热烈的讨论,所以我想我将更深入地...如果你“确实”拥有100%的代码覆盖率,那又意味着什么呢?你是如何测量的? 有很多不同的方法来衡量覆盖率。 衡
  • 在做单元测试时,代码覆盖率常常被拿来作为衡量测试好坏的指标,甚至,用代码覆盖率来考核测试任务完成情况。但是我相信,你不是为了覆盖率才...他们盲目地追逐100%的代码覆盖率,并相信,如果拥有这个数字,该软件..
  • 这篇来学习一个单元测试领域中很重要的一个概念,代码覆盖率,这个术语,早起是在航空航天方面的软件里提出来的一个测试方法,100%代码覆盖率是一个不可能完成的任务,只是一个理想的目标。本篇先来学习代码覆盖率和...

空空如也

空空如也

1 2 3 4 5 ... 19