Test an AngularJS directive through the same compilation and linking path that runs in the application: load its module, compile representative markup with $compile, link it to the scope arrangement the directive expects, then assert the rendered DOM, bindings and interactions it promises. Add angular-mocks.js for ngMock helpers, and use $httpBackend when the directive initiates HTTP requests.
Decide what the directive must prove
A directive test is strongest when it describes an observable contract rather than its private implementation. AngularJS directives can attach behavior to an element, listen for events, or transform the element and its children. Choose the behavior a user or parent component relies on before writing the test.
- Rendering: expected text, attributes, classes, child elements or visibility.
- Binding: whether an input, output or watched value moves correctly between the directive and its scope.
- Interaction: what a click, input, keyboard event or other listener does.
- Requirements: behavior when a required attribute, controller or binding is present or missing.
- Collaboration: the result of a service call, including the directive’s response to success or failure.
Keep assertions at the DOM and interaction boundary whenever possible. A directive that directly accesses the DOM is difficult to test if that access is spread through unrelated application code; keeping DOM-specific behavior in the directive gives the test a clear place to exercise it.
Include the AngularJS test support file, normally The exact runner configuration varies by project and AngularJS version, so use the dependency versions already installed in the legacy application. The AngularJS API documentation identifies a 1.8.4 snapshot and records that official support ended in January 2022; treat this workflow as maintenance guidance and verify local tooling before changing it. Use a child scope or the scope created by the directive according to its definition. An inherited-scope directive, an isolate-scope directive and a directive using AngularJS watchers update during a digest. Set initial state, compile and link, then run the digest needed for bindings to settle. Change the state and digest again when testing a watched value. Assert the contract a consumer can observe: text, structure, classes, attributes, emitted callbacks or changed model values. Avoid asserting the number or order of private watchers unless that implementation detail is itself part of the supported behavior. Listener directives register DOM handlers. Trigger the same event a user would cause and assert the resulting behavior, such as a callback, class change or model update. Use the event API appropriate to the test environment and directive. If the handler schedules a scope change, run a digest afterward before checking the DOM. Test meaningful branches separately: disabled controls should not invoke the callback, invalid input should show the documented state, and repeated events should not create duplicate effects. When a directive calls Add expectations for failure responses when the directive exposes an error state. A test that never calls The Tool Desk Check that the correct application module is loaded, the directive name and markup match, and the compiled element is linked to the intended scope. A missing digest can also leave bindings unapplied. Confirm that the test supplies every required attribute and uses the correct parent property. Isolate-scope bindings do not automatically behave like inherited-scope properties. Trigger the event on the element that actually owns the listener, then run a digest if the handler changes scope-backed output. Also test the directive’s disabled or guard conditions. Set the request expectation before the action that starts the request, call AngularJS official support ended in January 2022. Existing applications can still be maintained with this compile-and-link approach, but the browser, test runner, AngularJS package and Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.angular-mocks.js, after AngularJS and before the test suite. It provides the ngMock module and helpers such as module() and inject(). Load the application module that registers the directive before each relevant suite or test.describe('statusBadge directive', function () {
beforeEach(module('billingApp'));
var $compile, $rootScope;
beforeEach(inject(function (_$compile_, _$rootScope_) {
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
// tests go here
});#1 Best Overall
Compile representative markup and link it to a scope
$compile matches directives in a template and creates a template function. Linking applies that function to a scope, evaluates bindings and produces the live DOM that the test can inspect. Compile the markup a real caller would use instead of instantiating the directive’s factory in isolation.it('renders the current status', function () {
var scope = $rootScope.$new();
scope.invoice = { status: 'Paid' };
var element = $compile(
'<status-badge status="invoice.status"></status-badge>'
)(scope);
scope.$digest();
expect(element.text()).toContain('Paid');
});bindToController receive data differently. The test should reproduce that arrangement, including parent properties and attributes, rather than assigning values directly to an internal object.Check bindings after a digest
it('updates when the watched value changes', function () {
var scope = $rootScope.$new();
scope.label = 'Queued';
var element = $compile('<status-badge label="label"></status-badge>')(scope);
scope.$digest();
expect(element.text()).toContain('Queued');
scope.label = 'Sent';
scope.$digest();
expect(element.text()).toContain('Sent');
});Exercise event listeners and user interactions
it('calls the supplied callback on click', function () {
var scope = $rootScope.$new();
scope.onChoose = jasmine.createSpy('onChoose');
var element = $compile(
'<pick-row on-choose="onChoose(item)" item="row"></pick-row>'
)(scope);
scope.row = { id: 7 };
scope.$digest();
element.triggerHandler('click');
expect(scope.onChoose).toHaveBeenCalledWith(scope.row);
});Rank #4
Mock HTTP instead of using a live network
$http, configure an $httpBackend expectation before triggering the behavior. Invoke the action, flush the trained response, then assert the directive’s visible result. This keeps the test deterministic and verifies both the request and the response handling.beforeEach(inject(function (_$httpBackend_) {
$httpBackend = _$httpBackend_;
}));
it('renders data returned by the service', function () {
var scope = $rootScope.$new();
var element = $compile('<account-summary account-id="42"></account-summary>')(scope);
$httpBackend.expectGET('/api/accounts/42').respond(200, {
name: 'Northwind'
});
scope.$digest();
$httpBackend.flush();
scope.$digest();
expect(element.text()).toContain('Northwind');
});$httpBackend.flush() has not processed the pending request and cannot prove the post-response behavior. Verify that unexpected requests fail the test and that each expected request is satisfied.A practical directive-test checklist
angular-mocks.js and the application’s directive module.$compile.$httpBackend expectations, trigger the action, flush, and test success and error outcomes.Common failures and how to diagnose them
The element is empty or the directive never runs
Bindings stay undefined
An event assertion fails
The HTTP test hangs or reports outstanding requests
$httpBackend.flush(), and verify the URL and method exactly. Unexpected or unflushed requests indicate that the test’s setup does not match the directive’s network behavior.PC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWorking safely in a legacy AngularJS codebase
angular-mocks.js version must remain compatible with one another. Record the versions used by the project, avoid assuming that a modern runner supports an old configuration unchanged, and run the suite in the same environment used by the application build.Quick Recap




