Headless knockout viewmodel testing with mocha
Asked Answered
D

2

12

I am trying to do headless testing of my knockout viewmodels. I purposely avoid dealing with any ui constructs in my viewmodel and leave the wireup to the html page.

This works great in Jasmine since it runs in the browser, but when I switch to mocha, I end up running head-first into the last line on knockout which is:

})(window,document,navigator);

I've looked at using zombiejs which would be a nice alternative, but I don't see a good story on how to use it without changing the knockout source itself.

Any thoughts on how to approach this?

Demulsify answered 26/6, 2012 at 15:4 Comment(7)
What features of knockout do you need in your view models? If it's the observables only, then mock them and ignore the rest of knockout during testing the VM part.Antoinetteanton
The issue is that even with the debug version, the initialization of the knockout.js script (v2.1.0) errors on a call to navigator.userAgent. Therefore, I can't get to the point of testing the viewmodel and observable/computed/pubsub portions.Demulsify
I think zombie will be your best best as it simulates the dom. Knockout was written as a DOM-Model binder, not a generic model binder. Perhaps backbone might be more aligned to your style of development. Glue.js is also a nice event binder.Flummox
Glue and Zombie would work only with modifications to the knockout source or some type of helper. Even then, zombie does not mock out the window.navigator to the extent that knockout uses it.Demulsify
I also posted a reference to this question on the knockout google group encouraging folks to post here: groups.google.com/forum/?fromgroups#!topic/knockoutjs/…Demulsify
Did you look at casperjs.org already?Intuit
With the new knockout, this won't be an issue.Carola
P
5

This is a topic currently on my radar as well. I'll dump my findings here in the hopes that they might point you in the right direction.

The likely route I will attempt first will be PhantomJS. It's a headless WebKit browser, so it should have excellent DOM, JSON, HTML5, and CSS selectors support (it works with jQuery and qUnit, for example).

I chose this because it is used by knockout.js itself, which I discovered in the knockout.js repository, where there was a .travis.yml file and this comment:

enter image description here

I don't have any proof that this is going to work, but was encouraged by its use in knockout.js core. I also found this runner script for knockout/phantom that looks like a great launch point.

I've also found a few examples using Mocha and PhantomJS via node.js, including this lib extending grunt to run mocha inside Phantom, and this script showing how to run mocha inside PhantomJS. So that part is certain, at the very least.

Another solution noted in the knockoutjs archives, is to use knockout-node and JsDOM to create a workable DOM, but at first glance, this seemed too nebulous and likely to result in implementing your own test environment.

There is a slidedeck suggesting zombie.js would work with knockout/node/etc. But I can't find anything offering hard evidence, so I didn't like this route either.

Play answered 4/7, 2012 at 18:26 Comment(1)
@JohnRayner would you mind posting a description of your workflow?Endometriosis
M
2

Maybe this is because Knockout has changed (as the accepted answer is old), but today, I don't believe this is necessary (anymore). You can easily test a Knockout viewmodel. All I needed to do was set the global ko variable in my test:

global.ko = require('../../Website/Scripts/knockout-3.4.0.js');

After that, you can run your test as usual: instantiate your viewmodel, perform any operations on it and assert.

I've written a little more about it, but in essence, this works for me:

global.ko = require('../../Website/Scripts/knockout-3.4.0.js');

var MyViewModel = require('../../Website/Scripts/myViewModel.js').MyViewModel;

describe('MyViewModel', function() {
    var viewModel;

    beforeEach(function(){
        viewModel = new MyViewModel();
    });

    describe('...', function() {
        /* And so on */
    });
});
Monosyllabic answered 26/9, 2016 at 10:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.