Angular: basic testing with injection

So I'm new to the whole testing thing (I've been one of those people who has said 'I should write unit tests...' but never ended up ever doing it :-p). I'm now writing unit tests for this project.  I'm using testacular + Jasmine, with browserify to compile things.  I was having no problems until I started trying to do a lot angular-injection-stuff.

Right now I'm simply trying to do a test of ng-model to get my head around all of it.

I have a testacular.conf file which includes everything necessary:

files = [
    '../lib/jquery.js',
    '../lib/angular.js',
    './lib/jasmine.js',
    './lib/angular-mocks.js',
    JASMINE_ADAPTER,
    './tests.js' //compiled by browserify
];

I have my controller defined (MainCtrl.coffee)

MainCtrl = ($scope, $rootScope) ->
    $scope.hello = 'initial'

module.exports = (angularModule) ->
    angularModule.controller 'MainCtrl', ['$scope', '$rootScope', MainCtrl]
    return MainCtrl

And I have my test itself: (_MainCtrlTest.coffee, in same directory as MainCtrl.coffee)

testModule = angular.module 'MainCtrlTest', []
MainCtrl = require('./MainCtrl')(testModule)

describe 'MainCtrlTest', ->
    scope = null
    elm = null
    ctrl = null

    beforeEach inject ($rootScope, $compile, $controller) ->
        scope = $rootScope.$new()
        ctrl = $controller MainCtrl, $scope: scope
        elm = $compile('<input ng-model="hello"/>')(scope)

    describe 'value $scope.hello', ->

        it 'should initially equal input value', ->
            expect(elm.val()).toBe scope.hello

        it 'should change when input value changes', ->
            scope.$apply -> elm.val('changedValue')
            expect(scope.hello).toBe elm.val()

The test fails immediately, with the input's elm.val() returning blank, and scope.hello returning the intended value ('initial', set in MainCtrl.coffee)

What am I doing wrong here?

To get this working, you need to do scope.$apply():

it 'should initially equal input value', ->
  scope.$apply()
  expect(elm.val()).toBe scope.hello

Don't test the framework, test your code

Your test is trying to test whether Angular's binding, and ng-model works. You should rather trust the framework and test your code instead.

Your code is:

  1. the controller (setting initial scope.hello value)
  2. html templates (and all the binding, directives in there)

You can test the first one very easily, without even touching any DOM. That's the beauty of AngularJS - strong separation of view/logic.

In this controller, there is almost nothing to test, but the initial value:

it 'should init hello', ->
  expect(scope.hello).toBe 'initial'

To test the second one (template + binding), you want to do an e2e test. You basically want to test, whether the template doesn't contain any typos in binding etc... So you wanna test the real template. If you inline a different html during the test, you are testing nothing but AngularJS.