Fixtures & Test Data¶
What was run¶
A real 4-case JSON fixture file, loaded with cy.fixture() and iterated to drive the same login flow with
different real data — the same data-driven approach this network's own Selenium course used with plain JSON
files, now via Cypress's own built-in fixture loading.
[
{ "name": "valid credentials", "username": "realuser", "password": "realpass123", "expected": "Login Successful" },
{ "name": "wrong password", "username": "realuser", "password": "wrongpass", "expected": "Login Failed" },
{ "name": "wrong username", "username": "wronguser", "password": "realpass123", "expected": "Login Failed" },
{ "name": "empty credentials", "username": "", "password": "", "expected": "Login Failed" }
]
cy.fixture("login-cases.json").then((cases) => {
expect(cases.length).to.eq(4);
cases.forEach((c) => {
cy.visit("/login.html");
cy.get("#username").type(c.username);
cy.get("#password").type(c.password);
cy.get("#submit-btn").click();
cy.get("#result").should("have.text", c.expected);
});
});
A real bug, caught on the very first run¶
The "empty credentials" case's username: "" genuinely crashed the test — not because the login logic is
broken, but because .type("") is a real, deliberate Cypress restriction: typing nothing isn't a
meaningful user action, so Cypress refuses it outright rather than silently doing nothing. The real fix
was to skip the .type() call entirely when the fixture value is empty (the input already starts empty, so
there's nothing to type):
if (c.username) cy.get("#username").type(c.username);
if (c.password) cy.get("#password").type(c.password);
How It Actually Works¶
cy.fixture() reads a real file from cypress/fixtures/ and returns its parsed contents (JSON is parsed
automatically; other file types come back as text or base64) — it's a real file read, not a mocked or
in-memory data structure, which is why the fixture file genuinely needs to exist at that real path. Once
loaded, fixture data is just a normal JavaScript value — the .forEach() loop driving four separate real
login attempts isn't a Cypress-specific looping construct, it's ordinary JS iterating over real data that
happened to come from cy.fixture(). This lesson's real .type("") crash is a deliberate Cypress design
choice (not a fixture-specific bug) that exists specifically to catch tests that pass genuinely malformed
or empty data without the test author noticing — in this case, correctly flagging that "empty string"
needs different handling than "type this string," rather than silently treating them the same way.
Common mistakes¶
- Assuming fixture data can always be passed straight into
.type()without checking for empty values — this lesson's real crash is exactly what happens when a data-driven test's fixture includes an edge case (empty input) that needs different handling, not identical handling, to the normal cases. - Hardcoding fixture file paths with a leading
cypress/fixtures/—cy.fixture("login-cases.json")already resolves relative to that real directory; adding the prefix looks for a nonexistent nested path. - Loading a fixture inside every single
it()block when the data doesn't change between tests — this lesson's singlecy.fixture().then()call loads the real file once and iterates all 4 real cases inside it, which is both faster and avoids 4 separate real file reads for identical data.
Exercise¶
Add a fifth case to login-cases.json with "password": null instead of an empty string, and re-run this
lesson's test without additional changes. Determine, from the real observed Cypress error (or lack of one)
rather than assumption, whether .type(null) fails the same way .type("") did, differently, or not at
all — and adjust the if (c.password) guard if the real result shows it's needed.