A Beginner’s Guide to Component Testing with Playwright
By testing components in isolation, modern web UIs can be tested with a scalable approach.
With the growing complexity of web applications, performing end-to-end testing on them is equally complex. The traditional strategy of testing the application UI can be flaky and may require regular maintenance. A paradigm shift towards component testing in such an environment is useful.
Teams are adopting component-driven architectures like React, and using component testing strategy can gain overall testing confidence and quality. Playwright comes with the perfect capability to make component testing a perfect fit in your test tech stack. In this article, we will explore how we can write component tests using Playwright.
What is Component Testing?
Component testing can be attributed to be the testing microcosm, which focuses on verifying individual building blocks. The end-to-end test can be flaky, but these component tests are focused, much more reliable, and fast. When components are developed keeping in mind the testability, it yields less tightly coupled code. Additionally, testing early also prevents regression issues from bumping in later in the development cycle. Playwright offers component testing with vital advantages discussed in the section below.
Using Playwright for Component Testing
Playwright provides true isolation for the tests. It comes with a mounting and mocking model which is reliable and allows to unit test UI components without the need of an actual browser or server. This can be combined with the Playwright’s WebDriver capabilities for the end-to-end tests, which provides a complete spectrum coverage. This offering can hugely increase productivity and efficiency.
With Playwright you can easily target React components to test modular UIs. Playwright also ensures that the test reliability and speed are taken care of, while the developers can focus on building rather than debugging. This helps in creating the testing strategy unified across different levels achievable. Playwright component testing makes sure that irrespective of the complexity of the front end, quality is delivered.
Component Testing with Playwright
We will now start our journey to write our first component test using Playwright, but before we begin we will first create a React App. This React App will be used for running the component tests against its components.
React App Creation
Open Visual Studio Code.
Open the terminal and navigate to a folder where you would want to keep the react Keep.
Now, enter the command:
npx create-react-app appName
Once done, you will see that an app is created with the name that you gave.
4. Next, you can open the project you just created in Visual Studio and look at its project structure:
The different folders within our project consist of the logo, images, main HTML page, app information, styles for the application components, the main component of the app, vector images, performance vitals, index js file, etc. These files help in creating a structure for our project. Note that we will be using this default project for our test execution. In real, your applications may have other files and components as well.
React App Launch
Now that we have created the React App we will now launch it. We will use the start command to start the application. Navigate to the app location using the terminal and enter the below command:
npm start
Once done, you will notice that your React App is up and running.
Additionally, you will notice that a browser window opens up showing a page like below:
Installing Playwright Components
After starting the React App, the next step is to start writing the tests. Before we do so we will have to install the playwright components module. To do so, run the below command in the terminal:
npm init playwright@latest -- --ct
Upon execution, you will see some questions to which you can select the answer that suits your requirements. We are using Typescript & React 18 in this article.
Once the Playwright Component is installed, you will notice that some folders and files are also created, which can be seen from the updated project structure.
The new files created are the index files, .html & .tsx. One will be used to render components while testing alongside mounting the components. The other can be used to apply styles, and themes as well as inject code to the page where components will be mounted.
Additionally, there is a playwright-ct configuration file, which can be used to configure the browsers, timeouts, reports, etc.
After launching the React App and setting up the test framework, the next step id to write the components tests.
Writing and Executing the Component Test
We will first create a tests folder under the src folder, and in it, we will create our first component test file, componentTest.spec.tsx.
Unlike the regular end-to-end tests where we used .ts as the extension, we use .tsx here because the React components use the JSX syntax.
Write the below code in the .tsx file we just created:
//Imports the test and expect functions from the Playwright ct-react module
import { test, expect } from '@playwright/experimental-ct-react';
//Imports the App component to test from the relative ../App path
import App from '../App';
//Configures the viewport to a 500x500 size
test.use({ viewport: { width: 500, height: 500 } });
//Starts a test case named "should work" which will run asynchronously,
//mount function binding is destructured from test parameter
test('should work', async ({ mount }) => {
//Uses mount() to instantiate the <App /> component in isolation
const component = await mount(<App></App>);
//Asserts that component contains expected "Learn React" text on it verifying basic render.
await expect(component).toContainText('Learn React');
});
Before you execute this, you will have to create a tsconfig.json file inside your project directory. In this file, you will have to enable the Javascript extension using the compilerOptions. Use the code below in the tsconfig.json file:
{
"compilerOptions": {
"jsx": "react-jsx",
"target": "ESNext",
"module": "commonjs",
"moduleResolution": "Node",
"sourceMap": true,
"strict": true,
"noImplicitAny": false
}
}
In this file we are trying to configure the Typescript compiler to output CommonJS modules, enable React JSX, use the node module resolution strategy and enable strict type checking for the project. Note that you can alter this file as per your requirements.
The next step is to execute the component test and to do so, run the below command in the terminal:
npm run test-ct
You will see that the component test was executed successfully with logs as below:
Additionally, you can view the HTML report for your test:
Now let us add some components to our app. We will be adding a counter to the React App and to do so, create a file Counter.js under the src folder and add the below code to it:
// Import the useState hook from React
import { useState } from 'react';
// Export a default function component called Counter
export default function Counter() {
// Use the useState hook to create count state variable and
// setCount function to update it
const [count, setCount] = useState(0);
return (
<div>
<button onClick={() => setCount(count + 1)}>
Increment
</button>
<span>{count}</span>
</div>
);
}
Now, we will have to include this Counter component to our React App. To do so, open the App.js and update the code as below:
import logo from './logo.svg';
import './App.css';
import Counter from './Counter';
function App() {
return (
<div className="App">
<Counter/>
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>
Edit <code>src/App.js</code> and save to reload.
</p>
<a
className="App-link"
href="https://reactjs.org"
target="_blank"
rel="noopener noreferrer"
>
Learn React
</a>
</header>
</div>
);
}
export default App;
Now, open the React App by running below command in terminal:
npm start
This will launch the browser, and now you will see an Increment button on top of the webpage:
Now, we will update our test to validate this new component. Update the componentTest.spec.tsx as below:
//Imports the test and expect functions from the Playwright ct-react module
import { test, expect } from '@playwright/experimental-ct-react';
//Imports the App component to test from the relative ../App path
import App from '../App';
//Configures the viewport to a 500x500 size
test.use({ viewport: { width: 500, height: 500 } });//Starts a test case named "should work" which will run asynchronously,
//mount function binding is destructured from test parameter
test('should work', async ({ mount }) => {
//Uses mount() to instantiate the <App /> component in isolation
const component = await mount(<App></App>);
//Asserts that component contains expected "Learn React" text on it verifying basic render.
await expect(component).toContainText('Learn React');
//Searches the Increment button
await component.getByRole('button', { name: 'Increment' }).click();
//Asserts that the increment value is visible
expect(component.getByText('1')).toBeVisible();
});
Execute the test by running command npm run test-ct and see the execution results:
And there you go, you test passed with this newly added component. Let us see how we can execute a test for this component in isolation in the text section.
Executing Component Tests in Isolation
To execute this newly added component in isolation, go to your componentTest.spec.tsx and replace the import of the app with the Counter component. Additionally, we will replace the <App> component with <Counter> as shown in the code below:
//Imports the test and expect functions from the Playwright ct-react module
import { test, expect } from '@playwright/experimental-ct-react';
//Imports the Counter component to test from the relative ../Counter path
import Counter from '../Counter';
//Configures the viewport to a 500x500 size
test.use({ viewport: { width: 500, height: 500 } });
//Starts a test case named "should work" which will run asynchronously,
//mount function binding is destructured from test parameter
test('should work', async ({ mount }) => {
//Uses mount() to instantiate the <Counter /> component in isolation
const component = await mount(<Counter></Counter>);
//Asserts that the button is visible
await expect(component.getByRole('button')).toBeVisible();
//Searches the Increment button
await component.getByRole('button', { name: 'Increment' }).click();
});
Let us now execute our test and see the results:
And there you go. You have executed a component-specific test in isolation using Playwright. You can integrate other Playwright capabilities of Visual testing to capture screenshots.
Conclusion
In conclusion, Playwright simplifies component testing by offering a robust, reliable framework for testing UI elements in isolation. Its cross-browser support, easy setup, and detailed error reporting make it ideal for beginners, enabling faster feedback loops and higher-quality code. Start small, explore, and optimize your component tests effectively.
Source: This article was originally published at testgrid.io/blog/playwright-component-testing.