Reputation: 5239
I'm implementing some testing with Jasmine
in my web app. I'm using Coffeescript
to write my models, services and view models.
class MyViewModel
constructor: ( @options ) ->
@alert = new Alert
elementId: 'my-alert-element-id'
@service = new MyService
alertId: @alert.elementId
Now I write a test in jasmine
describe 'MyViewModel', ->
sut = null
beforeEach ->
sut = new MyViewModel()
afterEach ->
sut = null
describe 'constructor()', ->
it 'creates a new instance of the ViewModel', ->
expect( sut ).not.toBeNull()
So the problem here is that I've got dependencies on alert
and service
in my viewModel
. This is making the tests annoying to write and maintain.
Are there libraries out there for depency injection in Javascript. I've used several .net
libraries like castle windsor
and ninject
.
Or should I just be adopting a certain type pattern. I should say that I'm using knockout
and when I'm using the viewmodel in my actual application it looks something like this.
<script type="text/javascript">
$(function () {
var viewModel = new MyViewModel();
ko.applyBindings(viewModel);
});
</script>
Instead of me creating my own object I'd be asking the injection framework for an instance of MyViewModel
I assume.
I'm looking for advice on what pattern or library to adopt to make my testing a bit easier and help decoupling my javascript classes from each other.
Libs I've found:
EDIT: What I ended up doing
See coffeescript example of a test file
define ['squire', 'sinon' ], ( squire, sinon ) ->
describe '==== a view model ====', ->
sut = null
testContext = null
beforeEach ->
testContext =
squireInjector: new squire
stubService: sinon.stub()
stubJquery: sinon.stub()
someCallbackSpy: sinon.spy()
testContext.squireInjector.mock(
'jquery', squire.Helpers.returns( stubJquery ) )
testContext.squireInjector.require ['aViewModel'], ( viewModel ) =>
sut = new viewModel
service: testContext.stubService
someCallback: testContext.someCallbackSpy
waitsFor( -> sut? )
afterEach ->
sut = null
testContext = null
describe 'the constructor method should', ->
it 'create a new instance of the view
model and have required dependencies', ->
expect( sut ).toBeDefined
expect( sut.service ).toBeDefined
expect( sut.someCallback ).toBeDefined
describe 'the next method should', ->
it 'increment the route id by one', ->
# Arrange
sut.routeId = 5
# Act
sut.next()
# Assert
expect( sut.routeId ).toEqual( 6 )
expect( testContext.someCallbackSpy.called ).toBe(true)
Upvotes: 4
Views: 1466
Reputation: 6972
There is a library that provides very similar functionality to ninject
for Coffeescript, honk-di. Here's a helpful write up about it. Your example would become something more like this:
class MyViewModel
elementId: inject('element.id') # Inject a constant
alert: inject(Alert)
service: inject(MyService)
constructor: ->
@alert.elementId = @elementId
@service.alertId = @alert.elementId
Then, your tests will work just like they would with ninject
, Guice
, or similar. You describe your testing objects in a module/binder, and simply ask an injector for your class at test time.
describe 'MyViewModel', ->
sut = null
beforeEach ->
# Assuming you've made mocks or simplified classes for
# Alert and MyService which are set up in TestModule.
# `element.id` will also need a definition.
injector = new inject.Injector(new TestModule())
sut = injector.getInstance(MyViewModel)
afterEach ->
sut = null
describe 'constructor()', ->
it 'creates a new instance of the ViewModel', ->
expect( sut ).not.toBeNull()
Upvotes: 2
Reputation: 110972
You could use requirejs, and one of this solutions:
Or you just mock the prototypes of your Objects.
jasmine.spy(Alert.prototype, 'someFunction')
Btw. you can use wire.js with older browser using a shim. From the docs:
To support non-ES5 legacy browsers, wire.js 0.9.x requires poly 0.5.0 or higher. You can clone or download poly into your project, or install it via yeoman/bower:
Upvotes: 1