Ember components. You know, those itty bitty isolated views of goodness that are often built up on top of other Ember components. It’s like Yehuda and Tom watched an episode of Pimp My Ride and got inspired by Xzibit’s approach to building a car inside of a car.

The TV blares:

Yo dawg, we put a car inside your car!

Yehuda wide-eyed nearly coughs up his Saturday morning cereal, “Tom, there’s something to this!“

Components have evolved into a foundational part of EmberJS. They offer nice boundaries for grouping related areas of behavior while casting the unrelated out.

Surely as Xzibit intended, components can utilize components which in turn can utilize components, so on and so forth. Who needs turtles (or cars for that matter) when we can have components, all the way down.

A common pattern in Ember apps is passing action references from one component, the parent component, in as a property of another component, a nested component sub-component. If we’re respecting component boundaries then the nested component is essentially a blackbox. We instantiate it and pass in a set of properties which may include action references. This is so the sub-component can notify upstream objects (e.g. components and controllers) of particular events going on. IF this sounds familiar, it should. It’s the Data Down Actions Up (DDAU) mantra that Ember espouses.

When a parent component instantiates a sub-component, it doesn’t know what will happen to the properties it passes in. If we pass in an action reference, the sub-component may call it directly, or it may even pass it down to another level of sub-components, which in turn may utilize it directly or pass it down yet another level.

Let’s assume that we’ve both mastered – or at least are comfortable with – creating and using Ember components. Now, let’s talk about testing, specifically the component actions.

Questions that come to mind when testing components that use components

Should we test the entire component stack individually, all at once, or both?

What happens if we have a component that gets used in several other components?

Should we test the base component’s behavior in all of the other components’ tests?

Often, the components that get used inside other components are straightforward and there isn’t much benefit to spending a lot of time dwelling over these questions. But, there are times when you either don’t want to duplicate the testing of base component behavior.

Why?

The base component may be complex enough that duplicating its set up is cumbersome, makes it harder to understand what we’re actually trying to test, and makes it require more effort (and time) to maintain.

This is getting a bit hypothetical, though.

Let’s walk through a concrete example

Here’s the ember twiddle for the code we’re going to walk through. We’re going to take a look at what a traditional component test looks like and then see how we can isolate the behavior we’re interested in testing from the behavior of sub-components.

Say we have a a todo-list component. This is going to be our parent component and it’s going to define the action, clicked , whose behavior we want to test.

Here’s the todo-list component JS:

Testing the entire component hierarchy

Here’s the todo-list component template:

The above template uses the todo-list-item child component and passes through a reference to the clicked action defined in our todo-list component.

Now, how can we test the clicked action? Keep in mind that in this example we want to observe the state of the DOM (which is why we are using a component integration test). Unfortunately, when using a component integration test we don’t get access to the in-memory component itself so we have to interact with the rendered content in some way to exercise the clicked action.

Here’s a test that renders the entire component hierarchy and relies on interacting with content/behavior from the todo-list and the todo-list-item components:

For the sake of walking through a straightforward example both components here are dead simple. The test itself is pretty simple.

But what happens if the todo-list-item component were used in five other components? What if the todo-list-item component had more complex logic? Would we want every test that we wrote for a parent component to also test the todo-list-item component each time?

When the answer is yes then the above test works great, but when the answer is no then we may want to separate testing the todo-list-item component behavior from the parent component.

Stubbing Services For more information on stubbing services in Ember check out the Stubbing Services section in the Testing Components guide.

Separating component behavior from sub-component behavior in tests

This is where this next test comes into play. Instead or relying on the real todo-list-item component it takes a different approach by essentially stubbing out the todo-list-item component and template. This allows the test to provide the simplest component to be used so we can show that the clicked action is working when called from a sub-component. After all, what we’re interested in is that the clicked action is doing the right thing.

The key to this test is the first few lines that call register . We’re basically stubbing out the todo-list-item component JS and template and providing our own simple implementation.

Component Dependency Injection The approach used in this article is known as Dependency Injection, or DI for short. This is a fancy term for allowing us to create and provide the dependencies of another object. The register helper function provided by Ember lets us stub out the template:components/todo-list-item template and provide our own dummy handlebars template. A related, yet different, way to approach this is described in the Component Dependency Injection for Testing in Ember.js post from our friends at Dockyard. An important distinction between the two approaches, though, is that Dockyard’s approach works at the template level. For example, they are changing the arguments they pass to the component as a new data construct – a hash. With the approach used in this article you don’t need to do that, but you do need to have knowledge of what sub-components are used in order to stub them out. The benefit of the Dockyard approach is that things are more black-boxed from the test’s perspective. But a downside is that you are required to invent a new convention for how components are typically constructed, adding additional complexity and overhead.

Why?

This lets us ignore how todo-list-item actually works and instead focus on the behavior of the clicked action in our parent component. Our test shows that when we pass through a reference to the clicked action, and when a sub-component calls it, the clicked action did what we expect.

Whether or not this approach is right for your app is something you’ll have to decide. It’s another tool in the toolbox if you find you need it.

Happy coding!