Assertions

In general, most of your tests will probably assert against your app's UI. After visiting a route and interacting with the app, and after Mirage handles any requests, you'll assert that your UI is in the state you expect it to be.

But sometimes your app can have a buggy network request, even if the UI looks consistent.

To address this class of issues, you can assert against the state of your Mirage server within your tests, right alongside your UI assertions. This will give you more confidence that your JavaScript app is sending over the correct data to your backend.

Asserting against Mirage's database

The simplest way to assert that your app is sending over the correct data to your backend is to assert against Mirage's database. If the correct data makes it there, you'll have confidence not only that the JSON payloads from your JavaScript app are correct, but that your Mirage route handlers are behaving as you expect.

Here's an example using Cypress:

it("can change the movie title", function () {
  let movie = server.create("movie", { title: "Some movie" })

  cy.visit(`/movies/${movie.id}`)
    .contains("Edit")
    .click()
    .get("input.title")
    .type("Updated movie")
    .contains("Save")
    .click()

  // Assert against our app's UI
  cy.get("h1").should("contain", "Updated movie")

  // Also check that the data was "persisted" to our backend
  assert.equal(server.db.movies[0].title, "Updated movie")
})

This is a great way to gain some extra confidence that your app is sending over the data you expect.

Asserting against Mirage Models

It can also be useful to assert against Mirage's ORM models, to verify things like updates to your model's relationships:

it("can add a tag to a post", function () {
  let programming = server.create("tag", { name: "Programming" })
  let post = server.create("post")

  cy.visit(`/posts/${post.id}/edit`)
    .get(".tags")
    .select("Programming")
    .contains("Save")
    .click()

  cy.get(".toast").should("contain", "Saved!")
  expect(post.reload().tagIds).to.include(programming.id)
})

The reload method on Mirage models will rehydrate them with any new database data since they were instantiated, allowing you to verify that your route handler logic worked as expected.

Asserting against models is basically another way to verify Mirage's database data is correct.

Asserting against handled requests and responses

You can also assert against the actual HTTP requests and responses that are made during your test.

To do this, enable Mirage's request tracking feature using the trackRequests config option:

createServer({
  trackRequests: true,
})

Request tracking is disabled by default to avoid memory issues during long development sessions.

Now Mirage will track every request (along with the associated response) and make them available to you via server.pretender.handledRequests. That way you can assert against requests in that array at the end of your test.

it("can filter the table", function () {
  server.createList("movie", 5, { genre: "Sci-Fi" })
  server.createList("movie", 3, { genre: "Drama" })

  cy.visit("/").get(".tags").select("Sci-Fi")

  // Assert against our app's UI
  cy.get("tr.movie").should("have.length", 5)

  // Also assert against the HTTP request count & query
  let requests = server.pretender.handledRequests
  expect(requests).to.have.lengthOf(1)
  expect(requests[0].queryParams).to.deep.equal({ "filter[genre]": "Sci-Fi" })
})

In general we recommend asserting against Mirage's database and your UI, as the specifics of your app's HTTP requests should be considered implementation details of the behavior you're actually interested in verifying. But there are certainly valid reasons to drop down and assert against HTTP data.


And with that, you've completed the main portion of the guides! Read on to see some advanced use cases and configuration options, or head over to the API docs to learn more about Mirage's various classes.