Ideas Engineered for Tomorrow
We Engineer Services & Solutions for Your Business Needs
Consulting Services Hire Book Consulting

Automated Testing Services in India

UI and end-to-end test automation built in Playwright, Cypress and Selenium by an engineering team in India. We fix the flaky suite you already have, cut the pipeline wait, and hand back tests your developers actually believe.

Why Does a Suite Everyone Was Proud Of Stop Being Trusted?

Almost every team that calls us about automated testing services in India has the same story, and it is never the story they open with. They open with "we need more coverage". What has actually happened is that the suite they built two years ago stopped meaning anything.

The sequence is predictable. Someone writes a hundred end-to-end tests during a quiet quarter. They pass. Six months later a redesign changes the class names on the checkout page and forty tests go red on a change that broke nothing. Someone fixes the selectors under time pressure by pinning them to whatever is on screen that day. A few months after that, three tests fail intermittently and nobody can reproduce it locally, so a retry is added. Then two retries. Then a nightly cron replaces the pull request run because forty minutes of waiting was unbearable.

By the time we are called, the suite is red more often than green, and the team has quietly learned to merge anyway. That is the real cost, and it is worse than having no tests at all. A team with no automated tests is careful. A team with a suite they no longer read is careless and thinks it is covered.

The symptoms are easy to list and worth naming, because you probably recognise several. Pull request checks take longer than the code review. A rerun button is part of the merge ritual. Nobody can say what the suite covers without opening the files. The one engineer who understood the framework left. Coverage numbers are quoted in status reports while production incidents keep landing in flows that are supposedly tested.

None of that is fixed by writing more tests. It is fixed by deciding what the suite is for, deleting what does not serve that, and rebuilding the parts that do on foundations that survive a redesign. That is the work this page describes.

What Automated Testing Services Actually Cover Here

This page is about functional test automation at the user interface and end-to-end layer: driving a real browser through the flows your customers use, and asserting that the application still behaves. It is deliberately narrower than "QA". Load and stress work, API contract testing, penetration testing, accessibility conformance and device-lab mobile testing are separate disciplines with different tools, and we treat them that way rather than pretending one framework covers all of it.

Audit before anything is written

If a suite already exists, the first deliverable is an assessment, not code. We run the existing tests twenty to thirty times against unchanged code to get a per-test failure rate, because "it is flaky sometimes" is an opinion and a percentage is a fact. We map what each test actually asserts against the flows that matter commercially, which almost always reveals nine tests covering the login page and none covering the refund path. We time every stage of the pipeline run. And we read the selector strategy, because that single decision predicts how much of the suite is salvageable.

The output is a document with three lists: tests to keep, tests to rewrite, tests to delete. Deleting is usually the largest list and the most contested conversation. We bring evidence to it rather than an opinion.

Framework build or rebuild

When the foundation needs replacing, we build it: the runner configuration, the locator conventions, the test data strategy, fixtures and setup helpers, environment configuration, reporting, and the CI wiring. This is the layer that decides whether test number four hundred takes twenty minutes or two days to write. Getting it wrong is why so many suites stall at fifty tests.

Test authoring against critical flows

Then the tests themselves, written in priority order against a ranked list of user journeys that you sign off. Revenue paths first, then regulated or compliance-sensitive workflows, then the flows that generate the most support tickets, then everything else. The ranking is a business decision. We facilitate it and we push back when the list is really a list of what is easy to automate.

CI integration and the feedback loop

A suite that only runs on someone's laptop is a hobby. We wire it into GitHub Actions, GitLab CI, Azure Pipelines, CircleCI, Buildkite or Jenkins, split it so that a fast pre-merge lane gives an answer in minutes and the full sweep runs on a schedule, and make the failure output good enough to diagnose without reproducing locally. Traces, screenshots at the point of failure, and video for the cases that need it.

Triage as an ongoing job

The part most proposals leave out. Someone has to look at every red run, decide within minutes whether it is a real bug, an environment problem or a flake, and act. Without that, a suite decays back to noise within a quarter no matter how well it was built. On ongoing engagements this is a named responsibility with a written policy, not something people get to when they have time.

Definition of done

We agree it explicitly at the start, because "done" for a test is not "it passed once". Our default: the test passes fifty consecutive runs in CI with zero retries, it uses the agreed locator strategy with no raw CSS or XPath chains, it creates and cleans up its own data, it runs independently of every other test in any order, it has an owner, and its failure message tells you what broke without opening the code. A test that fails any of those is not merged.

Playwright, Cypress or Selenium, and When Each One Is Wrong

This is the first argument on every project and the one most often decided badly, usually by whichever tool someone used last. Here is our honest position on all three, including where each one is the wrong answer.

Playwright

