A cloud migration changes the engine, and that’s where the quiet breakage lives. A text column comes over a few characters short. An amount lands in a column with a different scale. A character that was fine on the old system turns into something else on the new one. The row count still matches, so the cutover meeting goes fine.
A health check can’t see it either. It asks whether the data looks right, and a trimmed string still looks right.
TestGen’s answer is to stop checking whether the target looks healthy and start checking whether it matches the source.
What Table Comparison checks, failure by failure
Table Comparison shipped on October 14, 2026. It profiles the source, records what every column looked like as a baseline, and turns that profile into tests from 32 test types. You run them on the target. The families below come from New in TestGen: Prove Your Data Migration Worked.
| What broke in the move | Tests that catch it |
|---|---|
| Rows lost or added | Row Count |
| Strings truncated or trimmed | Minimum, Maximum, and Total Length; Leading Spaces |
| Amounts changed, rounded, or truncated | Minimum, Maximum, Sum, Standard Deviation, Fractional Sum |
| Stray characters, case changes | Non-Printing and Upper Case counts; Frequent Patterns |
| Dates clipped or shifted across a time zone | Minimum Date, Maximum Date, Distinct Date Count |
| Any value changed, even to one of the same length | Text, Numeric, and Date Hash |
| A value moved to a different row | Keyed Text, Numeric, and Date Hash |
| A column retyped | Column Type |
The hash rows do the heavy lifting. Change one character in one row of a million and the column’s hash sum moves. In the Jaffle Shop run, two trailing spaces in a 10-row table set off 9 failing tests.
The numbers from the Jaffle Shop run
DataKitchen migrated the Jaffle Shop demo database on purpose, split one table in two, fixed some hygiene problems, and left 483 empty orders in quarantine. Then it ran two suites on the target.
| Suite on the target | Tests | Passed | Failed | Errors |
|---|---|---|---|---|
| Standard generated suite (health checks) | 55 | 36 | 0 | 19 |
| Table Comparison suite | 345 | 193 | 61 | 91 |
All 61 Table Comparison failures traced to changes the team made on purpose:
| Change made on purpose | Main tests that caught it |
|---|---|
| 483 empty orders left in quarantine | Row Count on orders (61,465 against a baseline of 61,948); Distinct Count; Total Length |
| Header rows removed from two tables | Row Count off by one on each; Distinct Count, Minimum Length, and Total Length; Text Hash |
| Trailing spaces trimmed on two products | Text Hash, Keyed Text Hash, Maximum Length, Total Length, Frequent Values |
| 13 columns retyped from text | Match Column Type on each one |
The 91 errors were text-only tests on retyped columns, which Match Column Type had already flagged, so you deactivate them on the target. The 345 tests ran in five seconds. Run them after every mock load, not once at cutover.
Source and target engines
Both sides of that example were Postgres. Most cloud migrations change engines, and the post names the usual ones: SQL Server to Snowflake, Oracle to Databricks, an on-premises warehouse to BigQuery. The tests run as SQL on the target, and hashing is canonical across engines. Numbers round to six decimal places and dates truncate to the second in UTC.
Two engines are the exception. On SAP HANA and Salesforce Data 360, TestGen doesn’t generate the hash tests. Every other database TestGen supports, including Oracle and BigQuery, plus DuckDB and file sources, gets the full set. The databases answer has the list, and every one works in both editions.
Some engine differences fail on a correct copy. Text hashes need UTF-8 collations on SQL Server, which means SQL Server 2019 or later, or Azure SQL. Oracle pads CHAR columns. CSV files read empty fields as nulls. Deactivate those tests on the target. Don’t loosen them.
What it won’t tell you
Table Comparison isn’t a row-by-row diff. It never moves data between systems, so a failure names the column, not the row. In DataKitchen’s testing on three public datasets with 785 planted changes, it caught 774. The misses were binary columns, type changes inside the same general type, and values swapped across a composite key. The TestGen docs add two more: values swapped between rows of a table with no key column, and numeric changes that vanish when rounded to six decimal places. If your migration risks one of those, write a specific test for it.
It also can’t tell an intended change from a mistake. That’s what a written list of expected differences is for, and validating a migration with a small team covers that list, the mapping file, and the sign-off. Before any of it, profile the source and keep the snapshot. Your legacy data has problems, and the snapshot is how you prove which ones you inherited. Validation needs that comparison between source and target. A target checked on its own can only tell you it looks fine.