在 Karma/Jasmine 中为使用 cli 和 grunt 运行的 Angular 应用程序设置单元测试来完成我们的繁琐工作。
首先,您需要安装 grunt,但从现在开始,我将跳过这些废话,只为您提供或多或少的副本,了解我们如何进行单元测试。顺便说一句,效果很棒 - 我完全是单元测试狂热的一部分 - Gooble Gabble Gooble Gabble!
包.json:
{
"name": "Kinetix-Store-Frontend",
"version": "0.0.1",
"devDependencies": {
"findup-sync": "^0.1.3",
"grunt-autoprefixer": "~0.7.3",
"grunt-bower-install": "~1.4.0",
"grunt-concurrent": "~0.5.0",
"grunt-contrib-clean": "~0.5.0",
"grunt-contrib-compass": "~0.7.2",
"grunt-contrib-concat": "~0.4.0",
"grunt-contrib-connect": "~0.7.1",
"grunt-contrib-copy": "~0.5.0",
"grunt-contrib-cssmin": "~0.9.0",
"grunt-contrib-htmlmin": "~0.2.0",
"grunt-contrib-jshint": "~0.10.0",
"grunt-contrib-sass": "~0.7.3",
"grunt-contrib-uglify": "~0.4.0",
"grunt-contrib-watch": "~0.6.1",
"grunt-html-snapshot": "~0.6.1",
"grunt-karma": "~0.8.3",
"grunt-newer": "~0.7.0",
"grunt-ng-annotate": "^0.3.0",
"grunt-rev": "~0.1.0",
"grunt-svgmin": "~0.4.0",
"grunt-usemin": "~2.1.1",
"jasmine-reporters": "~0.4.1",
"jshint-stylish": "~0.1.5",
"karma": "~0.12.14",
"karma-jasmine": "~0.2.2",
"karma-junit-reporter": "~0.2.2",
"karma-ng-html2js-preprocessor": "~0.1.0",
"karma-phantomjs-launcher": "~0.1.4",
"load-grunt-tasks": "~0.4.0",
"time-grunt": "~0.3.1"
},
"engines": {
"node": ">=0.8.0"
},
"dependencies": {
"grunt": "^0.4.5"
}
}
Karma.conf.js
// Karma configuration
// http://karma-runner.github.io/0.10/config/configuration-file.html
module.exports = function (config) {
config.set({
// base path, that will be used to resolve files and exclude
basePath: '',
// testing framework to use (jasmine/mocha/qunit/...)
frameworks: ['jasmine'],
preprocessors: {
'app/views/templates/*.tpl.html': ['ng-html2js']
},
// list of files / patterns to load in the browser
files: [
'app/bower_components/lodash/dist/lodash.js',
'app/bower_components/angular/angular.js',
'app/bower_components/angular-mocks/angular-mocks.js',
'app/bower_components/angular-resource/angular-resource.js',
'app/bower_components/angular-cookies/angular-cookies.js',
'app/bower_components/angular-sanitize/angular-sanitize.js',
'app/bower_components/angular-bootstrap/ui-bootstrap.js',
'app/bower_components/angular-ui-router/release/angular-ui-router.js',
'app/bower_components/angular-local-storage/angular-local-storage.js',
'app/bower_components/jquery/dist/jquery.js',
'app/bower_components/bootstrap/dist/js/bootstrap.js',
'app/bower_components/lodash/dist/lodash.compat.js',
'app/scripts/*.js',
'app/scripts/**/*.js',
'test/spec/**/*.js',
//Templates
'app/views/templates/*.tpl.html'
],
ngHtml2JsPreprocessor: {
stripPrefix: 'app/',
moduleName: 'Kinetix.Templates'
},
// list of files / patterns to exclude
exclude: [],
// Reporters
reporters: ['progress', 'junit'],
//Config for junit
junitReporter: {
outputFile: './test/test-results.xml',
suite: ''
},
// web server port
port: 9001,
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel: config.LOG_INFO,
// enable / disable watching file and executing tests whenever any file changes
autoWatch: false,
// Start these browsers, currently available:
// - Chrome
// - ChromeCanary
// - Firefox
// - Opera
// - Safari (only Mac)
// - PhantomJS
// - IE (only Windows)
browsers: ['PhantomJS'],
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun: false
});
};
GruntFile.js - ngMin 应该是 ngAnnotate 但我现在正在解决这个问题。如您所见,这是由 Yeoman 的 Angular Generator 为我们制作的 - 非常棒!推荐....
// Generated on 2014-01-16 using generator-angular 0.7.1
'use strict';
// # Globbing
// for performance reasons we're only matching one level down:
// 'test/spec/{,*/}*.js'
// use this if you want to recursively match all subfolders:
// 'test/spec/**/*.js'
module.exports = function (grunt) {
// Load grunt tasks automatically
require('load-grunt-tasks')(grunt);
// Time how long tasks take. Can help when optimizing build times
require('time-grunt')(grunt);
// Define the configuration for all the tasks
grunt.initConfig({
// Project settings
yeoman: {
// configurable paths
app: require('./bower.json').appPath || 'app',
dist: 'dist',
test: 'test'
},
configureRewriteRules: {
options: {
rulesProvider: 'connect.rules'
}
},
// Watches files for changes and runs tasks based on the changed files
watch: {
js: {
files: ['<%= yeoman.app %>/scripts/{,*/}*.js'],
tasks: ['newer:jshint:all'],
options: {
livereload: true
}
},
jsTest: {
files: ['test/spec/{,*/}*.js'],
tasks: ['newer:jshint:test', 'karma']
},
styles: {
files: ['<%= yeoman.app %>/styles/{,*/}*.css'],
tasks: ['newer:copy:styles', 'autoprefixer']
},
gruntfile: {
files: ['Gruntfile.js']
},
sass: {
files: ['**/*.{scss,sass}'],
tasks: ['sass:dev']
}
},
// The actual grunt server settings
connect: {
options: {
port: 9000,
// Change this to '0.0.0.0' to access the server from outside.
hostname: '0.0.0.0',
keepalive: true
},
livereload: {
options: {
open: true,
base: [
'<%= yeoman.app %>'
]
}
},
test: {
options: {
port: 9001,
base: [
'.tmp',
'test',
'<%= yeoman.app %>'
]
}
},
dist: {
options: {
open: true,
base: ['<%= yeoman.dist %>'],
keepalive: true
}
}
},
// Make sure code styles are up to par and there are no obvious mistakes
jshint: {
options: {
jshintrc: '.jshintrc',
reporter: require('jshint-stylish')
},
all: [
'Gruntfile.js',
'<%= yeoman.app %>/scripts/{,*/}*.js'
],
test: {
options: {
jshintrc: 'test/.jshintrc'
},
src: ['test/spec/{,*/}*.js']
}
},
// Empties folders to start fresh
clean: {
dist: {
files: [
{
dot: true,
src: [
'.tmp',
'<%= yeoman.dist %>/*',
'!<%= yeoman.dist %>/.git*'
]
}
]
},
server: '.tmp'
},
// Add vendor prefixed styles
autoprefixer: {
options: {
browsers: ['last 1 version']
},
dist: {
files: [
{
expand: true,
cwd: '.tmp/styles/',
src: '{,*/}*.css',
dest: '.tmp/styles/'
}
]
}
},
// Automatically inject Bower components into the app
'bowerInstall': {
app: {
src: ['<%= yeoman.app %>/index.html'],
ignorePath: '<%= yeoman.app %>/'
}
},
// Renames files for browser caching purposes
rev: {
dist: {
files: {
src: [
'<%= yeoman.dist %>/scripts/*.js',
'<%= yeoman.dist %>/styles/*.css',
'<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}',
'<%= yeoman.dist %>/styles/fonts/*'
]
}
}
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
html: '<%= yeoman.app %>/index.html',
options: {
dest: '<%= yeoman.dist %>'
}
},
// Performs rewrites based on rev and the useminPrepare configuration
usemin: {
html: ['<%= yeoman.dist %>/{,*/}*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css'],
options: {
assetsDirs: ['<%= yeoman.dist %>']
}
},
// The following *-min tasks produce minified files in the dist folder
imagemin: {
dist: {
files: [
{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.{png,jpg,jpeg,gif}',
dest: '<%= yeoman.dist %>/images'
}
]
}
},
svgmin: {
dist: {
files: [
{
expand: true,
cwd: '<%= yeoman.app %>/images',
src: '{,*/}*.svg',
dest: '<%= yeoman.dist %>/images'
}
]
}
},
htmlmin: {
dist: {
options: {
collapseWhitespace: true,
collapseBooleanAttributes: true,
removeCommentsFromCDATA: true,
removeOptionalTags: true
},
files: [
{
expand: true,
cwd: '<%= yeoman.dist %>',
src: ['*.html', 'views/{,*/}*.html'],
dest: '<%= yeoman.dist %>'
}
]
}
},
// Replace With Google CDN references
cdnify: {
dist: {
html: ['<%= yeoman.dist %>/*.html']
}
},
// Copies remaining files to places other tasks can use
copy: {
dist: {
files: [
{
expand: true,
dot: true,
cwd: '<%= yeoman.app %>',
dest: './.tmp/',
src: [
'*.{ico,png,txt}',
'.htaccess',
'*.html',
'views/{,*/}{,*/}*.html',
'scripts/{,*/}{,*/}{,*/}*.js',
'styles/{,*/}{,*/}glyphicons-*',
'bower_components/**/*',
'images/{,*/}*.{webp}',
'fonts/*'
]
},
{
expand: true,
cwd: './.tmp/images',
dest: '<%= yeoman.dist %>/images',
src: ['generated/*']
}
]
},
styles: {
expand: true,
cwd: '<%= yeoman.app %>/styles',
dest: '.tmp/styles/',
src: '{,*/}*.css'
}
},
ngAnnotate: {
options: {
singleQuotes: true
},
dist: {
files: [
{
expand: true,
src: './.tmp/scripts/{,*/}{,*/}{,*/}*.js'
}
]
}
},
// Run some tasks in parallel to speed up the build process
concurrent: {
server: [
'copy:styles'
],
test: [
'copy:styles',
'shell'
],
dist: [
'copy:styles',
'imagemin',
'svgmin'
]
},
sass: {
dev: {
options: {
style: 'nested',
sourcemap: 'true',
'cache-location': '/tmp/sass-cache'
},
files: {
'./app/styles/css/styles.css': './app/styles/sass/modules/styles.scss'
}
},
dist: {
options: {
style: 'compressed',
'cache-location': '/tmp/sass-cache'
},
files: {
'./app/styles/css/styles.css': './app/styles/sass/modules/styles.scss'
}
}
},
// By default, your `index.html`'s <!-- Usemin block --> will take care of
// minification. These next options are pre-configured if you do not wish
// to use the Usemin blocks.
// cssmin: {
// dist: {
// files: {
// '<%= yeoman.dist %>/styles/main.css': [
// '.tmp/styles/{,*/}*.css',
// '<%= yeoman.app %>/styles/{,*/}*.css'
// ]
// }
// }
// },
// uglify: {
// dist: {
// files: {
// '<%= yeoman.dist %>/scripts/scripts.js': [
// '<%= yeoman.dist %>/scripts/scripts.js'
// ]
// }
// }
// },
// concat: {
// dist: {}
// },
karma: {
unit: {
configFile: './karma.conf.js',
singleRun: true
}
},
protractor: {
options: {
configFile: '<%= yeoman.test %>/protractor.conf.js', // Default config file
keepAlive: true // If false, the grunt process stops when the test fails.
},
e2e: {
options: {
configFile: '<%= yeoman.test %>/protractor.conf.js', // Target-specific config file
args: {} // Target-specific arguments
}
}
},
shell: {
webdriver: {
options: {
stdout: true
},
command: 'phantomjs --webdriver=4445'
}
},
htmlSnapshot: {
all: {
options: {
//that's the path where the snapshots should be placed
//it's empty by default which means they will go into the directory
//where your Gruntfile.js is placed
snapshotPath: 'snapshots/',
//This should be either the base path to your index.html file
//or your base URL. Currently the task does not use it's own
//webserver. So if your site needs a webserver to be fully
//functional configure it here.
sitePath: 'http://localhost:9000',
//you can choose a prefix for your snapshots
//by default it's 'snapshot_'
fileNamePrefix: 'sp_',
//by default the task waits 500ms before fetching the html.
//this is to give the page enough time to to assemble itself.
//if your page needs more time, tweak here.
msWaitForPages: 1000,
//sanitize function to be used for filenames. Converts '#!/' to '_' as default
//has a filename argument, must have a return that is a sanitized string
sanitize: function (requestUri) {
//returns 'index.html' if the url is '/', otherwise a prefix
if (/\//.test(requestUri)) {
return 'index.html';
} else {
return requestUri.replace(/\//g, 'prefix-');
}
},
//if you would rather not keep the script tags in the html snapshots
//set `removeScripts` to true. It's false by default
removeScripts: false,
//set `removeLinkTags` to true. It's false by default
removeLinkTags: false,
//set `removeMetaTags` to true. It's false by default
removeMetaTags: false,
//Replace arbitrary parts of the html
replaceStrings: [
{'this': 'will get replaced by this'},
{'/old/path/': '/new/path'}
],
// allow to add a custom attribute to the body
bodyAttr: 'data-prerendered',
//here goes the list of all urls that should be fetched
urls: [
'',
'#!/en-gb/showcase'
],
// a list of cookies to be put into the phantomjs cookies jar for the visited page
cookies: [
{'path': '/', 'domain': 'localhost', 'name': 'lang', 'value': 'en-gb'}
]
}
}
}
});
grunt.registerTask('serve', function (target) {
if (target === 'dist') {
return grunt.task.run(['build', 'connect:dist:keepalive']);
}
grunt.task.run([
'clean:server',
'bowerInstall',
'concurrent:server',
'autoprefixer',
'connect:livereload'
]);
});
grunt.registerTask('server', function () {
grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.');
grunt.task.run(['serve']);
});
grunt.registerTask('test', [
'clean:server',
'concurrent:test',
'autoprefixer',
'connect:test',
'karma'
//'protractor'
]);
grunt.registerTask('build', [
'newer:jshint',
'clean:dist',
'bowerInstall',
'sass:dist',
'useminPrepare',
'copy:styles',
//'imagemin', // Removed From Build Process - Bamboo problems TODO: Fix this
'svgmin',
'autoprefixer',
'concat',
'copy:dist',
'ngAnnotate',
//'cdnify', //Do we need this - if so we need to install it
'cssmin',
'uglify',
'rev',
'usemin',
'htmlmin'
]);
grunt.registerTask('bamboo', [
'clean',
'newer:jshint',
'karma'
]);
grunt.registerTask('default', [
'newer:jshint',
'test',
'build'
]);
};
那么你需要做的就是从命令行运行:
$ root-of-your-app-where-Gruntfile-lives/grunt karma