{
  "doc": {
    "id": "app/references/trade-offs",
    "title": "Trade-offs in Cypress",
    "description": "Learn about the trade-offs you make when using Cypress, and how to work around them.",
    "section": "app",
    "source_path": "/llm/markdown/app/references/trade-offs.md",
    "version": "ce02913654e2655ee63448bdc92bb92c7b46a619",
    "updated_at": "2026-04-22T19:37:51.587Z",
    "headings": [
      {
        "id": "app/references/trade-offs#trade-offs",
        "text": "Trade-offs",
        "level": 1
      },
      {
        "id": "app/references/trade-offs#permanent-trade-offs-overview",
        "text": "Permanent trade-offs Overview",
        "level": 2
      },
      {
        "id": "app/references/trade-offs#temporary-trade-offs-overview",
        "text": "Temporary trade-offs Overview",
        "level": 2
      },
      {
        "id": "app/references/trade-offs#permanent-trade-offs",
        "text": "Permanent trade-offs",
        "level": 2
      },
      {
        "id": "app/references/trade-offs#automation-restrictions",
        "text": "Automation restrictions",
        "level": 3
      },
      {
        "id": "app/references/trade-offs#inside-the-browser",
        "text": "Inside the browser",
        "level": 3
      },
      {
        "id": "app/references/trade-offs#multiple-browsers-open-at-the-same-time",
        "text": "Multiple browsers open at the same time",
        "level": 3
      },
      {
        "id": "app/references/trade-offs#1-use-only-the-browser",
        "text": "1. Use only the browser:",
        "level": 4
      },
      {
        "id": "app/references/trade-offs#2-stub-the-other-connection",
        "text": "2. Stub the other connection:",
        "level": 4
      },
      {
        "id": "app/references/trade-offs#3-introduce-another-connection",
        "text": "3: Introduce another connection:",
        "level": 4
      }
    ]
  },
  "content": {
    "type": "root",
    "children": [
      {
        "type": "heading",
        "depth": 1,
        "children": [
          {
            "type": "text",
            "value": "Trade-offs"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Cypress automates the browser with its own unique architecture. While this\nunlocks the power to do things you will not find anywhere else, there are\nspecific trade-offs that are made."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "In this guide we'll lay out what some of the trade-offs are - and specifically\nhow you can work around them."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "While at first it may seem like these are strict limitations in Cypress - we\nthink you will soon realize that many of these boundaries are actually good\nto have. In a sense they prevent you from writing bad, slow, or flaky tests."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Permanent trade-offs Overview"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Cypress is not a general purpose "
                  },
                  {
                    "type": "link",
                    "title": null,
                    "url": "#Automation-restrictions",
                    "children": [
                      {
                        "type": "text",
                        "value": "automation tool"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": "."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Cypress commands run "
                  },
                  {
                    "type": "link",
                    "title": null,
                    "url": "#Inside-the-browser",
                    "children": [
                      {
                        "type": "text",
                        "value": "inside of a browser"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": "."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "You cannot use Cypress to drive\n"
                  },
                  {
                    "type": "link",
                    "title": null,
                    "url": "#Multiple-browsers-open-at-the-same-time",
                    "children": [
                      {
                        "type": "text",
                        "value": "two browsers at the same time"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": "."
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Each test is bound to a single superdomain. Cross-origin navigation inside\ntests can be enabled by using the "
                  },
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/origin.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "`cy.origin`"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": " command.\nPlease read our\n"
                  },
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/app/guides/cross-origin-testing.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "Cross Origin Testing Guide"
                      }
                    ]
                  },
                  {
                    "type": "text",
                    "value": "."
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Temporary trade-offs Overview"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "We want to highlight some temporary restrictions that Cypress hopes to\neventually address."
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "/llm/markdown/api/commands/hover.md",
                    "children": [
                      {
                        "type": "text",
                        "value": "Workarounds for the lack of a `cy.hover()` command."
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "https://github.com/cypress-io/cypress/issues/311#issuecomment-339824191",
                    "children": [
                      {
                        "type": "text",
                        "value": "There is not any native or mobile events support."
                      }
                    ]
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "link",
                    "title": null,
                    "url": "https://github.com/cypress-io/cypress/issues/136",
                    "children": [
                      {
                        "type": "text",
                        "value": "iframe support is somewhat limited, but does work."
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "heading",
        "depth": 2,
        "children": [
          {
            "type": "text",
            "value": "Permanent trade-offs"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Automation restrictions"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The Cypress App is a specialized tool that does end-to-end, component, and API\ntesting really well. We don't think Cypress is the optimal tool for things such as:"
          }
        ]
      },
      {
        "type": "list",
        "ordered": false,
        "start": null,
        "spread": false,
        "children": [
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Indexing the web"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Spidering links"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Performance testing"
                  }
                ]
              }
            ]
          },
          {
            "type": "listItem",
            "spread": false,
            "checked": null,
            "children": [
              {
                "type": "paragraph",
                "children": [
                  {
                    "type": "text",
                    "value": "Scripting 3rd party sites"
                  }
                ]
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "There are other excellent tools that are optimized for doing each item listed\nabove."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The sweet spot of Cypress is to be used as a tool to test your own\napplication."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Inside the browser"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "In case you missed it before - Cypress tests run inside of the browser! This\nmeans we can do things nobody else can. There is no object serialization or JSON\nwire protocols. You have real, native access to everything in your application\nunder test."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "But this also means that your test code is being evaluated inside the\nbrowser. Test code is not evaluated in Node, or any other server side\nlanguage. The only language we'll ever support is the language of the web:\nJavaScript."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This trade-off means it makes it a little bit more challenging to communicate with the\nback end - like your server or database. You will not be able to connect or\n`import` those server-side libraries or modules directly. Although you can require\n`node_modules` which can be used in the browser. Additionally, you have the\nability to use Node to import or talk directly to your back end scripts using\n"
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/node-events/overview.md",
            "children": [
              {
                "type": "text",
                "value": "our Node Events"
              }
            ]
          },
          {
            "type": "text",
            "value": " or\n"
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/task.md",
            "children": [
              {
                "type": "text",
                "value": "cy.task()"
              }
            ]
          },
          {
            "type": "text",
            "value": "."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "To talk to your database or server you need to use the\n"
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/exec.md",
            "children": [
              {
                "type": "text",
                "value": "`cy.exec()`"
              }
            ]
          },
          {
            "type": "text",
            "value": ", "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/task.md",
            "children": [
              {
                "type": "text",
                "value": "`cy.task()`"
              }
            ]
          },
          {
            "type": "text",
            "value": ", or\n"
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/request.md",
            "children": [
              {
                "type": "text",
                "value": "`cy.request()`"
              }
            ]
          },
          {
            "type": "text",
            "value": " commands. That means you will need to\nexpose a way to seed and setup your database. It might take a bit more elbow grease\nthan other testing tools written in your back end language."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "The trade-off here is that doing everything in the browser is a much better\nexperience in Cypress. But doing things outside of\nthe browser may take a little extra work."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 3,
        "children": [
          {
            "type": "text",
            "value": "Multiple browsers open at the same time"
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Cypress does not support controlling more than 1 open browser at a time.\nYou can test multiple tabs, however, using our "
          },
          {
            "type": "link",
            "title": null,
            "url": "https://github.com/cypress-io/cypress/tree/develop/npm/puppeteer",
            "children": [
              {
                "type": "text",
                "value": "@cypress/puppeteer"
              }
            ]
          },
          {
            "type": "text",
            "value": " plugin."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "With that said, except in the most unusual and rare circumstances, you can still\ntest most application behavior without opening multiple browsers at the same\ntime."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "You may ask about this functionality like this:"
          }
        ]
      },
      {
        "type": "blockquote",
        "children": [
          {
            "type": "paragraph",
            "children": [
              {
                "type": "text",
                "value": "I'm trying to test a chat application. Can I run more than one browser at a\ntime with Cypress?"
              }
            ]
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Whether you are testing a chat application or anything else - what you are\nreally asking about is testing collaboration. But, you don't need to recreate\nthe entire environment in order to test collaboration with 100% coverage."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Doing it this way can be faster, more accurate, and more scalable."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "While outside the scope of this article, you could test a chat application using\nthe following principles. Each one will incrementally introduce more\ncollaboration:"
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "1. Use only the browser:"
          }
        ]
      },
      {
        "type": "code",
        "lang": "text",
        "meta": null,
        "value": "    ↓\n← browser →\n    ↑"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Avoid the server, invoke your JavaScript callbacks manually thereby simulating\nwhat happens when \"notifications come in\", or \"users leave the chat\" purely in\nthe browser."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "You can "
          },
          {
            "type": "link",
            "title": null,
            "url": "/llm/markdown/api/commands/stub.md",
            "children": [
              {
                "type": "text",
                "value": "stub"
              }
            ]
          },
          {
            "type": "text",
            "value": " everything and simulate every single\nscenario. Chat messages, offline messages, connections, reconnections,\ndisconnections, group chat, etc. Everything that happens inside of the browser\ncan be fully tested. Requests leaving the browser could also be stubbed and you\ncould assert that the request bodies were correct."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "2. Stub the other connection:"
          }
        ]
      },
      {
        "type": "code",
        "lang": "text",
        "meta": null,
        "value": "server → browser\n            ↓\nserver ← browser\n  ↓\n(other connections stubbed)\n  ↓\nserver → browser"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Use your server to receive messages from the browser, and simulate \"the other\nparticipant\" by sending messages as that participant. This is certainly\napplication specific, but generally you could insert records into the database\nor do whatever it takes for your server to act as if a message of one client\nneeds to be sent back to the browser."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "Typically this pattern enables you to avoid making a secondary WebSocket\nconnection and yet still fulfills the bidirectional browser and server contract.\nThis means you could also test edge cases (disconnections, etc) without actually\nhandling real connections."
          }
        ]
      },
      {
        "type": "heading",
        "depth": 4,
        "children": [
          {
            "type": "text",
            "value": "3: Introduce another connection:"
          }
        ]
      },
      {
        "type": "code",
        "lang": "text",
        "meta": null,
        "value": "server → browser\n            ↓\nserver ← browser\n  ↓\nserver → other connection\n            ↓\nserver ← other connection\n  ↓\nserver → browser"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "To do this - you would need a background process outside of the browser to make\nthe underlying WebSocket connection that you can then communicate with and\ncontrol."
          }
        ]
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "You can do this in many ways and here is an example of using an HTTP server to\nact as the client and exposing a REST interface that enables us to control it."
          }
        ]
      },
      {
        "type": "code",
        "lang": "js",
        "meta": null,
        "value": "// Cypress tests\n\n// tell the http server at 8081 to connect to 8080\ncy.request('http://localhost:8081/connect?url=http://localhost:8080')\n\n// tell the http server at 8081 to send a message\ncy.request('http://localhost:8081/message?m=hello')\n\n// tell the http server at 8081 to disconnect\ncy.request('http://localhost:8081/disconnect')"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "And the HTTP server code would look something like this..."
          }
        ]
      },
      {
        "type": "code",
        "lang": "js",
        "meta": null,
        "value": "const client = require('socket.io:client')\nconst express = require('express')\n\nconst app = express()\n\nlet socket\n\napp.get('/connect', (req, res) => {\n  const url = req.query.url\n\n  socket = client(url)\n\n  socket.on('connect', () => {\n    res.sendStatus(200)\n  })\n})\n\napp.get('/message', (req, res) => {\n  const msg = req.query.m\n\n  socket.send(msg, () => {\n    res.sendStatus(200)\n  })\n})\n\napp.get('/disconnect', (req, res) => {\n  socket.on('disconnect', () => {\n    res.sendStatus(200)\n  })\n\n  socket.disconnect()\n})\n\napp.listen(8081, () => {})"
      },
      {
        "type": "paragraph",
        "children": [
          {
            "type": "text",
            "value": "This avoids ever needing a second open browser, but still gives you an\nend-to-end test that provides 100% confidence that the two clients can\ncommunicate with each other."
          }
        ]
      }
    ]
  },
  "token_estimate": 1497
}