The API-first setup pattern helps prepare test data quickly and reliably by using backend APIs before running UI tests.
API-first setup pattern in Cypress
Introduction
Syntax
Cypress
cy.request({
method: 'POST',
url: '/api/users',
body: { username: 'testuser', password: 'pass123' }
}).then((response) => {
expect(response.status).to.eq(201)
// use response data for next steps
})Use cy.request() to call backend APIs directly.
Check the response status to confirm setup success.
Examples
Cypress
cy.request('POST', '/api/login', { username: 'user1', password: 'pwd' }) .then((res) => { expect(res.status).to.eq(200) })
Cypress
cy.request({
method: 'DELETE',
url: '/api/users/123'
}).then((res) => {
expect(res.status).to.eq(204)
})Cypress
cy.request({
method: 'GET',
url: '/api/products'
}).then((res) => {
expect(res.body).to.have.length.greaterThan(0)
})Sample Program
This test suite uses API calls to create a user before tests, then tests login via UI, and finally deletes the user after tests.
Cypress
describe('API-first setup example', () => { before(() => { // Create a test user via API cy.request({ method: 'POST', url: '/api/users', body: { username: 'testuser', password: 'pass123' } }).then((response) => { expect(response.status).to.eq(201) cy.wrap(response.body.id).as('userId') }) }) it('logs in with the created user', function () { cy.visit('/login') cy.get('input[name=username]').type('testuser') cy.get('input[name=password]').type('pass123') cy.get('button[type=submit]').click() cy.url().should('include', '/dashboard') cy.contains('Welcome, testuser').should('be.visible') }) after(function () { // Clean up by deleting the test user cy.request({ method: 'DELETE', url: `/api/users/${this.userId}` }).then((res) => { expect(res.status).to.eq(204) }) }) })
Important Notes
Always verify API responses to catch setup errors early.
Use aliases (cy.wrap(...).as()) to share data between hooks and tests.
Cleaning up test data after tests keeps your environment stable.
Summary
API-first setup uses backend calls to prepare test data before UI tests.
This pattern makes tests faster and more reliable.
Remember to verify API responses and clean up after tests.
Practice
1. What is the main benefit of using the
API-first setup pattern in Cypress tests?easy
Solution
Step 1: Understand API-first setup purpose
API-first setup means using backend API calls to prepare data before UI tests.Step 2: Identify the benefit
This approach makes tests faster and more reliable by avoiding UI data entry steps.Final Answer:
It prepares test data quickly by calling backend APIs before UI tests run. -> Option AQuick Check:
API-first setup = faster, reliable test data setup [OK]
Hint: API-first means setup via backend calls before UI tests [OK]
Common Mistakes:
- Thinking API-first replaces UI tests
- Assuming API calls slow down tests
- Believing API-first only works on frontend
2. Which of the following is the correct way to make an API call in Cypress for setup?
easy
Solution
Step 1: Recall Cypress API call syntax
Cypress usescy.request()to make HTTP requests.Step 2: Match correct method and parameters
cy.request('POST', url, body)is the correct syntax.Final Answer:
cy.request('POST', '/users', {name: 'John'}) -> Option DQuick Check:
Use cy.request() for API calls in Cypress [OK]
Hint: Use cy.request() for API calls in Cypress [OK]
Common Mistakes:
- Using cy.api() which is not a Cypress command
- Using cy.call() or cy.fetch() which don't exist in Cypress
- Mixing up method names or parameters
3. Given this Cypress test snippet using API-first setup:
What will happen if the API returns status 400 instead of 201?
cy.request('POST', '/api/users', {name: 'Alice'}).then((response) => {
expect(response.status).to.eq(201)
cy.visit('/users/' + response.body.id)
})What will happen if the API returns status 400 instead of 201?
medium
Solution
Step 1: Analyze the assertion on response status
The test expectsresponse.statusto equal 201.Step 2: Understand behavior if status is 400
If status is 400, the assertionexpect(response.status).to.eq(201)fails and test stops with failure.Final Answer:
The test will fail at the assertion checking response.status. -> Option CQuick Check:
Assertion fails if response.status != 201 [OK]
Hint: Assertion fails if API response status is unexpected [OK]
Common Mistakes:
- Assuming test continues after failed assertion
- Thinking Cypress throws syntax error on bad status
- Believing test ignores failed API responses
4. You wrote this API-first setup code in Cypress:
But the test fails with "Cannot read property 'then' of undefined". What is the likely cause?
cy.request('POST', '/api/items', {name: 'Book'})
.then((res) => {
expect(res.status).to.eq(200)
cy.wrap(res.body.id).as('itemId')
})
cy.get('@itemId').then((id) => {
cy.visit(`/items/${id}`)
})But the test fails with "Cannot read property 'then' of undefined". What is the likely cause?
medium
Solution
Step 1: Understand Cypress command chaining and timing
Cypress commands run asynchronously and in order, but alias usage requires the alias to be set before use.Step 2: Identify timing issue with alias usage
cy.get('@itemId')is called immediately, before the alias is set inside thethencallback, causing undefined error.Final Answer:
Thecy.get('@itemId')runs before the alias is set. -> Option AQuick Check:
Alias must be set before cy.get() uses it [OK]
Hint: Set alias before using cy.get('@alias') to avoid timing errors [OK]
Common Mistakes:
- Assuming API error causes this specific error
- Thinking cy.request is misspelled
- Believing cy.wrap cannot create aliases
5. You want to use API-first setup to create multiple users before a UI test. Which approach correctly ensures all users are created before visiting the page?
const users = [
{name: 'Anna'},
{name: 'Ben'},
{name: 'Cara'}
]
// Which code snippet is correct?hard
Solution
Step 1: Understand Cypress command queue and async behavior
Cypress commands likecy.requestare asynchronous and queued. To wait for all requests, usecy.wrap().each()with a.then()callback.Step 2: Analyze each option
The nativeforEachandfor-ofoptions placecy.visit()inside the loop, causing visits after each request instead of after all. Thecy.wrap(users).each(...) cy.visit()executes visit before requests due to queuing. Onlycy.wrap(users).each(...).then(() => cy.visit())waits for all requests before visiting.Final Answer:
cy.wrap(users).each(user => { cy.request('POST', '/api/users', user) }).then(() => { cy.visit('/users') }) -> Option BQuick Check:
Use cy.wrap().each() with then() to wait before next step [OK]
Hint: Chain .then() after cy.wrap().each() to wait for all API calls [OK]
Common Mistakes:
- Placing cy.visit() inside the forEach or for-of loop
- Using cy.wrap().each() without .then() afterward
- Not chaining .then() to wait for all iterations
Recommended
Ready to prove it?
Pass the test and this pattern moves unknown
10 questions ~5 mineasy hard
