AngularJS Unit Testing Skill
Overview
This skill specializes in writing, refactoring, and maintaining high-quality unit tests for AngularJS applications using both Jasmine and Jest. It provides comprehensive guidance on testing controllers, services, filters, directives, and other AngularJS components.
Skill Capabilities
Core Testing Competencies
- Controllers: Write tests for controller logic, state management, and event handling
- Services: Test factory/service dependencies, HTTP calls, and business logic
- Filters: Validate filter transformations and edge cases
- Directives: Test directive compilation, linking, and DOM manipulation
- HTTP Mocking: Mock HTTP calls using
$httpBackend(Jasmine) orjest.mock()(Jest) - Promises & Async: Handle
$q, deferred objects, and$timeout - Dependency Injection: Test with mocked and real dependencies
- Scope Management: Test scope lifecycle, watchers, and event broadcasting
- Coverage Analysis: Generate and interpret code coverage reports
- Test Organization: Structure tests following best practices and maintainability principles
- Framework Choice: Understand when to use Jasmine vs Jest, or both
Testing Patterns
This skill implements the following testing patterns:
AAA Pattern (Arrange-Act-Assert)
- Organize tests with clear setup, execution, and verification phases
- Improves readability and maintainability
Mocking & Spying
- Use Jasmine spies or Jest mocks to mock functions and verify calls
- Mock HTTP responses and service dependencies
- Simulate user interactions
Test Fixtures
- Create reusable test data and helper functions
- Use beforeEach/afterEach for common setup/teardown
- Reduce test duplication
Edge Case Testing
- Test boundary conditions, empty states, and error scenarios
- Validate error handling and graceful degradation
- Test asynchronous operations and race conditions
Snapshot Testing (Jest)
- Capture expected component output
- Detect unintended changes in UI rendering
Testing Frameworks & Test Runners
Jasmine (Recommended for AngularJS)
Jasmine is a behavior-driven development (BDD) testing framework optimized for unit testing AngularJS applications.
Key Concepts:
describe(): Group related tests into a test suiteit(): Define individual test casesexpect(): Create assertionsbeforeEach()/afterEach(): Setup and teardown hooksspyOn()/jasmine.createSpy(): Mock functions and track calls
Setup:
npm install --save-dev jasmine karma karma-jasmine karma-chrome-launcher
Jest (Modern Alternative)
Jest is a modern testing framework with powerful mocking, coverage, and snapshot testing capabilities. It can also test AngularJS applications with appropriate configuration.
Key Concepts:
describe(): Group related tests (same as Jasmine)test()orit(): Define individual test casesexpect(): Create assertions (Jasmine-compatible)beforeEach()/afterEach(): Setup and teardown hooksjest.fn(): Create mock functionsjest.mock(): Mock modules
Setup:
npm install --save-dev jest jest-preset-angular @angular/core
Jest Configuration (package.json or jest.config.js):
{
"preset": "jest-preset-angular",
"setupFilesAfterEnv": ["<rootDir>/setup-jest.ts"],
"testPathIgnorePatterns": ["/node_modules/", "/dist/"],
"collectCoverage": true,
"collectCoverageFrom": ["src/**/*.js", "!src/**/*.spec.js"]
}
Karma (Test Runner for Jasmine)
Karma is a test runner that executes Jasmine tests in real browsers and provides reporting.
Configuration:
karma.conf.js: Main configuration file- Specifies browser environment, files to load, and plugins
- Supports code coverage reporting and CI integration
Test Structure
Jasmine Test Structure (Recommended for AngularJS)
All Jasmine tests follow this standard structure:
describe('Component Name', function() {
var componentUnderTest, dependencies;
// Load the module
beforeEach(module('myApp'));
// Inject dependencies
beforeEach(inject(function($injector) {
componentUnderTest = $injector.get('ComponentName');
dependencies = $injector.get('DependencyName');
}));
// Cleanup after each test
afterEach(function() {
// Cleanup code
});
describe('Functionality Group', function() {
it('should do something specific', function() {
// Arrange
var input = 'test';
// Act
var result = componentUnderTest.method(input);
// Assert
expect(result).toBe('expected');
});
});
});
Jest Test Structure (Modern Alternative)
Jest tests follow a similar structure but with some differences:
describe('Component Name', () => {
let componentUnderTest;
let dependencies;
beforeEach(() => {
// Setup code or mock initialization
componentUnderTest = require('./component').default;
dependencies = jest.mock('./dependency');
});
afterEach(() => {
// Cleanup code
jest.clearAllMocks();
});
describe('Functionality Group', () => {
test('should do something specific', () => {
// Arrange
const input = 'test';
// Act
const result = componentUnderTest.method(input);
// Assert
expect(result).toBe('expected');
});
});
});
Key Differences:
- Jest uses arrow functions (ES6) while Jasmine supports both
- Jest uses
test()orit()(both work) - Jest uses
jest.mock()instead of Jasmine spies for module mocking - Jest auto-discovers
.spec.jsand.test.jsfiles - Jest provides built-in snapshot testing and code coverage
Best Practices
1. Test Organization
- One test file per component (e.g.,
controller.spec.jsforcontroller.js) - Organize tests into logical groups using
describe() - Use meaningful test names that describe expected behavior
2. DRY Principle (Don't Repeat Yourself)
- Extract common setup into
beforeEach()blocks - Create reusable test data fixtures
- Use helper functions to reduce duplication
3. Test Independence
- Each test should be independent and runnable in any order
- Clean up resources in
afterEach() - Avoid shared state between tests
4. Realistic Mocks
- Mock external dependencies realistically
- Use actual data structures when possible
- Avoid overly simplified or unrealistic mocks
5. Comprehensive Coverage
- Aim for 80%+ code coverage
- Test happy paths, edge cases, and error scenarios
- Test async operations and race conditions
6. Performance
- Keep tests fast (< 100ms per test)
- Use in-memory mocks instead of real HTTP requests
- Avoid unnecessary database operations
7. Maintainability
- Write tests that are easy to understand
- Use the AAA pattern for clarity
- Document complex test logic with comments
- Refactor tests when the component changes
Common Testing Scenarios
Testing Controllers
describe('UserController', function() {
var $scope, controller;
beforeEach(module('myApp'));
beforeEach(inject(function($controller, $rootScope) {
$scope = $rootScope.$new();
controller = $controller('UserController', {
$scope: $scope
});
}));
it('should initialize with default values', function() {
expect($scope.users).toBeDefined();
});
it('should load users on init', function() {
expect($scope.users.length).toBeGreaterThan(0);
});
});
Testing Services
describe('UserService', function() {
var userService, $httpBackend;
beforeEach(module('myApp'));
beforeEach(inject(function(_UserService_, _$httpBackend_) {
userService = _UserService_;
$httpBackend = _$httpBackend_;
}));
afterEach(function() {
$httpBackend.verifyNoOutstandingExpectation();
});
it('should fetch users from API', function() {
var expectedUsers = [{ id: 1, name: 'John' }];
$httpBackend.expectGET('/api/users').respond(expectedUsers);
userService.getUsers().then(function(users) {
expect(users).toEqual(expectedUsers);
});
$httpBackend.flush();
});
});
Testing with Promises
describe('PromiseService', function() {
var service, $q, $rootScope;
beforeEach(inject(function(_Service_, _$q_, _$rootScope_) {
service = _Service_;
$q = _$q_;
$rootScope = _$rootScope_;
}));
it('should handle promise resolution', function() {
var deferred = $q.defer();
var result;
service.asyncOperation().then(function(data) {
result = data;
});
deferred.resolve('success');
$rootScope.$apply();
expect(result).toBe('success');
});
});
Debugging Tests
Jasmine Debugging
// Skip a test
xit('should do something', function() { ... });
// Run only this test
fit('should do something', function() { ... });
// Console logging in tests
it('should debug', function() {
console.log('Current state:', $scope);
expect(true).toBe(true);
});
Jest Debugging
// Skip a test
test.skip('should do something', () => { ... });
// Run only this test
test.only('should do something', () => { ... });
// Debug with Node inspector
// Run: node --inspect-brk node_modules/.bin/jest --runInBand
Integration with CI/CD
GitHub Actions Example
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v2
- run: npm install
- run: npm test -- --coverage
- uses: codecov/codecov-action@v2
Jenkins Example
pipeline {
stages {
stage('Test') {
steps {
sh 'npm install'
sh 'npm test'
publishHTML([
reportDir: 'coverage',
reportFiles: 'index.html',
reportName: 'Coverage Report'
])
}
}
}
}
Resources
- Jasmine Documentation
- Karma Test Runner
- Jest Documentation
- jest-preset-angular
- AngularJS Testing Guide
- Angular Testing Guide
Troubleshooting
Tests not running
- Check that module is loaded:
beforeEach(module('myApp')) - Verify dependencies are injected:
beforeEach(inject(...)) - Check for syntax errors
Async tests timing out
- Ensure promises are resolved:
$rootScope.$apply()or$httpBackend.flush() - Use
done()callback:it('...', function(done) { ... done(); }) - For Jest: use
async/awaitor return promise
HTTP mocks not working
- Verify mock is set up before service call
- Check exact URL match in expectations
- Use
passThrough()for unmocked requests
Coverage gaps
- Review untested branches in coverage reports
- Test error conditions and edge cases
- Mock external dependencies properly
Next Steps
- Start Small: Write tests for a single component
- Understand Patterns: Study the testing patterns guide
- Use Templates: Reference template files for your component type
- Refine: Improve tests based on coverage and feedback
- Automate: Integrate tests into your CI/CD pipeline
- Share: Document testing patterns for your team
Specialization: AngularJS Unit Testing with Jasmine and Jest
Version: 1.0
Last Updated: January 10, 2026
Converted and distributed by TomeVault — claim your Tome and manage your conversions.