Our default for new work. Microsoft's runner drives Chromium, Firefox and WebKit through a single API, and the design decisions that matter were made correctly. Locators are lazy and re-resolve on every action, so the classic stale element problem largely disappears. Auto-waiting is built into the actionability checks rather than bolted on, so you are not writing explicit waits everywhere. Browser contexts give you a clean, isolated session per test at a fraction of the cost of a fresh browser launch, which is what makes real parallelism cheap.

The trace viewer is the feature that changes team behaviour. When a test fails in CI at 3am, you open the trace and get a timeline with a DOM snapshot at every step, the network log, the console, and the exact locator that failed. Engineers stop saying "it works on my machine" because they no longer have to reproduce anything. That alone justifies the migration for most teams drowning in CI failures they cannot explain.

When Playwright is wrong: when your team has a large, healthy Cypress suite and a working debugging habit around it, rewriting for marginal gain is a bad trade. When you need to test on real physical devices or a broad matrix of legacy browser versions, Playwright's bundled browser builds are not the same thing as the browser your customer runs, and you want a device cloud or Selenium Grid instead. And it is the wrong tool if you are trying to test a native desktop or mobile application, which it does not do.

Cypress

Cypress earned its reputation for a reason. Running inside the browser alongside your application gives it a debugging experience nothing else has matched: the interactive runner with time travel, where you hover a command in the log and see the DOM as it was at that moment, is genuinely excellent, and its automatic assertion retry made an entire category of timing bug go away for teams that had never used explicit waits properly.

The constraints come from the same architectural choice. Because it lives in the browser's run loop, anything that leaves that page is awkward. Multiple tabs are not supported. Cross-origin flows need cy.origin and remain more constrained than the equivalent Playwright test. The command chain is not a promise, which trips up every engineer arriving from async JavaScript in their first week. Parallelisation across machines is straightforward through Cypress Cloud, which is a commercial service, or through self-hosted orchestration if you would rather not pay for it.

When Cypress is wrong: OAuth and single sign-on redirect chains across several domains, anything involving a second tab or a popup window, and applications where you need to intercept and control traffic outside the page context. If your critical flow is "log in through the identity provider, get redirected back, then open the invoice in a new tab", you will spend more time fighting the tool than writing the test.

Selenium and WebDriver

Selenium is the oldest of the three and still the right answer more often than the internet suggests. It is a W3C standard rather than a product, which means the widest browser and language support of anything available, first-class Java, C# and Python bindings, and a grid model that has been running at scale in enterprises for over a decade. If your organisation already has a Selenium Grid, a licensed browser matrix and a team of Java engineers, replacing it with a Node-based runner is a large cost for a benefit you may not need.

The trade-off is that WebDriver gives you almost nothing for free. There is no auto-waiting, so every interaction needs a considered explicit wait, and the difference between a good Selenium suite and a nightmare is entirely down to whether the team disciplined itself about that. Selenium 4 improved matters with relative locators and BiDi support, and the WebDriver BiDi standard is closing the gap on the bidirectional features that made Playwright feel modern. But out of the box, a naive Selenium test is far more likely to be flaky than a naive Playwright test.

When Selenium is wrong: a greenfield JavaScript or TypeScript project with no existing grid investment and no requirement for exotic browsers. You will write more code for less feedback. It is also the wrong choice if the team writing the tests are not primarily engineers, because the discipline it demands around waits and synchronisation is not optional.

What about the record-and-playback and low-code tools?

Codegen tools that watch you click and emit a test are useful for one thing: getting a first draft of a locator chain and a flow shape in front of an engineer. Playwright's codegen and Selenium IDE both do this well. What they produce is a starting point, not a test. The selectors are usually positional, there is no data strategy, and there are no meaningful assertions.

The commercial low-code platforms that promise self-healing locators solve a real pain and introduce a different one. When the tool silently repairs a selector because a button moved, you have lost the signal that the button moved. Sometimes that is exactly what you wanted. Sometimes the button moved because a developer broke the layout. We are not against these tools, but we will ask what happens to your suite when the subscription lapses, and whether your tests are portable out of the vendor's format.

The decision in one paragraph

New project, web only, JavaScript or TypeScript team, no existing investment: Playwright. Existing healthy Cypress suite with a team that likes it: keep it and fix the selectors instead. Large enterprise with an existing grid, Java or C# engineers, and a broad browser matrix: Selenium, done properly with explicit waits. Native mobile applications: not any of these, and see our mobile testing work. We will tell you if the answer is "keep what you have", which is more often than you would expect from anyone selling a rebuild.

The Test Pyramid, and Why Most Teams Have It Upside Down

Mike Cohn described the test pyramid in Succeeding with Agile in 2009: many fast unit tests at the base, fewer service or integration tests in the middle, a thin layer of end-to-end tests at the top. Martin Fowler has written about it since, and the widely quoted Google Testing Blog guideline of roughly 70 percent unit, 20 percent integration and 10 percent end-to-end comes from the same tradition.

