0
0
Cypresstesting~15 mins

App Actions pattern in Cypress - Deep Dive

Choose your learning style9 modes available
Overview - App Actions pattern
What is it?
The App Actions pattern is a way to organize test code by grouping user interactions and app behaviors into clear, reusable functions. These functions represent what a user does or what the app does, like clicking buttons or filling forms. This pattern helps keep tests clean, easy to read, and maintain. It separates the 'how' of interacting with the app from the 'what' the test wants to check.
Why it matters
Without the App Actions pattern, test code can become messy and hard to understand, especially as tests grow. Tests might repeat the same steps many times, making them fragile and slow to update. Using this pattern saves time, reduces errors, and makes tests more reliable, so developers can trust their tests and fix bugs faster.
Where it fits
Before learning this, you should know basic Cypress commands and how to write simple tests. After mastering App Actions, you can learn about Page Object Model, custom commands, and advanced test design patterns to organize even bigger test suites.
Mental Model
Core Idea
App Actions pattern means writing small, named functions that describe user or app behaviors to make tests clear and reusable.
Think of it like...
It's like having a recipe book where each recipe is a clear step, such as 'chop onions' or 'boil water', so cooking a meal is easy and repeatable without guessing what to do next.
┌───────────────┐
│ Test Scenario │
└──────┬────────┘
       │ calls
┌──────▼────────┐
│ App Actions   │
│ (clickLogin)  │
│ (fillForm)    │
└──────┬────────┘
       │ uses
┌──────▼────────┐
│ Cypress APIs  │
│ (cy.get,     │
│  cy.click)   │
└──────────────┘
Build-Up - 7 Steps
1
FoundationUnderstanding Cypress Basic Commands
🤔
Concept: Learn the basic Cypress commands to interact with web elements.
Cypress uses commands like cy.get(selector) to find elements and cy.click() to click them. For example, cy.get('#submit').click() clicks a button with id 'submit'. These commands let you simulate user actions.
Result
You can write simple tests that open pages, find buttons, and click them.
Knowing how Cypress commands work is essential before grouping them into reusable actions.
2
FoundationWriting Simple Cypress Tests
🤔
Concept: Create basic tests that perform user steps directly in the test body.
A test might look like: cy.visit('/login'); cy.get('#username').type('user'); cy.get('#password').type('pass'); cy.get('#login').click(); cy.contains('Welcome').should('exist');
Result
Tests run and check if the app behaves as expected after user actions.
Directly writing steps works but can get repetitive and hard to maintain.
3
IntermediateIntroducing App Actions Functions
🤔Before reading on: do you think grouping commands into functions makes tests longer or shorter? Commit to your answer.
Concept: Group related Cypress commands into named functions that represent user actions.
Instead of repeating cy.get and cy.click everywhere, create functions like function login(username, password) { cy.get('#username').type(username); cy.get('#password').type(password); cy.get('#login').click(); } Then call login('user', 'pass') in tests.
Result
Tests become shorter and easier to read, focusing on what is done, not how.
Grouping commands into actions hides complexity and improves test clarity.
4
IntermediateUsing App Actions for Reusability
🤔Before reading on: do you think App Actions help reduce duplicated code or increase it? Commit to your answer.
Concept: Reuse App Actions functions across multiple tests to avoid repeating code.
If many tests need to log in, they all call the login() function. If the login process changes, update only login(), not every test.
Result
Tests are easier to maintain and less error-prone when app behavior changes.
Reusability through App Actions saves time and reduces bugs in test suites.
5
IntermediateSeparating Actions from Assertions
🤔Before reading on: should App Actions include checks or only perform steps? Commit to your answer.
Concept: Keep App Actions focused on performing steps; keep assertions separate in tests.
App Actions like login() do not check if login succeeded. Tests call login() then assert cy.contains('Welcome').should('exist'). This separation keeps actions reusable and tests clear.
Result
Tests clearly show what is done and what is checked, improving readability.
Separating actions and checks prevents mixing concerns and makes tests flexible.
6
AdvancedOrganizing App Actions in Modules
🤔Before reading on: do you think putting App Actions in separate files helps or complicates tests? Commit to your answer.
Concept: Place App Actions functions in separate files or folders to organize code better.
Create files like loginActions.js exporting login(), logout(), etc. Import them in tests. This keeps test files clean and actions reusable across projects.
Result
Test codebase is scalable and easier to navigate as it grows.
Modular organization supports team collaboration and long-term maintenance.
7
ExpertHandling Complex Flows with Parameterized Actions
🤔Before reading on: can App Actions handle different user roles or inputs with the same function? Commit to your answer.
Concept: Design App Actions to accept parameters for flexible, complex user flows.
For example, login(role) can fill different usernames/passwords based on role. This reduces function count and adapts to many scenarios.
Result
Tests can cover many cases with fewer, smarter action functions.
Parameterized actions increase power and reduce duplication in large test suites.
Under the Hood
App Actions are just JavaScript functions wrapping Cypress commands. Cypress commands queue asynchronously and run in order. When an App Action calls cy.get or cy.click, Cypress schedules these commands in its internal queue. The test runner executes them step-by-step, waiting for each to complete before moving on. This means App Actions do not run commands immediately but build a chain of commands that Cypress processes.
Why designed this way?
Cypress uses a command queue to handle asynchronous browser interactions smoothly. Wrapping commands in functions (App Actions) leverages this queue without blocking code. This design keeps tests readable and reliable, avoiding callback hell or complex async code. The pattern emerged to manage growing test suites by abstracting repeated steps into reusable units.
┌───────────────┐
│ Test Function │
└──────┬────────┘
       │ calls
