【问题标题】:How do you test the functionality of less CSS with Jest?你如何用 Jest 测试 less CSS 的功能?
【发布时间】:2022-02-08 00:00:25
【问题描述】:

我最近加入了一个新组织,他们使用大量 CSS 来隐藏/显示元素。

首先,这是一种好的做法吗?我总是(在大多数情况下)使用布尔值显示和隐藏组件以在 dom 中添加或删除它(这也很容易测试)

在尝试使用 @testing-library/react 添加测试时,我发现使用 identity-obj-proxy 模块可以看到这些类。

但是,当尝试测试一个元素是否可见的功能时,这变得很困难,因为我认为编译的代码不会更少。

是否可以编译更少的代码以反映在测试中?

可能与正在使用的类名模块有关吗?

测试失败

  it('should open and close when clicked', async () => {
    render(
      <Collapse
        label="Collapse"
        testId="collapse-test"
        isHidden
      >
        <div>
          <h1>just some demo text</h1>
        </div>
      </Collapse>
    )
    const content = screen.getByTestId('collapse-test-content')
    expect(content).not.toBeVisible()
    userEvent.click(screen.getByTestId('collapse-test-button'))
    await waitFor(() => expect(content).toBeVisible())
  })

====================result====================
expect(element).not.toBeVisible()

    Received element is visible:
      <div aria-expanded="false" class="accordionContent contentHidden" data-testid="collapse-test-content" />

      38 |     )
      39 |     const content = screen.getByTestId('collapse-test-content')
    > 40 |     expect(content).not.toBeVisible()
         |                         ^
      41 |     userEvent.click(screen.getByTestId('collapse-test-button'))
      42 |     await waitFor(() => expect(content).toBeVisible())
      43 |   })

组件

import React, { useState } from 'react'
import cn from 'classnames'
import styles from './styles.less'

const AccordionContent = ({ children, hidden, testId }) => {
  const displayClass = hidden ? styles.contentHidden : styles.contentBlock

  const accordionContentClass = cn(styles.accordionContent, displayClass)
  return (
    <div
      className={ accordionContentClass }
      aria-expanded={ !hidden }
      data-testid={ `${testId}-content` }
    >
      {children}
    </div>
  )
}

const CollapseComponent= ({
  isHidden,
  onClick,
  label,
  children,
  testId
}) => {
  const [hidden, toggleHidden] = useState(isHidden)

  const handleOnpress = () => {
    toggleHidden((curr) => !curr)
    if (onClick) { onClick }
  }

  return (
    <div
      className={ styles.accordionWrapper }
      data-testid={ testId }
    >
      <AccordionButton
        onPress={ handleOnpress }
        buttonLabel={ label }
        testId={ testId }
      />
      <AccordionContent
        hidden={ !!hidden }
        testId={ testId }
      >
        {children}
      </AccordionContent>
    </div>
  )
}

styles.less

.accordion-content {
  background-color: @preservica-gray-1;
  display: flex;
}

.content-hidden {
  display: none;
} 

.content-block {
  display: flex;
}

jest.config

const config = {
  testEnvironment: 'jsdom',
  coverageThreshold: {
    global: {
      statements: 80,
      branches: 75,
      functions: 75,
      lines: 80
    }
  },
  testPathIgnorePatterns: [
    "./src/components/atoms/Icons",
    "./src/models"
  ],
  coveragePathIgnorePatterns: [
    "./src/components/atoms/Icons",
    "./src/models"
  ],
  setupFilesAfterEnv: [
    "<rootDir>/src/setupTests.ts"
  ],
  moduleNameMapper: {
    "\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
    "\\.(css|less)$": "identity-obj-proxy",
    "^@root(.*)$": "<rootDir>/src$1",
    "^common(.*)$": "<rootDir>/src/common$1",
    "^translation(.*)$": "<rootDir>/src/translation$1",
    "^view(.*)$": "<rootDir>/src/view$1",
    "^actions(.*)$": "<rootDir>/src/actions$1",
    "^usecases(.*)$": "<rootDir>/src/usecases$1",
    "^repository(.*)$": "<rootDir>/src/repository$1",
    "^models(.*)$": "<rootDir>/src/models$1",
    "^router(.*)$": "<rootDir>/src/router$1",
  },
  transform: {
    "^.+\\.(ts|tsx|js|jsx)$": "ts-jest",
  },
  snapshotSerializers: [
    "enzyme-to-json/serializer"
  ]
}

