Loading
Chester Rivas
This is a live streamed presentation. You will automatically follow the presenter and see the slide they're currently on.
what is unit testing?
why should we unit test?
what tools do we use to unit test?
why do we need both karma and jasmine?
module.exports = function (config) {
config.set({
// base path that will be used to resolve all patterns (eg. files, exclude)
basePath: '',
// frameworks to use
// available frameworks: https://npmjs.org/browse/keyword/karma-adapter
frameworks: [
'jasmine',
'phantomjs-shim'
],
// test results reporter to use
// possible values: 'dots', 'progress'
// available reporters: https://npmjs.org/browse/keyword/karma-reporter
reporters: ['progress', 'coverage'],
// optionally, configure the reporter
coverageReporter: {
type: 'html',
dir: 'coverage'
},
// preprocess matching files before serving them to the browser
// available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
preprocessors: {
'app/views/**/*.html': 'ng-html2js',
'app/js/**/*.js': 'coverage'
},
junitReporter: {
outputFile: 'junit/test-results.xml',
suite: ''
},
// list of files / patterns to load in the browser
files: [
// bower library
'app/bower_components/angular/angular.js',
'app/bower_components/ui-router/release/angular-ui-router.js',
'app/bower_components/angular-mocks/angular-mocks.js',
// our app
'app/js/**/*.js',
// tests
'tests/unit-tests/**/*.js',
//location of templates
'app/views/**/*.html'
],
// list of files to exclude
exclude: [],
// web server port
port: 9876,
// enable / disable colors in the output (reporters and logs)
colors: true,
// level of logging
// possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// start these browsers
// available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, Karma captures browsers, runs the tests and exits
singleRun: true
});
};suite - a javascript function containing more suits or specs, this is represented with describe() method, the signature looks like this:
describe('in this factory', function() {
});spec - has similar signature to describe, it is represented by it() method and this is where you will make your assertions. The goal is for the tests to be human readable: ie: "it should do something"
it('should return correct sum', function() {
});idempotent - your test's shouldn't affect the next. Use teardown
stub/mock/spy - focus on testing ONE function and mocking dependencies.
var numberSquared = function(a) {
return a * a;
};
describe('math computations', function() {
it('should return the passed in number squared', function() {
expect(numberSquared(2)).toEqual(4);
expect(numberSquared(3)).toEqual(9);
expect(numberSquared(4)).toEqual(16);
});
});focusing - you can focus a suite or a spec by putting f in front: fit() and fdescribe(), now when you run karma it will only test the focused suites and specs (used to be iit and ddescribe)
ignoring - the opposite of what focusing does, putting an x in font: xit() and xdescribe()
afterEach - used to teardown your spec, so it doesn't effect the next test, will return literally after every spec
beforeEach - very frequently used, will run before every spec
describe('mathFactory', function () {
var mathFactory;
beforeEach(module('demoApp'));
beforeEach(inject(function ($injector) {
mathFactory = $injector.get('mathFactory');
}));
it('should have convertMinutesToHours method defined', function () {
expect(mathFactory.convertMinutesToHours).toBeDefined();
});
it('should return hours formatted correctly', function () {
expect(mathFactory.convertMinutesToHours(1)).toBe('0:01');
expect(mathFactory.convertMinutesToHours(122)).toBe('2:02');
expect(mathFactory.convertMinutesToHours(489)).toBe('8:09');
});
});describe('homeCtrl', function () {
var $scope,
$controller;
beforeEach(module('demoApp'));
beforeEach(inject(function ($injector) {
$scope = $injector.get('$rootScope').$new();
$controller = $injector.get('$controller');
$controller('homeCtrl', { $scope: $scope });
}));
it('should have $scope variables defined', function () {
expect($scope.hello).toBeDefined();
expect($scope.items).toBeDefined();
expect($scope.addItem).toBeDefined();
});
it('should increase the length of items array when calling addItem', function () {
expect($scope.items.length).toEqual(0);
$scope.addItem('unit testing')
expect($scope.items.length).toEqual(1);
});
});describe('hello directive', function () {
var $rootScope, $compile, markup, element, $directiveScope;
beforeEach(module('demoApp'));
beforeEach(inject(function ($injector) {
$rootScope = $injector.get('$rootScope');
$rootScope.madeUpName = 'Ace Ventura';
$compile = $injector.get('$compile');
markup = '<hello-world name="{{madeUpName}}"></hello-world>';
element = $compile(markup)($rootScope);
$directiveScope = element.isolateScope();
$rootScope.$digest();
}));
it('should have proper dom structure with the correct name', function () {
// less specific
expect(element.html()).toContain('Ace Ventura')
// super specific
expect(element.html()).toEqual('<h3 class="ng-binding">Hello my name is Ace Ventura</h3>');
});
});