Almost nobody has that shape. What we find is the ice cream cone: a fat layer of end-to-end tests, a thin middle, a handful of unit tests, and a scoop of manual regression on top. It happens for an understandable reason. End-to-end tests are the easiest to justify to a stakeholder, because they look like what a user does. Nobody has to explain what they cover.

The cost arrives later. Every end-to-end test is slow, needs an environment, needs data, and can fail for a dozen reasons unrelated to the thing it is checking. When one fails, it tells you something is broken somewhere in a stack of ten components. A unit test tells you which function is wrong. The information density per second of runtime is not close.

Where the middle layer went

Kent C. Dodds' testing trophy argues the middle should be fattest, and for modern component-based front ends we largely agree. A React or Vue component rendered in a test runner with Testing Library, or a Playwright component test, will catch the overwhelming majority of what teams currently use browser end-to-end tests for: does the form validate, does the empty state render, does the disabled button enable when the field is valid. Those run in milliseconds, need no environment, and cannot be flaky in the ways a browser test can.

When we rebuild a suite, a large part of the work is not writing end-to-end tests. It is identifying which of your existing end-to-end tests are answering a question a component test could answer in one hundredth of the time, and moving them down. A typical rebalance takes a suite from three hundred browser tests to sixty, with the coverage question answered better than before.

What genuinely belongs at the top

Keep an end-to-end test when the value is in the integration itself. Checkout across the payment provider. Sign-in across the identity provider. A document upload that touches storage, a queue and a notification. A multi-step wizard where the state carries across pages. Anything where the bug you fear lives in the wiring between systems rather than inside one of them.

The test to be most suspicious of is the one that drives a browser to verify a business rule. If your test clicks through four screens to check that a discount over 30 percent needs approval, that rule should be unit tested in the pricing service and the browser test should only prove the approval banner appears. One is a five millisecond test. The other is ninety seconds and three potential flake points.

Flaky Tests: Root Causes, Quarantine, and Why Retries Hide Bugs

Flakiness is the reason automated testing programmes fail. Not lack of coverage. A test that passes 92 percent of the time is worse than useless, because it teaches the team that red does not mean broken, and that lesson generalises to every other test in the suite.

The root causes, in the order we find them

Waiting on time instead of state. A hardcoded sleep of two seconds is a bet that the machine will never be slower than it was on the day someone wrote it. Under parallel load in CI, that bet loses. The fix is to wait for the condition you actually care about: the network request settling, the element becoming enabled, the spinner disappearing from the DOM.

Animations and transitions. A modal that fades in over 300 milliseconds is clickable at pixel coordinates that are still moving. The click lands on whatever is underneath. This class of flake is invisible locally because a human is slower than the animation and CI is not. Disabling CSS animations in the test environment removes an entire category of failure in one line.

Genuine race conditions in the product. The important one. A button rendered before its event handler is attached. A page that reads a value it just wrote before the write has propagated. A toast notification that appears over the element the next step needs to click. These are user-facing bugs. They fail your test at the rate they fail your customers, which is the whole point.

Shared state between tests. Test A creates a user, test B lists all users and asserts a count. They pass in sequence and fail in parallel, and the failure moves around depending on scheduling. This is the single largest cause of flakes we see once teams turn on parallel execution.

Time, timezone and locale. A test that creates something "due tomorrow" and asserts a formatted date will break at month end, on a leap day, or the first time the runner is in UTC and the developer is in IST. Freeze the clock or generate the expectation from the same source the application uses.

Third-party content. An analytics script, a chat widget, an embedded map, a payment iframe. Each is a network dependency you do not control that can be slow or down. In test environments most of these should be blocked at the network layer, and the one you genuinely need to test should be stubbed except in a small number of integration tests.

Resource contention. Eight parallel browser workers on a two-core CI runner is not a test suite, it is a load test with assertions. Half the "flaky test" reports we investigate resolve to a runner that is out of CPU or memory.

Why we do not fix flakes with retries

Retries are the standard response and they are mostly wrong. Consider what a retry does: a test fails, the runner runs it again, it passes, the build goes green, and nobody looks. If the cause was infrastructure noise, no harm done. If the cause was the third item on that list above, you have just used your test suite to hide a race condition from yourself, and the next report of it will come from a customer.

Our policy is one retry, with every retry recorded and reported as a distinct number. Flake rate becomes a metric on the dashboard next to pass rate. A test that needed a retry is not a passing test, it is a test with an open question against it. Teams that adopt this find real product bugs in the first month, reliably.

The quarantine policy we implement

Zero tolerance for flakes is a policy that survives about three weeks before someone starts skipping tests without telling anyone. What works is a structured quarantine.