【问题讨论】:

  • 测试 classcontentHidden,而不是可见性。
  • 我有一个测试可以涵盖这一点,但我想实际测试它是否可见。因为拥有该类名并不意味着内容必须隐藏/显示

标签: javascript reactjs jestjs less testing-library


【解决方案1】:

您可以在Jest docs 中阅读更多关于 Jest 如何处理模拟 CSS 模块的信息。您也许可以编写自己的模块名称映射器或自定义转换来加载和处理 Less 文件。但是,您必须弄清楚如何将 CSS 实际注入到被测代码中(这是 Webpack 通常处理的事情)。 jest-transform-css 之类的东西可能会这样做。

就个人而言,我只是测试 CSS 类是否存在,就像 @jonrsharpe 建议的那样。从test pyramid 的角度考虑:您的 Jest 测试可能应该集中在单元测试级别,强调速度和简单性。理想情况下,单元测试足够快,以至于您几乎可以在保存文件时立即运行它们;增加解析和插入的复杂性 Less CSS 可能会对此产生不利影响。

如果单元测试不测试整个堆栈也没关系;你有其他测试,在金字塔的更高处,可以做到这一点。例如,您可能有一些 Cypress 测试在实际浏览器中运行您的应用程序并验证几个控件实际上是隐藏的,那么假设 (1) Jest 验证所有控件设置正确应该是安全的class plus (2) Cypress 验证具有正确类的一些控件被正确隐藏意味着 (3) 所有控件都被正确隐藏。

为了帮助您的测试更加自我记录,并在您更改控件的显示和隐藏方式时使它们更易于维护,您可以使用 Jest 的 expect.extend 来制作您自己的匹配器。也许是这样的(未经测试):

expect.extend({
  toBeVisibleViaCss(received) {
    const pass = !received.classList.contains('content-hidden');
    const what = pass ? 'not visible' : 'visible';
    return {
      message: () => `expected ${received} to be ${what}`,
      pass,
    };
  },
});

首先,这是一种好的做法吗?我总是(在大多数情况下)使用布尔值显示和隐藏组件,以便在 dom 中添加或删除它(这也很容易测试)。

通过 CSS 隐藏组件肯定不是我习惯的。在不了解您的代码库的情况下,我想知道开发人员是否习惯了以前的 jQuery 风格的通过操作类列表来隐藏的方法。我知道保持组件始终呈现的主要优点是您可以根据需要为它们的过渡设置动画。我不确定性能如何比较;浏览器可能会发现切换 CSS 类比添加或删除元素更快,但删除元素意味着 React 需要呈现的内容更少,这有助于提高性能。

【讨论】:

  • 感谢乔希的全面回答。我查看了用于模拟 CSS 文件的文档,但我发现的很多内容只是将它模拟为一个没有做我想要的空对象。从你所说的来看,这似乎不是一件容易的事情,甚至是否值得。但是,我将研究 jest-transform-css 并查看我们是否遇到任何性能问题。您对树上的其他测试提出了很好的意见,所以也许我可以停止对此感到压力!谢谢
猜你喜欢
  • 1970-01-01
  • 2016-01-19
  • 1970-01-01
  • 2019-06-16
  • 1970-01-01
  • 1970-01-01
  • 2021-05-27
  • 2019-04-24
  • 1970-01-01
相关资源
最近更新 更多