10 Types of software testing you can do with Playwright
Playwright isn’t just for UI testing , it’s a full-stack testing powerhouse. Discover 10 types of software testing you can perform with Playwright, from API and accessibility testing to performance, visual, and security checks.
Testing sucks when it's slow.
You write a test. It opens a browser. Clicks around for 30 seconds. Then tells you something broke. But what broke? The UI? The API? A network timeout? Who knows.
Here's the thing: Playwright isn't just for clicking buttons. It's a Swiss Army knife for testing everything in your web app. API calls. Performance metrics. Accessibility compliance. Visual consistency. All of it.
And when you connect Playwright to TestDino? You get instant visibility into what's actually failing and why. No more digging through CI logs at 2 AM.
Let's explore 10 testing approaches that'll make your test suite faster, smarter, and actually useful.
1. API Testing: create and verify a user
API tests run 10-50x faster than UI tests. That’s not a typo. Skip the browser entirely.
Hit your endpoints directly:
import { test, expect } from '@playwright/test';
test('Create and verify user', async ({ request }) => {
// Create user via API. This takes milliseconds.
const response = await request.post('/api/users', {
data: { name: 'Sarah', role: 'admin' },
});
expect(response.status()).toBe(201);
const user = await response.json();
// Verify the response structure
expect(user.id).toBeDefined();
expect(user.role).toBe('admin');
});
Smart teams test business logic at the API level. UI tests? Save those for actual UI behavior. API testing is a software testing method and technique that focuses on validating business logic and endpoints.
Your API test results flow straight into TestDino’s dashboard. See response times, failure patterns, and which endpoints break most often. The AI automatically groups similar API failures.
2. Performance Testing: User-Perceived Speed
Your server responds in 50ms. Cool. Evaluating how the system performs from the user's perspective is essential to ensure responsiveness and satisfaction. But users wait 5 seconds for the page to load. Not cool.
Playwright measures what users actually experience:
import { test, expect } from '@playwright/test';
test('Measure real loading performance', async ({ page }) => {
await page.goto('https://yourapp.com');
// Get Largest Contentful Paint (LCP) from the browser
const lcp = await page.evaluate(() => {
return new Promise((resolve) => {
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
resolve(lastEntry.startTime);
observer.disconnect();
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });
});
});
// Google recommendation for LCP is under 2.5s
expect(lcp).toBeLessThan(2500);
});
This captures Core Web Vitals, the metrics Google uses for search rankings. Real browser. Real rendering. Real performance data. Performance testing like this helps you analyze software behavior under real-world conditions, ensuring your application meets user expectations.
TestDino tracks performance trends over time. Watch your LCP creep up? You’ll know before users complain.
3. Accessibility Testing: Don't Lock People Out
Approximately 15% of the world’s population has some disability. Your app should work for everyone. It is essential to test the user interface for accessibility to ensure all users can interact with your application effectively.
Axe-core + Playwright = automated accessibility scanning:
import { test, expect } from '@playwright/test';
import AxeBuilder from '@axe-core/playwright';
test('Check accessibility compliance', async ({ page }) => {
await page.goto('/signup');
const results = await new AxeBuilder({ page })
.withTags(['wcag2a', 'wcag2aa'])
.analyze();
expect(results.violations).toEqual([]);
});
This catches missing alt text, poor color contrast, and broken screen reader navigation. Automatically. Usability testing is also important to ensure the application is user-friendly and accessible for all users.
Failed accessibility tests show up with detailed explanations. “Button needs 4.5:1 contrast ratio, currently 3.2:1.” No cryptic error messages.
4. Visual Regression: Catch the Tiny Breaks
Someone changed the CSS. Now, your checkout button is invisible on a mobile device. Visual regression testing helps ensure existing functionality is not broken by UI changes. Functional tests pass. Users can’t buy anything.
Visual regression testing catches this:
import { test, expect } from '@playwright/test';
test('Checkout page looks right', async ({ page }) => {
await page.goto('/checkout');
// Compare against baseline screenshot
await expect(page).toHaveScreenshot('checkout-page.png', {
maxDiffPixels: 100,
});
});
First run creates the baseline. Future runs compare pixel-by-pixel. A regression test like this verifies that existing functionalities remain unaffected after updates, and running multiple regression tests as part of your test suite helps catch issues that could impact existing functionality.
Visual diffs appear side-by-side in TestDino. Expected vs. actual vs. diff overlay. Approve new baselines with one click. Version control tracks who approved what.
5. Load Testing: Real Browser Under Pressure
Traditional load testing tools send HTTP requests. But they miss JavaScript execution, rendering bottlenecks, and memory leaks.
In addition to load testing, related types of performance testing, such as stress testing, volume testing, and endurance testing, are used to evaluate the system's behavior under various conditions, focusing on stability, capacity, and long-term reliability.
Combine Artillery + Playwright for browser-based load testing:
config:
target: "https://yourapp.com"
phases:
- duration: 60 # seconds
arrivalRate: 20 # new virtual users per second
plugins:
playwright: {} # enable the Playwright plugin
scenarios:
- name: "Shopping flow under load"
engine: playwright
testFunction: "userShopping"
# In some versions this is instead:
# flow:
# - function: "userShopping"
import { chromium, Browser, Page } from 'playwright';
export async function userShopping() {
const browser: Browser = await chromium.launch();
const page: Page = await browser.newPage();
await page.goto('https://yourapp.com/shop');
// Measure performance while the system is under load
const timing = await page.evaluate(() =>
performance.timing.loadEventEnd - performance.timing.navigationStart
);
console.log(`Page loaded in ${timing}ms under load`);
await browser.close();
}
This shows how real users experience your app when 1000 others are using it simultaneously. During these tests, you are evaluating the system's behavior under load to identify performance issues and bottlenecks.
Compare performance metrics between normal and load conditions. See exactly when and where performance degrades.
6. Network Testing: When the Internet Breaks
Users don’t have fiber connections. They have spotty 3G on the subway.
Test your app on a poor network connection: This process involves testing how your application performs under various network scenarios, such as slow connections, offline mode, or connection drops.
import { test, expect } from '@playwright/test';
test('Works on slow connection', async ({ page, context }) => {
// Simulate slow 3G by delaying all requests a bit
await context.route('**/*', async (route) => {
await new Promise((r) => setTimeout(r, 200));
await route.continue();
});
await page.goto('/');
await page.click('[data-testid="load-more"]');
// Should show loading state
await expect(page.locator('.loading-indicator')).toBeVisible();
// Should eventually load
await expect(page.locator('.content')).toBeVisible({ timeout: 10_000 });
});
Test offline mode. Test connection drops. Test what happens when APIs timeout.
Network-related failures are tagged automatically. “Failed due to timeout” vs “Failed due to 500 error.” Different problems need different solutions.
7. Component Testing: Fast and Focused
Testing entire user flows is slow. Testing individual components? Lightning fast. Component testing is similar to unit testing, where unit tests verify individual code components in isolation to ensure they function correctly.
import { test, expect } from '@playwright/experimental-ct-react';
import React from 'react';
import { Button } from './Button';
test('Button handles clicks', async ({ mount }) => {
let clicked = false;
const component = await mount(
<Button onClick={()=>{ clicked = true; }}>
Click me
</Button>
);
await component.click();
expect(clicked).toBe(true);
});
Real browser rendering. Isolated component. No full app startup.
8. Device Testing: Beyond Desktop
Your app works great on your MacBook. How about a 5-year-old Android phone? Compatibility testing helps ensure your application works seamlessly across different devices and operating systems, identifying issues that may arise due to variations in hardware, OS versions, and environments.
import { test, expect, devices } from '@playwright/test';
test('Mobile checkout flow', async ({ browser }) => {
const context = await browser.newContext({
...devices['iPhone 12'],
geolocation: { latitude: 40.7128, longitude: -74.0060 },
permissions: ['geolocation'],
});
const page = await context.newPage();
await page.goto('/checkout');
// Test mobile-specific features
await page.tap('[data-testid="address-lookup"]');
await expect(page.locator('.location-result')).toContainText('New York');
await context.close();
});
Test touch interactions, device capabilities, and screen sizes, all without physical devices.
Device-specific failures get flagged. “Fails on iOS Safari but passes on Chrome Android.” Now you know where to look.
9. Configuration Testing: Every Combination
Different locales. Timezones. Permissions. Dark mode. Your app needs to handle all of them.
Localization testing and globalization testing are crucial for verifying that your application adapts correctly to various languages, regions, and cultural contexts.
import { test, expect } from '@playwright/test';
test('German locale with dark mode', async ({ browser }) => {
const context = await browser.newContext({
locale: 'de-DE',
colorScheme: 'dark',
timezoneId: 'Europe/Berlin',
});
const page = await context.newPage();
await page.goto('/settings');
// Verify German formatting
await expect(page.locator('.price')).toContainText('99,99 €');
// Verify dark mode
await expect(page.locator('body')).toHaveCSS(
'background-color',
'rgb(18, 18, 18)'
);
await context.close();
});
Test every user configuration without manual setup.
Configuration failures show patterns. “Always fails in RTL languages.” “Breaks in UTC+9 timezone.” TestDino spots these patterns automatically.
10. Network Mocking: Control Everything
External APIs go down. Third-party services return garbage. Your tests shouldn’t care.
import { test, expect } from '@playwright/test';
test('Handles payment API errors gracefully', async ({ page }) => {
// Mock payment service failure
await page.route('**/api/payment', async (route) => {
await route.fulfill({
status: 503,
contentType: 'application/json',
body: JSON.stringify({ error: 'Service unavailable' }),
});
});
await page.goto('/checkout');
await page.click('[data-testid="pay-now"]');
// Should show user friendly error
await expect(page.locator('.error-message')).toContainText(
'Payment temporarily unavailable. Please try again.'
);
});
Test error states. Test edge cases. Test the unhappy paths that users will definitely find. Recovery testing is essential here: intentionally induce failures to evaluate your application's ability to recover from crashes or unexpected issues.
Best Practices for Software Testing
Great software testing doesn't happen by accident.
It's the result of smart planning, the right tools, and a commitment to quality throughout your entire development process. No magic. Just discipline and the right approach.
1. Start with a solid test plan.
Know what you're testing, how you'll test it, and what success looks like. Your test cases should cover both the happy path (when everything works as expected) and the edge cases (when everything fails). Write them down. Be specific. "User can log in" isn't a test case. "User with valid credentials sees dashboard within 3 seconds" is.
2. Mix automated and manual testing.
Automation handles the repetitive stuff, running the same regression tests 100 times without complaining. However, humans catch the unusual automation misses. That button that technically works but looks terrible? A human will spot it.
3. Test early. Test often.
Waiting until the end to test is like checking your parachute after you jump. Bad idea. Build testing into every stage of the development process. Write code, test it, ship it, and repeat.
Your testing team isn't the cleanup crew; they're your co-pilots. Bring them in during planning. They'll spot problems in your requirements before you write a single line of code.
Give them the right tools: test automation frameworks, defect tracking systems, and (hint) TestDino for making sense of all those test results.
Build this foundation, and you'll ship better software. Skip it, and you'll ship bugs.
Level Up Your Testing
Start with API tests. Seriously. They're 10x faster than UI tests and catch most of your business logic bugs.
Next? Add visual regression testing. You know those bugs where everything technically works but looks completely broken? Visual tests catch those before your CEO sees them in production.
Don't forget accessibility. It's not just the right thing to do; it's often legally required. Plus, accessible apps are better for everyone.
Your testing strategy needs layers:
- Define clear test cases.
- Write maintainable test scripts.
- Run smoke tests on every deploy to catch the obvious breaks.
- Use acceptance testing to verify that features actually solve user problems.
- Mix in some beta testing with real users, and they'll find bugs your team never imagined.
Alpha testing catches the embarrassing stuff internally. User acceptance testing confirms you built what users actually wanted.
Sanity testing ensures your quick fixes haven’t broken something else. And yes, sometimes you need good old ad hoc testing, just poking around to see what breaks.
Want maximum test coverage? You need all these pieces working together.
- Your testing process should flow smoothly from development through deployment.
- Your development team and testing team should communicate daily, rather than throwing code over the wall.
TestDino ties it all together. One dashboard. All your test types. AI that spots patterns humans miss. Because raw test results are just noise, you need insights that tell you exactly what to fix and where.
Connecting Everything with TestDino
Once your Playwright tests are running locally or in CI, the final step is to connect them to TestDino so your team can access centralized runs, insights, history, and trends. The process takes only a few minutes and works with any Playwright project.
1. Configure Playwright to Produce Reports
TestDino requires a JSON report. Adding the HTML reporter is optional but recommended because it allows richer debugging data, such as screenshots, traces, and videos, to appear in the TestDino dashboard.
// Add this in playwright.config.js or playwright.config.ts
reporter: [
// Mandatory reporter for JSON results
['json', { outputFile: './playwright-report/report.json' }],
// Optional, enables native HTML upload
['html', { outputDir: './playwright-report' }],
]
Keep the output folder consistent. The uploader reads the directory that contains report.json.
2. Setting Up CI Integration
If you want TestDino to automatically receive test runs on each commit, add the upload step to your CI job. Here is the GitHub Actions example.
- name: Upload results to TestDino
run: npx tdpw upload ./playwright-report --token="${{ secrets.TESTDINO_TOKEN }}" --upload-html
Place this step right after the test execution step. Once added, every CI run is pushed to TestDino automatically.
3. Send Your First Test Run
Run your Playwright tests:
npx playwright test
Then upload the report folder to TestDino:
npx tdpw upload ./playwright-report --token="<API_KEY>" --upload-html
If you are not using the HTML reporter, remove the HTML flag:
npx tdpw upload ./playwright-report --token="<API_KEY>"
After upload, your run immediately appears in TestDino with all test results and artifacts
4. Verify That Everything Is Working
Open your TestDino project and visit the Test Runs section. You should see:
- A new test run with pass and fail counts
- Timing and duration metrics
- Screenshots, videos, traces, and HTML reports if uploaded
- Failure insights and classification powered by TestDino
Your Playwright setup is now fully connected to TestDino, and your test reporting becomes organized, searchable, and shareable across your team.
Conclusion
UI testing alone leaves blind spots. These 10 testing approaches with Playwright cover your entire app.
Backend to frontend.
Performance to accessibility.
Desktop to mobile.
Testing the whole system and software ensures comprehensive coverage across all components and integrations. It's also crucial to test software in real-world environments and verify the installed software system to guarantee reliability and readiness for users.
TestDino turns those test results into insights. Instead of “test failed,” you get “API timeout caused cart test to fail on mobile Safari when network latency exceeds 200ms.”
Stop debugging. Start shipping.
Your tests should make development faster, not slower. With Playwright’s versatility and TestDino’s intelligence, they finally can.
FAQs
You can use Playwright for UI, API, performance, accessibility, visual regression, component, device, configuration, and network‑condition testing.
Use Playwright’s `request` API to hit backend endpoints directly for most business logic, and keep a smaller set of UI flows for critical journeys.
They measure page timings and key metrics in browser tests while capturing baseline screenshots, then compare timings and images on every run in CI.
It reveals environment‑specific issues such as failures only on mobile, in certain locales, or under poor networks before code reaches production.
Configure Playwright to emit JSON/HTML reports from CI and feed them into a reporting platform like TestDino that unifies runs, trends, and failures in one dashboard.
Table of content
Flaky tests killing your velocity?
TestDino auto-detects flakiness, categorizes root causes, tracks patterns over time.