Every test gets a rolling failure rate, measured by running the suite against unchanged code on a schedule. Anything over an agreed threshold moves to a quarantine lane. Quarantined tests still run and still report, so you keep the data, but they cannot block a merge. Each one gets a named owner and a date. If it is not fixed by that date, the test is deleted, along with the coverage it was pretending to provide. The quarantine list is reviewed weekly and its size is reported. If the list grows for three weeks running, that is escalated as a delivery problem rather than a testing problem, because it usually is one.

Selectors: The Decision That Decides Whether Your Suite Survives

If you take one thing from this page, take this. The single strongest predictor of whether a browser suite is alive in two years is the locator strategy, and it is chosen in the first week by whoever writes the first test, usually without discussion.

Why CSS and XPath chains kill suites

A locator like div.container > div:nth-child(3) > form > button.btn-primary encodes the shape of the DOM at one moment in time. It is coupled to the styling framework, the layout, and the order of elements, none of which are contracts anyone agreed to. A developer adds a wrapper div for a spacing fix and forty tests fail. Nothing broke. But the team spends a day fixing tests, and the lesson they learn is that tests are expensive to have.

Absolute XPath is worse in the same way and harder to read. Class-based selectors are worse again on any project using utility CSS, because class="px-4 py-2 rounded bg-blue-600" is a description of appearance that will change the next time a designer touches it.

The order we actually use

First choice is a role and accessible name: Playwright's getByRole('button', { name: 'Place order' }), or the Testing Library equivalents. This queries the accessible tree rather than the DOM structure. It is stable across restyling, it reads like the thing a user does, and it has a side effect worth having: if you cannot find an element by its role and name, that element is probably not properly exposed to a screen reader either. Your test suite starts telling you about accessibility defects for free.

Second choice is a dedicated test attribute, data-testid or similar, agreed as a contract with the developers and treated as production code that must not be removed casually. This is the right tool for elements with no meaningful accessible identity, such as a container you need to scope a query to, or a list row that has no unique visible text.

Third is visible text, which is stable enough in practice but breaks under internationalisation and under copy changes from marketing. Fine for internal tools, risky for a localised product.

Last, and only where nothing else exists, a scoped CSS selector, always relative to something stable and never a chain of positional steps. We treat every one of these as technical debt with a comment explaining why it was unavoidable.

What we do when the application has no hooks at all

A great deal of our work is on applications we did not build, sometimes with no access to the source and sometimes with a vendor who will not add attributes. The approach is to build a locator layer that leans entirely on roles, labels and visible text, and to accept that it takes longer to write. It holds up better than people expect, because the words a user sees are among the most stable things in an application. What changes weekly is the markup underneath.

Where we do have access, adding test identifiers to the application is one of the first pull requests we raise, and we keep it small and separate so it is trivial to review.

Test Data and Isolation

Selectors are the most common cause of test maintenance cost. Test data is the most common cause of flakes once selectors are fixed. The two together account for most of what makes automation feel expensive.

The shared fixture database problem

The usual starting point is a shared test environment with a snapshot of data loaded once, and tests written against known records. It works until two things happen: tests start mutating that data, and tests start running in parallel. Then test 14 cancels the order that test 31 was about to assert on, and the failure appears in a test that is entirely correct.

Restoring the database between tests is the obvious fix and it is far too slow to survive contact with a real suite. Restoring between runs leaves you unable to run two branches at once. Neither scales.

What we build instead

Each test creates the data it needs and owns it exclusively. Where the application has an API, setup happens through that API rather than by driving the browser, which is faster by an order of magnitude and removes the possibility of your login flow breaking every test in the suite. A factory layer generates records with sensible defaults so a test only specifies what matters to it, and identifiers are made unique per run so parallel workers cannot collide.

The strongest form of isolation, if your product supports it, is a fresh tenant or organisation per test. Multi-tenant SaaS products get this nearly for free and should use it. Where that is not possible, unique-per-run identifiers and a cleanup step get you most of the way.

Cleanup deserves a policy rather than a habit. We prefer cleanup at the start of a test over cleanup at the end, because a test that crashes never runs its teardown, and the residue accumulates until something breaks weeks later. A scheduled sweep of test-created records is a cheap safety net.

Stubbing and when it is dishonest

Network interception, through Playwright's routing or Cypress's intercept, is the right tool for third-party dependencies, error path testing and slow endpoints. Testing that your interface handles a 500 correctly is nearly impossible without it.

It also lets you write a test that passes against a backend contract that no longer exists. A stub is a copy of an agreement, and copies drift. Our rule is that stubs are for the paths you cannot otherwise reach, and that the contract itself is verified elsewhere by contract tests rather than assumed. If every one of your end-to-end tests runs against mocks, you have built a very elaborate front-end unit test suite and should be honest with yourself about that.

Production data is not test data

Worth saying plainly because it still happens. Copying production data into a test environment for realism moves real personal information into a system with weaker access control, generally without a lawful basis, and creates a problem under GDPR and comparable regimes that is much larger than the testing problem it solved. Use generated data, or a properly anonymised extract signed off by whoever owns data protection at your end. This is a point to check with your counsel rather than take from us.

