Testing React Checkbox Component

栏目: IT技术 · 发布时间: 6年前

内容简介:published date: [2019-08-29 Thu]keywords: react, testing, checkbox, querySelector, rerender, queryByTestId, testing-libraryRecently, I built a partial form section that included a couple of checkboxes, and I wanted to verify their operation with a unit tes

Testing React Checkbox Component

published date: [2019-08-29 Thu]

keywords: react, testing, checkbox, querySelector, rerender, queryByTestId, testing-library

Recently, I built a partial form section that included a couple of checkboxes, and I wanted to verify their operation with a unit test.

I’m using testing-library , including the react-specific methods. I’m finding this a godsend in making tests easier to write and understand.

Testing and debugging react components is still fraught with uncertainty for me, I’m still learning and fumbling my way around. Hooks have made writing components much easier, without a doubt, yet there are still times I just don’t know what’s happening.

In this particular episode, your intrepid frontend dev (me) had to find out three things about the form segment:

  1. a lookup (autosuggest) field was completely hidden until a partcular checkbox was set.
  2. another checkbox was disabled until another field had data in it.
  3. a specific row of the form would appear only when another value was true.

For our purposes here, only 1 and 2 are of interest.

Displaying a field form when a checkbox is setfield

For this form row, the code under test looks like this:

const ServiceLocation = ({ name, value, initialValue, onSelectParent }) => {
  const [serviceArea, setServiceArea] = useState(!!value)
  const toggleCheckbox = () => {
    setServiceArea(!serviceArea)
  }
  return (
    <FormField name={name}>
      <FormFieldSubRow>
        <Checkbox
          value={serviceArea}
          onChange={toggleCheckbox}
          data-testid="service-area-checkbox"
        >
          {" "}
          {I18n.t("service_location.label", { scope: i18nScope })}
        </Checkbox>{" "}
        {serviceArea ? (
          <CustomerLookupWrapper data-testid="customer-lookup-wrapper">
            <CustomerLookup
              name={name}
              initialValue={initialValue}
              placeholder="Parent"
              onSelect={onSelectParent}
            />
          </CustomerLookupWrapper>
        ) : null}
      </FormFieldSubRow>
    </FormField>
  )
}

Placing the data-testid “service-area-checkbox” on the Checkbox component gives the test a way of finding the DOM element it needs to work with. The “customer-lookup-wrapper” `data-testid` will be used to determine if the second element shows up when the that checkbox is clicked.

The spec for this looks like:

it("displays the parent lookup field when the service location checkbox becomes checked", async () => {
  const LOOKUP_WRAPPER_TESTID = "customer-lookup-wrapper"
  const props = {
    fields: {},
    parent: {},
    handleChange: jest.fn(),
    onDuplicateCustomerName: jest.fn(),
    onSelectParent: jest.fn()
  }

  const { queryByTestId, getByTestId, rerender } = render(
    <Providers>
      <CustomerInfoFormSection {...props} />
    </Providers>
  )

  const serviceAreaCheckbox = getByTestId("service-area-checkbox")
  const checkBoxActual = serviceAreaCheckbox.querySelector(
    "input[type=checkbox]"
  )

  /* PRECONDITIONS */
  expect(queryByTestId(LOOKUP_WRAPPER_TESTID)).not.toBeInTheDocument()
  expect(checkBoxActual.checked).toEqual(false)

  /* ACTION */
  fireEvent.click(checkBoxActual)

  /* VERIFY */
  expect(checkBoxActual.checked).toEqual(true)
  expect(queryByTestId(LOOKUP_WRAPPER_TESTID)).toBeInTheDocument()
})

The two line where things get really interesting are:

const serviceAreaCheckbox = getByTestId("service-area-checkbox")
const checkBoxActual = serviceAreaCheckbox.querySelector(
  "input[type=checkbox]"
)

We can get the Checkbox component with the first line, but this only gets us the label wrapper and not the actual checkbox, so the second line pulls up the actual checkbox from within it.

The rendered code from the Checkbox component looks like this:

<label
 
  data-testid="service-area-checkbox"
>
  <input
   
    type="checkbox"
  />

  service_location.label
</label>

The funky looking classes are the result of style-components . What’s interesting to note is that the data-testid was placed on the label node. Using querySelector let’s me pull out the actual checkbox, which is an HTML element of type “checkbox”.

From there, verifying the preconditions and postconditions becomes fairly easy:

queryByTestId

Making the change happens with the fireEvent.click . ( Note: the change event doesn’t work for checkboxes. See: https://github.com/testing-library/react-testing-library/pull/178/files#diff-04c6e90faac2675aa89e2176d2eec7d8R631 ) (and beware the drift of time as well…)

Verifying a checkbox is en-/disabled based on another value

This case shows something a little different, and shows how to approach changing props as related to component testing.

The code under test in this case is:

const SmsNotifications = ({ name, value, onChange, isMobileSet }) => {
  return (
    <FormField name={name} label={null}>
      <div>
        <strong>{I18n.t("notify_via_sms.title", { scope: i18nScope })}</strong>
        <Checkbox
          name={name}
          value={value || ""}
          onChange={onChange}
          disabled={isMobileSet ? null : "disabled"}
          wrap={"wrap"}
          data-testid="notify-via-sms-checkbox"
        >
          {" "}
          {I18n.t("notify_via_sms.label", { scope: i18nScope })}
        </Checkbox>
      </div>
    </FormField>
  )
}

The spec test for this feature is:

it("enables the notify via sms checkbox when the mobile field has a value", () => {
  const NOTIFY_CHECKBOX_TESTID = "notify-via-sms-checkbox"

  const props = {
    fields: {},
    handleChange: jest.fn(),
    onDuplicateCustomerName: jest.fn(),
    onSelectParent: jest.fn()
  }
  const { queryByTestId, getByTestId, rerender } = render(
    <Providers>
      <CustomerInfoFormSection {...props} />
    </Providers>
  )
  const notifyCheckBoxComponent = getByTestId(NOTIFY_CHECKBOX_TESTID)
  const checkBoxActual = notifyCheckBoxComponent.querySelector(
    "input[type=checkbox]"
  )
  expect(checkBoxActual.disabled).toEqual(true)

  rerender(
    <Providers>
      <CustomerInfoFormSection {...props} fields={{ mobile: "555121" }} />
    </Providers>
  )

  expect(checkBoxActual.disabled).toEqual(false)
})

I’m doing the same trick as before about grabbing the actual checkbox by using querySelect on the element returned with getByTestId("NOTIFY_CHECKBOX_TESTID") . This provides access to the disabled property.

This test uses the testing library’s rerender method, which is used to apply different props. In this case the props for the compoonent are controlled above this component, so this is actually a valid test matching how it would work in the user’s environment.

For the rerender, I’m passing in a value for the mobile phone number, which is used to determine whether the checkbox is en-/disabled.


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

软件开发者路线图

软件开发者路线图

Dave H. Hoover、Adewale Oshineye / 王江平 / 机械工业出版社 / 2010年9月 / 35.00元

作为一名软件开发者,你在奋力推进自己的职业生涯吗?面对今天日新月异和不断拓展的技术,取得成功需要的不仅仅是技术专长。为了增强专业性,你还需要一些软技能以及高效的学习技能。本书的全部内容都是关于如何修炼这些技能的。两位作者Dave Hoover和Adewale Oshineye给出了数十种行为模式,来帮你提高主要的技能。 本书中的模式凝结了多年的调查研究、无数次的访谈以及来自O’Reilly在......一起来看看 《软件开发者路线图》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

在线进制转换器
在线进制转换器

各进制数互转换器