Skip to content
Text Repeater
Development

Generating Repeated Test Data: A Developer's Guide

How to generate bulk and repeated text to find validation, overflow and Unicode bugs, with JavaScript and Python snippets and edge-case strings worth testing.

The Text Repeater Team8 min read

Most text-handling bugs are found by putting something rude into a field: too long, too short, too strange. The hard part is not the testing, it is having the right awkward string ready when you need it. This is a practical guide to generating repeated and bulk text for testing, and to the specific strings that reliably break software.

What repeated text is actually good for

Repetition is a blunt instrument, which is exactly why it works. A 10,000-character string of a does not care what your validation meant to do; it only exercises what it does.

Finding validation bugs

Validation is usually written against a happy path and then never revisited. Long inputs expose the gaps:

  • Fields with a maxlength on the client but no check on the server.
  • Server-side limits enforced at a different number than the UI advertises.
  • Validation that counts UTF-16 code units while the database counts bytes.
  • Error messages that themselves break when they interpolate the offending value.

Paste 256, 257, 1,000 and 100,000 characters in turn. Off-by-one bugs live at the boundary; crashes live past it.

Text overflow and truncation in the UI

Repeated text is the fastest way to see how a layout fails. Three cases behave very differently:

Input What it tests
A long string with spaces Normal wrapping and container height growth
A long string with no spaces Horizontal overflow, overflow-wrap, broken grids
Many short lines Scroll containers, virtualisation, list height calculation

The no-spaces case is the one that ships broken. A 500-character unbroken token will push a flex child past its container, add a horizontal scrollbar to the whole page, or silently clip content depending on the CSS. Test it on every user-supplied string that renders in a constrained box: display names, filenames, tag chips, table cells.

Truncation deserves its own pass. Check that ellipsis appears where you expect, that the full value is still reachable (title attribute, tooltip, detail view), and that truncation happens at grapheme boundaries rather than mid-character.

Database column limits

VARCHAR(255) is a promise your application has to keep. Generate exactly 255 characters, then 256, and confirm you get a clean validation error rather than a driver exception or a silent truncation. Then repeat with multi-byte characters, because "255" means different things in different engines and encodings — a column sized in bytes will reject a 255-character string of accented text that a column sized in characters accepts.

Pagination and virtualised lists

Bulk lines are the cheapest way to get a realistic list. Generate 10, 11, 50, 501 and 10,000 numbered rows and check the page-boundary behaviour: the last page with one item, the count display, the "select all" semantics, and whether a virtualised list recycles rows correctly when you scroll fast. Numbered lines matter here — with identical rows you cannot tell whether the list is scrolling or repainting. A number repeater gives you a distinct value per line, which makes off-by-one paging errors visible immediately.

Load-testing text areas and editors

Rich text editors, markdown previews, syntax highlighters and diff views all degrade non-linearly. Paste 50,000 characters into an editor and watch the input latency; paste 5,000 short lines and watch it again, because line count and character count stress different code paths. Debounced autosave that fires a full-document POST on every keystroke shows up here and nowhere else.

Doing it in code

For anything repeatable — a fixture, a regression test, a seeded database — write it in code so it lives in the repository.

// Basic repetition
const long = "a".repeat(10_000);

// A long word with no break opportunities
const unbreakable = "Lorem".repeat(200); // 1000 chars, zero spaces

// Exactly N characters, from a repeating pattern
const exactly255 = "abcdefghij".repeat(26).slice(0, 255);

// Numbered lines for list and pagination tests
const rows = Array.from({ length: 10_000 }, (_, i) => `Row ${i + 1}`).join("\n");

// Careful: length counts UTF-16 code units, not characters
"\u{1F44D}".length;                      // 2
[..."\u{1F44D}"].length;                 // 1
new Intl.Segmenter().segment("\u{1F44D}"); // grapheme-accurate
# Basic repetition
long = "a" * 10_000

# Exactly N characters
exactly_255 = ("abcdefghij" * 26)[:255]

# Numbered lines
rows = "\n".join(f"Row {i}" for i in range(1, 10_001))

# Byte length is what your database cares about
s = "café"
len(s)                 # 4 characters
len(s.encode("utf-8")) # 5 bytes

Two things to internalise from those snippets. First, len() in Python counts code points and .length in JavaScript counts UTF-16 code units, and neither counts what a user calls a character. Second, none of them counts bytes, which is usually what a storage limit is expressed in. If your validation and your column disagree about the unit, you have a bug waiting for the first user with an emoji in their display name.

