DEV Community

marques woodson for Pluralsight

Posted on • Updated on

React Specific Selectors in TestCafe

The TestCafe Selector function allows you to use CSS selectors to find the elements that you need to test in the DOM. Using CSS selectors is extremely convenient, and many other testing libraries use the same approach. When using TestCafe to test a React application, using plain CSS selectors will definitely get the job done, is normally the route I take. But it's not the only option.
TestCafe has React-specific selectors that allow you to traverse a component hierarchy to select components by name, by property value, and even the component's state.

Let's say we have the above component tree. We could use React selectors to get the ProductList component like this:

The ReactSelector function takes in a component name and should start with the root component. The comparable CSS selector for the ProductList variable we have above would be:

Selector('.app > .productList');

ProductList would need to be an immediate child of the App component. This can get annoying since in a more complex React app, there could be many levels of nested components to find. Luckily, TestCafe thought of that:

The findReact function allows you to search for a react component nested inside of another component. It's very similar to the find method on the Selector object.

Selector('#component').find('h1');

withProps({ … })

Selecting components by name is quick and easy. But in a real application having something more exact would be better. You could either use more selectivity in a CSS selector, or you can use ReactSelector's withProps function to find a component with specific props.

withProps will work even if your property value is an object, with matching either being partial or exact.

The more you know about your React components, the easier it'll be to find them in the DOM. You could bypass all CSS selectors if you wanted to.

You would need to be careful with this approach, however. If you are doing exact object matching in your selector, but then decide to add a new prop to the object in your component, you would need to update the end to end test as well. I personally don't like to tie my testing code to my application code as much, since it just makes for something else I need to think about while working on a feature. I'd just recommend being careful not to overdo the exact match, especially on complex objects, to save yourself some headaches.

getReact()

There are some test scenarios where you would like to test that a property was updated correctly, or check the value of state. Ideally, you would use something like react-testing-library to test these types of interactions. If you would rather check the internals in your end to end test though, the getReact function is what you'll need. The getReact function returns a client function that resolves with an object of component properties and state.


With this info you can run expectations against state values or component properties:

I would still recommend a Jest test to make these checks, but sometimes that's not always the easiest solution, especially if you have a lot of complex setup involved. You could use getState in a TestCafe test and run it in chrome headless mode. That may run fast enough to make it feel like a Jest test, depending on your app. Try it out :)

In Conclusion

TestCafe is a wonderful tool for creating end-to-end tests and is really transforming how developers approach testing. There are smart test actions and assertions with automatic waiting for elements to appear, no need for webdriver, and full TypeScript support. Along with React specific selectors, there are other selector libraries for Vue, AngularJS, Angular, and Aurelia. I think there are a lot of benefits to using React Selectors in a React app, and you should give them a try while writing your next test!

The more you know about your React components, the easier it'll be to find them in the DOM. You could bypass all CSS selectors if you wanted to.
You would need to be careful with this approach, however. If you are doing exact object matching in your selector, but then decide to add a new prop to the object in your component, you would need to update the end to end test as well. I personally don't like to tie my testing code to my application code as much, since it just makes for something else I need to think about while working on a feature. I'd just recommend being careful not to overdo the exact match, especially on complex objects.

Follow me on twitter 😀

Top comments (8)

Collapse
 
tallku profile image
Tall-KU

This is really beneficial! Coming from a beginner in React though, I am finding it difficult to really utilize the React plugin for TestCafe.

Maybe the website I'm having to test isn't really following React principals, but how do you print properties, state, keys, etc. from React? I want to print stuff to the console, but get this: ReExecutablePromise { _then: [], _fn: [Function], _taskPromise: null }

Collapse
 
mwoodson profile image
marques woodson

Where are you doing the console.log? In the React component, or in your test?

Collapse
 
tallku profile image
Tall-KU

no, it is within the TestCafe fixture/test



test.only('testing', async t => {
    var doubleArrowLeft = ReactSelector('Button').withProps({Key : 'fal fa-chevron-double-left'});
    console.log(doubleArrowLeft.getReact())
    await t.expect(doubleArrowLeft).eql(true);
})
Thread Thread
 
mwoodson profile image
marques woodson

doubleArrowLeft.getReact() returns a promise. So try this:

var doubleArrowLeft = ReactSelector('Button').withProps(
  { Key : 'fal fa-chevron-double-left' }
);
const doubleArrowLeftData = await doubleArrowLeft.getReact();
console.log(doubleArrowLeftData)
Thread Thread
 
tallku profile image
Tall-KU

That didn't work....

Thread Thread
 
mwoodson profile image
marques woodson

Here's what I tested really quick:

fixture("A fixture this is").page("http://localhost:3002");

test("this is a test", async t => {
  const header = ReactSelector("Header").withProps({ name: "marques" });
  const stuff = await header.getReact();
  console.log(stuff);
});

outputs:

$ testcafe chrome:headless test.ts
 Running tests in:
 - HeadlessChrome 74.0.3729 / Mac OS X 10.14.5

 A fixture this is
{ state: {}, props: { name: 'marques' }, key: null }
 ✓ this is a test

Try this out

Thread Thread
 
tallku profile image
Tall-KU

Awesome, thanks for the help. So, I figured out my issue. I was trying to locate the selector by 'Key', which is NOT a property!

Thread Thread
 
mwoodson profile image
marques woodson

Aaah yea, you'd want to use withKey instead of withProps. I'm glad you got it figured out :)