Getting the Suite Under Ten Minutes in CI

A forty minute pipeline does not just cost forty minutes. It changes how people work. Engineers batch changes to avoid the wait, batched changes are harder to review, and harder reviews let more bugs through. The old Extreme Programming target of a ten minute build is still the right thing to aim at for the pre-merge path.

Parallelism and sharding

Two different levers, often confused. Parallelism is running several workers on one machine, which Playwright does by default with one browser context per worker. Sharding is splitting the suite across several machines, so shard 1 of 4 runs a quarter of the specs and the CI matrix runs four jobs at once. Combine both and a suite that takes thirty two minutes serially finishes in four.

The prerequisite is total test independence. If any test depends on another having run, or on data another test created, parallelism turns a working suite into a random number generator. This is why the test data work comes before the parallelism work, always.

Sharding naively by file count wastes time, because one spec with twenty long tests and one with two short ones land in the same bucket. Balancing shards by recorded duration is the difference between four minutes and eleven.

Run the right tests at the right time

Not every test needs to run on every commit. The shape that works: a smoke lane of the ten or fifteen most critical journeys on every push, finishing in under five minutes. The full suite on merge to the main branch. Long-running cross-browser sweeps, WebKit and Firefox included, on a nightly schedule. Anything that genuinely takes an hour runs before a release, not before a pull request.

Test impact analysis narrows things further, mapping tests to the code they exercise so a change only runs what it touches. It pays off on large monorepos. On a small codebase it is not worth building.

The unglamorous wins

Before any clever optimisation, we look at where the minutes actually go, and it is frequently not the tests. Rebuilding the application from scratch inside the test job instead of pulling a prebuilt image. Installing browser binaries on every run instead of caching them. Waiting ninety seconds for a database container to become healthy. Reinstalling dependencies with no lockfile cache. On one engagement, the largest single saving came from pulling a prebuilt container instead of building it, and no test was touched.

Measure the pipeline before optimising it. Teams routinely spend a sprint parallelising a test suite that accounted for a third of the wall clock time.

Visual Regression Testing, and Where It Goes Wrong

Functional tests answer whether the button works. They do not answer whether the button is now white on white, or whether the footer has collapsed on top of the terms link. For products where the interface is the product, that gap matters.

Visual regression fills it by capturing screenshots and comparing them against approved baselines. Playwright has this built in through screenshot assertions with a configurable pixel difference tolerance. Percy, Applitools and Chromatic are the hosted options, with Chromatic being the natural fit if you already maintain a Storybook and Applitools differentiating on perceptual comparison rather than raw pixel diffing.

The failure mode nobody warns you about

Visual testing fails when it produces diffs nobody reads. Font rendering differs between macOS and Linux, so a baseline captured on a developer's laptop will fail on a CI runner every time. Anti-aliasing differs between GPU and headless rendering. Any dynamic content, a timestamp, an avatar, a chart with live data, produces a diff on every run.

Within a month of an undisciplined rollout, every build has fourteen visual diffs, someone is clicking approve-all as a formality, and the entire mechanism is now a checkbox that catches nothing.

How we make it useful

Baselines are captured in the same containerised environment CI uses, never locally. Dynamic regions are masked explicitly. Comparison runs against component states in isolation, which is far more stable than full-page shots of a page assembled from live data. Coverage is deliberately narrow: the design system components, the two or three highest-traffic pages, and the responsive breakpoints that actually matter to your traffic. Approval is a review step with a named person, not a bulk action.

If you do not have the discipline for the approval step, we will say so and recommend not adopting visual regression at all. A mechanism that is ignored is worse than an absent one, because it occupies the space where a working control should be.

Page Object, Screenplay, or Neither

How you structure the code is the other half of maintenance cost. There are two established patterns and one common mistake.

The page object pattern

Martin Fowler's page object wraps each screen in a class exposing the operations a user can perform, so tests read as intent and the locators live in one place. When the login markup changes, one file changes. It is well understood, every engineer recognises it, and for most suites it is the right default.

Where it degrades is scale. Page objects grow into thousand-line classes covering every possible interaction with a screen. Inheritance appears, because two pages share a header. Then a diamond hierarchy appears. We have inherited suites where the page object layer was larger and harder to change than the application it tested. The discipline that prevents this is keeping page objects thin and composing small components rather than inheriting from base classes.

The screenplay pattern

Screenplay, which grew out of the Serenity BDD community, reorganises around actors performing tasks with abilities, rather than around pages. A test reads as a sequence of business tasks, and the composition is by small reusable units instead of by inheritance. On large suites with many roles and heavily shared behaviour, it holds up better than page objects do.

