One of the biggest challenges we’ve faced in building our test suite is mocking services in acceptance tests. When you mock a service well, you come as close as possible to replicating the actual behavior of the service in question. You may want to do this because a service accesses an external source or an object that may not be available. It's difficult because you need to override the natively written code but have it mirror the functionality.
Lately, we've come up with a self-documenting, reusable pattern to make it easier for our team to write tests in the future. By making testing less intimidating and more transparent, it has encouraged our team to write more tests in each PR, this speeding up development time and causing less bugs!
It starts with creating a `tests/helpers/your-mock.js` which in turn, creates a setup and teardown function we import in our `tests/helpers/module-for-acceptance.js`.
As an example, we’ll create a simple mock that will stub out a flash notification service. This service can render flash notifications on a page after a successful or unsuccessful form submission.
In the code below we create a mock service with corresponding functions to notification types. All they do is push messages into a hash of corresponding queues called notifications. From there we register the mock service as `service:mockNotifications` and inject it into the application. Finally we create two functions called `setup` and `teardown` to keep our environment clean. In the future you should follow this blueprint for each service, each with a setup and teardown function.
Next, in the `module-for-acceptance`, we import the `setup` and `teardown` functions and execute them in the `beforeEach` and `afterEach` functions (which have been shortened for brevity). It’s important to notice that we assign the imported functions more specific names. This way we can have many `setup` and `teardown` functions within our `module-for-acceptance`, which is essential in complex environments with multiple mock services. Since we already created multiple mock files for each service, we can import as many as we need here, while still following a reusable blueprint.
Now all we need to do is test for the notifications! We import the previously defined variable, `notifications`, with its hash of queues and assert that it’s been filled with notifications and they have the right messages.
At SimpleReach, we use the testing framework QUnit for front end testing. Since it comes out of the box with Ember it is really easy to get going, has great documentation, and there is a community. Creating a custom assertion was wicked easy by following by the documented community best practices. For extra credit, you can create your own QUnit custom assertion following the below example. Below we create an assertion that checks if a message type exists in the imported notifications hash and has a specific message text. We also pass the test success message as well.
Finally we need to register our custom assertions before running our tests. We do this by running the exported `registerCustomAssertions` function in the `tests/test-helper` before running `start`. Now you save yourself two assertions, and your tests become more prescriptive! A win’s a win’s a win in my book.
This process is one we follow for all of our services. It allows us to have comprehensive acceptance tests and feel confident while making any refactors or creating new features. In the future, we hope to extract service mocks in integration tests to a similar reusable blueprint as seen in our acceptance tests.
About the author:
Andrew Freeman is a full stack engineer from New England masquerading as a New Yorker who enjoys doing first and learning later.