┌──────▼────────┐
│ App Action Fn │
│ (wraps cmds) │
└──────┬────────┘
       │ queues
┌──────▼────────┐
│ Cypress Cmds  │
│ (cy.get,     │
│  cy.click)   │
└──────┬────────┘
       │ executed by
┌──────▼────────┐
│ Cypress Runner│
│ (async queue)│
└──────────────┘
Myth Busters - 4 Common Misconceptions
Quick: Do you think App Actions should include assertions inside them? Commit to yes or no.
Common Belief:App Actions should check if steps worked by including assertions inside the functions.
Tap to reveal reality
Reality:App Actions should only perform steps; assertions belong in tests to keep actions reusable and focused.
Why it matters:Mixing assertions in actions makes them less flexible and harder to reuse in different test scenarios.
Quick: Do you think writing App Actions makes tests slower? Commit to yes or no.
Common Belief:Adding App Actions adds overhead and slows down test execution.
Tap to reveal reality
Reality:App Actions are just wrappers around Cypress commands and do not slow tests; they improve maintainability without performance cost.
Why it matters:Avoiding App Actions due to false performance fears leads to messy, duplicated code.
Quick: Do you think App Actions replace the need to understand Cypress commands? Commit to yes or no.
Common Belief:Using App Actions means you don't need to know Cypress commands well.
Tap to reveal reality
Reality:Understanding Cypress commands is essential to write correct and effective App Actions.
Why it matters:Without Cypress knowledge, App Actions may hide bugs or misuse commands, causing flaky tests.
Quick: Do you think App Actions must be large functions covering many steps? Commit to yes or no.
Common Belief:App Actions should be big functions that do many things to reduce code size.
Tap to reveal reality
Reality:Smaller, focused App Actions are better for clarity, reuse, and debugging.
Why it matters:Large actions become hard to maintain and understand, defeating the pattern's purpose.
Expert Zone
1
App Actions can be combined with Cypress custom commands for even cleaner syntax and global reuse.
2
Parameterizing App Actions allows handling edge cases and different user roles without code duplication.
3
Keeping App Actions pure (no assertions, no test logic) enables easier mocking and stubbing in advanced test setups.
When NOT to use
Avoid App Actions for very simple tests or one-off scripts where abstraction adds unnecessary complexity. For UI elements that change frequently, consider using Page Object Model or component testing instead to better isolate UI structure.
Production Patterns
In real projects, teams organize App Actions by feature or page, often in separate files. They combine App Actions with fixtures for test data and use them in CI pipelines to ensure consistent, maintainable tests. Senior teams also integrate App Actions with reporting tools to trace user flows.
Connections
Page Object Model
Builds-on
App Actions focus on behaviors while Page Object Model organizes UI elements; combining both leads to highly maintainable test code.
Command Pattern (Software Design)
Same pattern
App Actions are an example of the Command Pattern, encapsulating requests as objects (functions) to decouple execution from invocation.
Instruction Manuals (Technical Writing)
Analogy in documentation
Just like clear instructions in manuals help users perform tasks step-by-step, App Actions provide clear, reusable steps for tests.
Common Pitfalls
#1Mixing assertions inside App Actions functions.
Wrong approach:function login() { cy.get('#username').type('user'); cy.get('#password').type('pass'); cy.get('#login').click(); cy.contains('Welcome').should('exist'); }
Correct approach:function login() { cy.get('#username').type('user'); cy.get('#password').type('pass'); cy.get('#login').click(); } // Assertions done separately in tests
Root cause:Confusing the role of actions (perform steps) with assertions (check results).
#2Duplicating Cypress commands in every test instead of using App Actions.
Wrong approach:cy.get('#username').type('user'); cy.get('#password').type('pass'); cy.get('#login').click(); // repeated in many tests
Correct approach:login('user', 'pass'); // Reuse the login action function
Root cause:Not recognizing the value of abstraction and reuse in test code.
#3Writing very large App Actions that do too many things.
Wrong approach:function fullUserSetup() { login(); fillProfile(); addPayment(); submitOrder(); }
Correct approach:Separate functions: login(), fillProfile(), addPayment(), submitOrder(); then combine in tests as needed.
Root cause:Trying to reduce code lines without considering readability and maintainability.
Key Takeaways
App Actions pattern organizes test steps into clear, reusable functions representing user or app behaviors.
Separating actions from assertions keeps tests flexible and easier to maintain.
Using App Actions reduces code duplication and improves test readability.
Parameterizing actions allows handling many scenarios with fewer functions.
Properly organizing App Actions supports scalable, reliable test suites in real projects.