The honest trade-off is the learning curve. It is a genuinely different mental model, and on a team of five where two people write tests occasionally, it is overhead you will not recover. We use it where the suite is large, long-lived and maintained by people who work on it full time, and page objects everywhere else.

The mistake, and a note on Cucumber

The mistake is no structure at all: locators inline in every test, copy-pasted login steps in forty files, and no shared helpers. It is fast for the first ten tests and unbearable by the hundredth. Every rescue project we take on has some version of this.

On Gherkin and Cucumber, our position is unpopular but consistent. Business-readable scenarios earn their cost only when a non-engineer genuinely reads and writes them. If the product owner has never opened a feature file, you have added a translation layer between the test and the code for no reader, and every step definition is indirection an engineer has to trace through when a test fails. We will build it where the collaboration is real. We will tell you when it is ceremony.

When Not to Automate a Test

A test automation supplier telling you to automate less is unusual, so here is the reasoning. Every automated test is an asset with a maintenance liability attached. If the liability exceeds the value, the test is a net loss even though it passes.

Do not automate a flow that is about to change

If the checkout is being redesigned next quarter, automating the current one buys you a few weeks of regression safety and a rewrite. Test it manually until the design settles. The exception is when the redesign is exactly the risk, in which case a small set of tests written against roles and visible labels rather than markup will survive it.

Do not automate what a person judges better

Whether the layout looks right on an ultrawide monitor. Whether the error message makes sense. Whether the onboarding flow feels confusing. Whether a chart is legible. Machines check equality. People notice wrongness. Visual regression catches unintended change, which is not the same as catching bad design.

Do not automate the once-a-year path

A test for the annual data export that runs on every commit for twelve months costs more in maintenance and CI minutes than the twenty minutes a person would spend testing it before the export. Scripted manual testing is a legitimate answer for genuinely rare flows.

Do not automate against an interface with no stable hooks and no willingness to add them

If you are automating a third-party portal that changes without notice and offers no test identifiers, expect a high ongoing maintenance cost and decide up front whether it is worth paying. Sometimes it is, because the flow is critical. Often it is not, and monitoring the outcome in production is cheaper than testing the path.

Do not automate to hit a coverage number

Coverage targets imposed from above produce tests written to touch lines rather than to detect defects, and those tests still cost maintenance forever. We would rather report that sixty flows are covered and name them than report a percentage that means nothing to anybody.

How the Engagement Runs

Week 1: measure and rank

Run any existing suite repeatedly against unchanged code to establish a real flake rate per test. Time every pipeline stage. Sit with your team and rank user journeys by commercial impact, not by ease of automation. Output is an assessment document with the keep, rewrite and delete lists.

Week 2: foundation

Runner configuration, locator conventions written down as a standard, the test data and factory layer, environment configuration, and reporting. Then the first three tests through the whole loop, including CI, to prove the path end to end before anything is written at volume.

Weeks 3 to 6: the critical path

Tests written in ranked order, each meeting the definition of done before merge. Reviewed by your engineers, not just ours. By the end of this phase the flows that lose money when they break are covered and running on every merge.

Weeks 4 to 8: pipeline shape

Run in parallel with authoring. Split the smoke lane from the full suite, balance shards by duration, cache what should be cached, and cut the pre-merge wait to the target we agreed. This is where the ten minute goal is either met or explained.

Weeks 6 to 10: rebalance the pyramid

Identify end-to-end tests answering questions a component test could answer, and move them down. Usually the largest single reduction in suite runtime, and it comes with a coverage improvement rather than a loss.

Ongoing: triage and handover

Daily triage of red runs with a written policy, weekly flake and quarantine reporting, and documentation good enough that your engineers add tests without us. On a build engagement, handover is a deliverable with a date. On a retained one, triage is a named responsibility.

Three Situations We Get Called Into

None of these map to one client. Each is built from the failure mode as it actually shows up, stripped of anything that would identify who it happened to.

The suite nobody trusts

A B2B platform with around three hundred Cypress tests, a pipeline sitting near thirty five minutes, and a merge culture where clicking rerun twice was normal. The team's request was more coverage on a new module. Measurement told a different story: a large share of tests failed at least once across repeated runs of unchanged code, and the great majority of those traced to two causes, positional CSS selectors from a rushed redesign fix and shared seeded data being mutated by parallel workers.

The work was not new tests. It was rewriting locators onto roles and test identifiers, moving setup from the browser to the API so each test owned its data, deleting the tests that duplicated other tests, and moving form validation checks down to component level. The suite ends up smaller, the pipeline ends up faster, and the important change is behavioural: red starts meaning something again, so people start reading it.

The team with no automation and a compliance deadline

A healthcare-adjacent product where an audit required evidence that critical workflows were verified before each release, and where all regression testing was a spreadsheet worked through by two people over three days. Not a technology problem so much as an evidence problem.