When a browser tool beats writing a script

Code is right for anything that needs to be repeated. A tool is right for the other 80% of testing, which is exploratory.

Reach for a browser tool when:

  • You are poking at a form by hand and need a 5,000-character blob now, not after opening an editor.
  • You are testing someone else's application and have no local environment.
  • You need to eyeball the output before pasting it, which is hard to do with a variable in a REPL.
  • You are handing a reproduction case to a non-developer — a designer or support colleague can use a web page, not a Node script.
  • You need a specific separator, wrapper or numbering scheme and would otherwise spend longer on the join() than on the test.

Practically, the main text repeater covers arbitrary strings, the sentence repeater produces realistic prose blocks for layout testing, the emoji repeater builds multi-byte payloads quickly, and the character counter tells you exactly how long the result is before you paste it. When your generated fixtures need deduplicating, remove duplicate lines is faster than a shell pipeline you have to remember the flags for.

Write the script when the string becomes a test case. Use the tool when the string is a question you are asking once.

Edge-case strings worth keeping in a file

Keep a scratch file of these and paste them into every text field you own. Codepoints are given so you can reproduce them exactly rather than relying on copy-paste surviving the trip.

Case How to produce it What it breaks
Empty string "" Required-field checks, if (value) truthiness, empty-state rendering
Single space " " Validation that trims after checking presence; "not empty" that is functionally empty
Leading/trailing whitespace " name " Duplicate detection, lookups, uniqueness constraints
Very long single word "a".repeat(500) Layout overflow, word-break, table columns, PDF export
Newlines in a single-line field "a\nb" Log injection, CSV export, headers, single-line display
RTL text Arabic or Hebrew, e.g. مرحبا Bidi layout, mixed-direction punctuation, cursor position
Bidi override characters U+202E RIGHT-TO-LEFT OVERRIDE Filename spoofing, display that disagrees with stored value
Combining characters "e" + "́" (é as two code points) Length counts, comparison, search, normalisation
Stacked combining marks Base letter + 20 combining marks Line height blowout, rendering performance
Astral-plane characters U+1F600 and similar Naive substring, charAt, MySQL utf8 (3-byte) columns
Emoji with skin-tone modifier U+1F44D followed by U+1F3FD Grapheme counting, truncation splitting the modifier off
ZWJ sequences Family and profession emoji joined with U+200D Character counts, reversal, cursor movement
Zero-width characters U+200B, U+200C, U+FEFF Invisible content passing "not empty", broken search, sneaky duplicates
Non-breaking space U+00A0 trim() that only strips ASCII whitespace, failed equality with a normal space
Homoglyphs Cyrillic а (U+0430) for Latin a Username uniqueness, phishing, search misses
HTML and template syntax <script>, {{7*7}}, ${x} Escaping, template injection, sanitiser gaps
SQL-ish punctuation O'Brien, --, ; Query building, CSV quoting
Null byte "\0" C-backed libraries, filesystem calls, some drivers

Two notes on that list. Normalisation is the quiet one: "é" composed (U+00E9) and decomposed (e + U+0301) look identical and compare unequal, so a user can register a "duplicate" account that your uniqueness check accepts. Normalise to NFC on input and compare normalised forms. And zero-width characters are the ones that reach production most often, because they survive copy-paste from styled documents and are invisible in every code review.

A quick routine for a new text field

  1. Submit empty, then a single space.
  2. Submit exactly the advertised limit, then one over.
  3. Submit 500 characters with no spaces and look at the layout, not the response.
  4. Submit an emoji with a skin-tone modifier and read the value back from the database.
  5. Submit text with a leading and trailing space, then try to create the same record without them.
  6. Submit 5,000 characters and watch input latency and network traffic.
  7. Render the stored value somewhere else — an export, an email, a PDF — and check it survived.

Step seven catches the most bugs. Text usually breaks on the way out, not on the way in.

Key takeaways

  • Repeated text is a cheap fuzzer for validation, layout and storage limits; the no-spaces long string is the single highest-yield input.
  • Character counts, code-unit counts and byte counts are three different numbers, and mismatches between them cause most Unicode storage bugs.
  • Write a script when a string becomes a test case; use a browser tool when you are exploring by hand or working outside your own environment.
  • Keep a file of edge-case strings — zero-width characters, combining marks, ZWJ emoji and homoglyphs — and run every new field through the same short routine.
  • testing
  • qa
  • unicode
  • javascript
  • python

Keep reading