Skip to main content

Vue Components with Props

Mounting Your First Component

Simple components with few environmental dependencies will be easiest to test.

Start with a <Button>, a <Stepper>, or the <HelloWorld> component that comes with the initial Vue project scaffold. Here are a few examples of similar components that will be easier or harder to test.

Easier-to-testHarder-to-test
A Presentational ComponentA Presentational Component w/ Vuetify
Layout Component w/ SlotsLayout Component w/ Vue Router
Product List with PropsProduct List w/ Pinia or Vuex

In this guide, we'll use a <Stepper/> component with zero dependencies and one bit of internal state -- a simple "counter" that can be incremented and decremented by two buttons.

We'll add one prop, called initial to set the initial value in the <Stepper>.

Then, we'll emit a custom "change" event whenever the user clicks on the increment and decrement buttons. Testing this is covered in "Testing Emitted Events".

We'll start simple and build out our Stepper as we go.

it("renders the Stepper, with a default of 0", () => {
cy.mount(Stepper);
});

Testing a Component with Props

We're going to add functionality to our Stepper.vue component.

We want to be able to control the initial value of the stepper. To do this, we'll declare initial as an optional prop and test that the prop is used by the component.

To test this component, we'll exercise the Stepper's API and validate that the initial prop sets the internal count state for the first time.

it('supports an "initial" prop to set the value', () => {
cy.mount(Stepper, { props: { initial: 100 } });
cy.get("[data-testid=stepper]").should("contain.text", 100);
});
info

Depending on your Vue version, the syntax for how to mount your component will change slightly. Please always refer to the Vue Test Utils documentation for the latest syntax when using the mount function's Object API.

The main difference is that "props" should be "propsData" for Vue 2 applications.

What Else Should You Test in This Component?

We should also test that the initial prop is used to set the counter, which is reactive and should change when the increment and decrement buttons are clicked.

If we didn't interact with the component, our test wouldn't catch various logic issues -- such as using {{ initial }} in the template, or hard-coding the template to be 100.

You'll notice that we're talking about technical, Vue-specific concepts. A well-written, comprehensive test for our Stepper component can instead be done by approaching this test like user would.

Don't think about data, methods, or emitted events. Think solely about the UI and use your test to automate what you would naturally do as a user.

This way, you'll test the component thoroughly, without getting bogged down in the technical details. At the end of the day, all that matters is that if the developer uses the component with a given API, the end user will be able to use it as expected.

Now, let's render and interact with the Stepper component as a user would!

  1. First, ensure that the initial value of the Stepper is correct
const stepperSelector = "[data-testid=stepper]";

it("supports an initial prop", () => {
cy.mount(Stepper, { props: { initial: 100 } });
cy.get(stepperSelector).should("contain.text", 100);
});
  1. Next, ensure that you can increment and decrement the Stepper by clicking on the correct buttons.
const stepperSelector = "[data-testid=stepper]";
const incrementSelector = "[aria-label=increment]";
const decrementSelector = "[aria-label=decrement]";

it("can be incremented", () => {
cy.mount(Stepper);
cy.get(incrementSelector).click();
cy.get(stepperSelector).should("contain.text", 1);
});

it("can be decremented", () => {
cy.mount(Stepper);
cy.get(decrementSelector).click();
cy.get(stepperSelector).should("contain.text", -1);
});
  1. Finally, run through the behavior of the Stepper as a user would. There is duplication of coverage here -- but that's okay because it exercises the component in a more real-world usage. This test is more likely to fail if there are any issues in the component, not just with specific buttons or text rendered.
const stepperSelector = "[data-testid=stepper]";
const incrementSelector = "[aria-label=increment]";
const decrementSelector = "[aria-label=decrement]";

it("has an initial counter that can be incremented and decremented", () => {
cy.mount(Stepper, { props: { initial: 100 } });
cy.get(stepperSelector).should("contain.text", 100);
cy.get(incrementSelector).click();
cy.get(stepperSelector).should("contain.text", 101);
cy.get(decrementSelector).click().click();
cy.get(stepperSelector).should("contain.text", 99);
});
info

Vue 2 vs. Vue 3

Please refer to the Vue Test Utils migration guide for minor differences in the mount function's signature.

In general, this guide will be using Vue 3 syntax.

Learn More

The Introduction to Cypress guide goes deeper into how to write tests with Cypress.

What's Next?

We're going to emit a custom event from our Stepper component.