Here the build starts from the audit requirement backwards. Identify the workflows the auditor cares about, automate exactly those first in Playwright, and make the reporting output something a non-engineer can read as evidence: which workflow, which build, when, pass or fail, with a trace attached. The three day manual cycle compresses to a pipeline run, and the two testers move to exploratory work on the areas the script never covered. What we do not do is make claims about your compliance posture. We build the mechanism; your compliance function decides whether it satisfies the standard.

The migration nobody has time for

A retail front end on an ageing Selenium suite in Java, maintained by one engineer who had left, with tests failing on Chrome updates and nobody able to fix the grid. The internal debate had been running for a year: rewrite in Playwright, or repair what exists.

The answer was neither, initially. First we made the existing suite explicable: catalogue what each test covers, run the grid in containers so browser versions are pinned rather than floating, and get to a known state. Only then does the rewrite question become answerable, and it is answered per flow rather than as a single decision. High-value flows get rewritten in Playwright with proper locators. Low-value ones get deleted rather than migrated, which is the cheapest migration available. Running two frameworks side by side for a period is normal and is much safer than a big-bang cutover that leaves you with no working tests for six weeks.

How Does an Automation Team in India Work With Your Timezone?

Being straight about this matters more than selling it. A standard 09:30 to 18:30 IST working day overlaps a UK team by roughly four to five hours in the afternoon. Against US Eastern it overlaps by almost nothing, and against US Pacific by nothing at all. Anyone telling you otherwise is either running a night shift they have not mentioned or hoping you do not check.

Where a shift pattern is genuinely needed, it is a real cost rather than a free feature. Staggered hours mean the engineer is working when their family is asleep, which affects who you can retain on the work and for how long. We will agree an overlap window with you before the engagement starts, and it goes into the agreement rather than being assumed.

Why test automation suits the gap better than most work

The output of this work is a suite that runs unattended. That is unusually well suited to a distributed team. Your engineers merge during your day. The suite runs. Our team picks up the results at the start of the IST morning, triages every failure while your office is closed, and by the time your day begins there is a written report saying which failures are product bugs, which are environment problems, which are flakes, and what was already fixed.

That is a genuine advantage rather than a marketing line, and it is the specific reason offshore test engineering works when offshore feature development sometimes struggles. Triage is asynchronous by nature. It needs evidence, not conversation.

What we run to make it work

Written first, always. Every triage decision is recorded with the trace or screenshot attached, so a conversation is never the only place a decision lives. A daily written handover at the end of the IST day covering what was triaged, what is blocked and what needs a decision from your side. A short live call inside the overlap window on the days a decision is needed, rather than a standing meeting nobody prepares for.

Direct access between your engineers and ours, in your Slack or Teams, with no account manager relaying technical questions. Test automation requires constant small clarifications about intended behaviour, and routing those through a third party adds a day to each one.

Code review runs both directions. Our tests go through your pull request process and are reviewed by your engineers, which is the only reliable way to keep conventions shared and to stop a parallel codebase forming that only we understand.

The talent question for this specific skill

Test automation engineering in India has a wide range in it, wider than for backend or front-end roles, because a lot of people carrying the title have only ever recorded and replayed scripts. Our screening reflects that. Candidates debug a deliberately flaky suite rather than write a fresh test, because diagnosing why a test passes locally and fails in CI is the actual job. They are asked to justify a locator strategy and defend it. They are asked which test in a given set they would delete and why, since knowing what not to automate is the harder judgement.

English is assessed on written technical explanation, not on conversation, because the deliverable that crosses the timezone gap is a written triage note that has to be unambiguous to somebody reading it eight hours later without you in the room.

What Goes Wrong, and How We Handle It

The environment is the real blocker

The most common reason an automation engagement stalls is not testing at all. It is that there is no stable environment to test against: the shared staging box is broken three days a week, the database has drifted from production, or a dependency is only available from your office network. We check this in week one and say so early, because no amount of test writing fixes it and it usually needs your team, not ours.

Access and onboarding take longer than anyone plans

VPN, source control, CI, the test environment, any device or browser cloud, the issue tracker. On engagements with security review this can take a fortnight, and it is dead time if it starts on day one instead of before. We ask for the access list to be started before the engagement begins, and we work from a documented list rather than requesting things one at a time as we discover we need them.

Nobody owns the flake list

A suite decays without a named owner for triage. When that role is unassigned, the quarantine list grows, the retry count creeps up, and within a quarter you are back to where you started. We insist on the role being named, on our side or yours, and we report the flake rate weekly whether or not anyone asks.

The tests find real bugs and nobody wants them

This happens more than you would think. A new suite surfaces genuine defects in flows that have been broken quietly for months, and now there is a backlog nobody budgeted for. Worth anticipating at the start, because the pressure to soften the tests instead of fixing the product is real and it arrives around week four.

Security, access and IP

