If you’re a user of Aurelia and you’re writing unit tests, you might have run into some confusion around testing your custom elements, more specifically how you can mock an injected instance of something into a class.
In my case I had a Custom Element which allows me to display a select2 custom select within my application. I was also injecting the Element
itself and EventAggregator
onto the class using the @inject
class decorator.
At the time of writing this, with Aurelia being so new, this kind of information about dependency injection is hard to come by or not really that well explained yet. A few of the concepts in Aurelia are so new to Javascript, that there are not a lot of examples of testing them (especially within Aurelia).
I apologise in advance if this article doesn’t go into too much depth. I am making an assumption that you are already familiar with Aurelia, you have an understanding of decorators and other concepts that Aurelia uses to allow for your apps to be built.
I decided to share this because it took some serious digging through Aurelia’s internals to find out how I can handle injection on a Custom Element class, I couldn’t find any blog posts or documentation on this aspect (that isn’t to say it doesn’t exist, but I couldn’t find it).
For the context of this article, we are going to be working off of the premise of having a custom element class called CustomSelect (supplied below).
src/custom-element.js
import {EventAggregator} from 'aurelia-event-aggregator';
import {bindable, inject, customElement} from 'aurelia-framework';
import $ from 'jquery';
import 'select2';
import 'select2/css/select2.css!'
@customElement('select2')
@inject(Element, EventAggregator)
export class CustomSelect {
@bindable name = null;
@bindable selected = false;
@bindable handler = function() {};
@bindable options = {};
constructor(element, eventAggregator) {
this.element = element;
this.eventAggregator = eventAggregator;
}
attached() {
$(this.element).find('select').select2({
minimumResultsForSearch: -1,
width: '100%'
}).on('change', (event) => {
this.changed(event);
});
}
changed(event) {
var target = event.target;
this.eventAggregator.publish('select2-'+target.name+'', event);
}
}
Now lets write our unit test which will take into account the fact we are injecting Element
and the EventAggregator
for use within our test. We aren’t going for 100% coverage here, but more detailing how we can test custom elements with bindables as well.
test/unit/custom-element.spec.js
// Import our custom element class that we defined earlier
import {CustomSelect} from '../../../src/custom-select';
// Import container and BehaviorInstance for testing
import {Container} from 'aurelia-dependency-injection';
import {BehaviorInstance} from 'aurelia-templating';
describe('the custom select module', () => {
// References to containers and our element we define later on
var selectElement;
var container;
// Before each test, we set some things up
beforeEach(() => {
// Create a global container from the dependency-injection module
container = new Container().makeGlobal();
// Register an instance of Element and set it to be a DIV.
container.registerInstance(Element, '<div>');
// Using our BehaviorInstance we use the createForUnitTest method and supply our custom element created earlier
selectElement = BehaviorInstance.createForUnitTest(CustomSelect);
// You might want to add a class stub here so you can listen to events
selectElement.EventAggregator = {};
});
// A test-case that checks if default properties have been defined
// this just proves we have an instance and can test bindable values on the class
// These values are defined as @bindable in our custom element class
it('contains default properties', () => {
expect(selectElement.name).toEqual(null);
expect(selectElement.selected).toEqual(false);
expect(selectElement.handler).toBeDefined();
expect(selectElement.options).toEqual({});
});
});
Conclusion
This is just a taste-tester. I am working on a larger article that will go into detail testing parts of Aurelia like mocking http requests, attributes and other parts of Aurelia that aren’t well documented in the testing department.
Hi, I want to know is this the approach to follow when you are creating a stub for a class which has other classes like EventAggregator injected on it and also some custom user classes.
Could you provide some information on that?
Thanks,
Sagar
My code is not able to find the Container. I tried npm install aurelia-dependency-injection –save-dev. Is this still relevant?
@Michael Prescott, try importing it from ‘aurelia-framework’ instead