garo
garo

Reputation: 171

How to test styled components conditionally and children elements?

I'm new to unit testing and I've spent some 20-30 hours in docs, articles and YT videos but still can't make sense of how to achieve this. Basically I want to test 3 things here:

  1. Make sure that this component renders 3 components
  2. Test the conditional styling
  3. Test the click event

So far, for the 1st thing, if I try to do:

import { shallow } from "enzyme";
import React from "react";
import ButtonsComponent, { SchedButton } from "./ButtonsComponent";

it("renders three <MyButton /> components", () => {
  const wrapper = shallow(<ButtonsComponent />);
  expect(wrapper.find(MyButton)).to.have.lengthOf(3);
});

I get the following error: TypeError: Enzyme::Selector expects a string, object, or Component Constructor

And I don't have any good idea how to test the other two things, I've seen some examples but I don't understand how to adapt them to my particular situation since they don't seem to be testing exactly what I'm trying to do.

This is a simplifed version of my component:

import React from "react";
import styled from "styled-components";

const MyButton = styled.button`
  background: ${props =>
    props.color ? theme.palette.secondary.dark : "#e6e6e6"};
  color: ${props => (props.color ? "white" : "#737373")};
`;

const ButtonsComponent = ({ currentState, updateState }) => {
  const handleClick = event => {
    updateState(event.target.value);
  };

  return (
    <div>
      <MyButton
        value="Button 1"
        onClick={handleClick}
        color={currentState === "Button 1" ? "#1fbd45" : ""}
      >
        Button 1
      </MyButton>
      <MyButton
        value="Button 2"
        onClick={handleClick}
        color={currentState === "Button 2" ? "#1fbd45" : ""}
      >
        Button 2
      </MyButton>

      <MyButton
        value="Button 3"
        onClick={handleClick}
        color={!currentState || currentState === "Button 3" ? "#1fbd45" : ""}
      >
        Button 3
      </MyButton>
    </div>
  );
};

export default ButtonsComponent;

Any help is greatly appreciated, an ELI5 explanation would be so much more!

Upvotes: 3

Views: 4483

Answers (1)

DiegoTArg
DiegoTArg

Reputation: 487

I will try to answer your questions as detailed as possible.

So you have 3 questions:

1. Make sure that this component renders 3 components

Your test was almost correct. The error was that you were doing: wrapper.find(MyButton) instead of wrapper.find('MyButton'). To do wrapper.find(MyButton) you need to import the MyButton component first, so something like this should work:

import { shallow } from "enzyme";
import React from "react";
import ButtonsComponent, { MyButton } from "./ButtonsComponent";

it("renders three <MyButton /> components", () => {
  const wrapper = shallow(<ButtonsComponent />);
  expect(wrapper.find(MyButton)).to.have.lengthOf(3);
});

But you can't do this in your code because you are not exporting the MyButton component. My advise regarding this question would be that you create a file for MyButton component and another file for ButtonsComponent component. In general it is much more clear to have one component per file, and unit testing becomes clearer as well. Then you will be able to do something like this:

import { shallow } from "enzyme";
import React from "react";
import ButtonsComponent from "./ButtonsComponent";
import MyButton from "./MyButton"

it("renders three <MyButton /> components", () => {
  const wrapper = shallow(<ButtonsComponent />);
  expect(wrapper.find(MyButton)).to.have.lengthOf(3);
});

2. Test the conditional styling

I do not know what testing engine (mocha, jest, etc) you are using, but if I had to choose one to test a React app I would choose Jest (this is just a matter of taste though). I suggest jest because it have everything you would need, like spies, matchers, etc, and there are also some additional packages that will make your life easier, like in this case.

So to test your styled-components with jest you will need two extra packages react-test-renderer (which basically converts a React components into a js object) and jest-styled-components (which will give you an extra matcher named toHaveStyleRule that will help you a lot with your goal).

With those 2 packages installed what you will do is, first import them:

import renderer from "react-test-renderer";
import "jest-styled-components";

Then you will convert your component to a js object:

const tree =
  renderer
  .create(<MyButton color="red">Test</MyButton>)
  .toJSON();

And finally you will ask for the style you are expecting to have applied with the properties you specified for your component:

expect(tree).toHaveStyleRule("background-color", "red");

3. Test the click event

To test the click event you will need a spy. If you are using jest you already have this feature, if you are using mocha I think you will probably need an extra package like sinon or similar.

The spy will let you track what happen to your updateState function, so when you are testing the click event you will pass a spy function as value of updateState prop. In this way when you simulate the click on your button your spy would be invoked and then you will be able to ask question to it like "have you been called?", as a result you will be able to check that on click event you invoke the updateState function with the expected params.

In this link you will find your example (with a few small modifications) working and with tests for all the things you wanted to check (do not pay attention to the place where the dependencies appear in package.json file, many of them should appear as dev dependencies but for some reason if I do that it does not work, it is probably something I am doing wrong in that tool).

I hope this answer helps you.

Upvotes: 2

Related Questions