Work happens on managed machines with disk encryption and controlled access, over your VPN where you require one, with credentials held in your secret manager rather than in test files or a spreadsheet. Test accounts are scoped and separate from production. Ownership of the code and test assets, confidentiality, and any data processing arrangements are settled in the agreement before work starts rather than described on a web page, so ask for those terms early and read them. If your context brings HIPAA, GDPR or a sector regulator into scope, that is a conversation for your counsel and ours before scope is agreed.

Continuity

Loss of the one person who understands the framework is a genuine risk in every automation engagement, and it is the reason so many suites are abandoned. We mitigate it with conventions written down rather than held in someone's head, at least two engineers familiar with any suite, and documentation treated as a deliverable rather than an afterthought. The handover and continuity terms themselves are agreed in the contract before work begins.

Engagement Models

Dedicated test engineers

Automation engineers working as part of your team, in your tools and your pull request process, reporting to your engineering manager. Suits an ongoing suite with continuous triage and a growing product. You direct the priorities day to day.

Fixed-scope build

An agreed set of flows automated to the definition of done, with the framework, CI wiring, documentation and handover as deliverables. Suits a first suite or a rescue with a clear end state. Scoped after the week one assessment, never before it.

Retained maintenance

Your team writes the tests; we keep the suite healthy. Daily triage, flake investigation, framework upgrades, browser version changes and pipeline tuning. Suits teams whose problem is not writing tests but keeping them worth reading.

Commercial terms, notice arrangements and team composition are agreed with you in the contract before work starts. We do not publish them, because the honest answer depends on scope and we would rather quote against your actual situation than a number that fits nobody.

Where This Sits Alongside Our Other Work

Functional UI automation is one layer. Load and stress behaviour, contract verification between services, security testing and accessibility conformance are separate disciplines and we scope them separately rather than folding them into a browser suite where they do not belong.

The suite is only as useful as the pipeline it runs in, so this work usually runs alongside CI/CD pipeline engineering, and it overlaps with the broader QA and testing practice where manual and exploratory work sits. Where you need testers embedded in your team rather than a project, you can hire QA testers directly, and for device-level work our mobile testers cover the native side. If the automation gap is really a shortage of engineers who can write typed test code well, teams often start by choosing to hire TypeScript developers in India and grow the capability internally.

Frequently Asked Questions About Automated Testing in India

Playwright or Cypress for a new project?

For most new suites we start with Playwright. Parallel workers, browser contexts, cross-origin flows and multiple tabs work without argument, and the trace viewer cuts CI debugging from hours to minutes. Cypress is the better answer when your team values its in-browser debugging above everything else and your app is a single-origin single-page application. Neither choice is permanent. The expensive part is the selectors and the test data, and both survive a migration.

How many end-to-end tests should we have?

Far fewer than most teams end up with. We aim for a small set covering the flows that lose money if they break: sign-in, checkout, the primary create-and-save path, and any regulated workflow. Everything else belongs in component or integration tests that run in seconds. A suite of forty solid end-to-end tests that people trust beats four hundred that get rerun until green.

Why not just add retries to flaky tests?

Because a retry that passes on the second attempt is evidence, not noise. Roughly half the flakes we investigate turn out to be real race conditions in the product: a button live before its handler is bound, a stale read after a write, a toast that swallows a click. Retries make that class of bug invisible. We keep one retry to absorb genuine infrastructure noise, and we record every retry as a signal to investigate.

Can you automate tests for an application we did not build in-house?

Yes, and it is most of our work. The constraint is not the code, it is the markup. If the application has no stable identifiers we either agree a small set of test attributes with whoever maintains it, or we build role and label based locators against the accessible tree. The second route is slower to write and holds up surprisingly well, because visible labels change less often than class names.

How long before an automated suite is worth trusting?

Two to three weeks to get a first pass of critical flows running green in your pipeline, and around eight to twelve weeks before people stop asking whether a red build is real. The gap between those two dates is the part teams underestimate. Trust is earned by flake rate and by triage discipline, not by the number of tests written.

Do we still need manual testers once the suite is automated?

Yes, and they should be doing different work. Automation is good at checking that known behaviour has not changed. It is poor at noticing that a screen is confusing, that an error message is wrong, or that a workflow nobody specified is broken. Exploratory testing finds those. The suite exists so that skilled testers stop spending their week reclicking the same regression script.

How does an automation team in India fit our release schedule?

A 09:30 to 18:30 IST day gives a UK team roughly four to five hours of live overlap and a US East Coast team very little. Test automation suits that better than most work: the suite runs on your merges overnight in our timezone, and the triage report is written before your day starts. The overlap window and any shift pattern are agreed with you before work begins.

Tell Us What Your Suite Does Today

How long the pipeline takes, how often it goes red, and which flows you would not ship without checking. We will come back with the flake numbers, the keep and delete lists, and a scope against what we found rather than against what we guessed.

Start the Conversation