One of the most common errors encountered in AngularJS unit tests is:
TypeError: Cannot read property 'value' of undefined
This error typically occurs when trying to access a property on an undefined object. In AngularJS tests, this can happen due to:
- Incorrect Dependency Injection
- Uninitialized Variables
- Mocking Issues
- Asynchronous Code Not Resolved Properly
- Failure to Provide Required Dependencies
Let’s go step by step to understand, debug, and fix this issue.
1. Common Scenarios Causing the Error
Scenario 1: Service Not Injected Properly
Example
app.service('myService', function () {
this.getValue = function () {
return { value: 100 };
};
});
Incorrect Test Case:
describe('MyService Tests', function () {
var myService;
beforeEach(module('myApp'));
beforeEach(inject(function (_myService_) {
// Misspelled service name or missing injection
myService = undefined; // Service not properly assigned
}));
it('should return a value', function () {
expect(myService.getValue().value).toBe(100); // ❌ Error here
});
});
Error:
TypeError: Cannot read property 'value' of undefined
Fix
Make sure the service is properly injected:
beforeEach(inject(function (_myService_) {
myService = _myService_; // Correct injection
}));
Scenario 2: Scope Variable Not Defined in Controller
Example
app.controller('MyController', function ($scope) {
$scope.data = { value: 50 };
});
Incorrect Test Case:
describe('MyController Tests', function () {
var $scope, $controller;
beforeEach(module('myApp'));
beforeEach(inject(function (_$controller_) {
$controller = _$controller_; // Injected but $scope not initialized
}));
it('should have a value', function () {
expect($scope.data.value).toBe(50); // Error: $scope is undefined
});
});
Error:
TypeError: Cannot read property 'value' of undefined
Fix
Initialize $scope
properly:
beforeEach(inject(function (_$controller_, _$rootScope_) {
$scope = _$rootScope_.$new();
$controller('MyController', { $scope: $scope });
}));
Scenario 3: Mocking Issues
If a service is required in a test, but it’s not mocked properly, it can result in an undefined object.
Incorrect Mock:
beforeEach(module(function ($provide) {
$provide.value('myService', {}); // Missing method definition
}));
it('should return a value', function () {
expect(myService.getValue().value).toBe(100); // Fails
});
Fix
Properly define the mock:
beforeEach(module(function ($provide) {
$provide.value('myService', {
getValue: function () {
return { value: 100 };
}
});
}));
Scenario 4: Using then()
on Undefined Promise
If a service returns a promise, but it is not mocked correctly in a test, it may result in an undefined property.
Incorrect Test Case:
app.service('myService', function ($http) {
this.getData = function () {
return $http.get('/api/data'); // Returns a promise
};
});
describe('MyService Tests', function () {
var myService;
beforeEach(module('myApp'));
beforeEach(inject(function (_myService_) {
myService = _myService_;
}));
it('should handle promise response', function () {
myService.getData().then(function (response) {
expect(response.data.value).toBe(100); // ❌ Error if response is undefined
});
});
});
Error:
TypeError: Cannot read property 'value' of undefined
Fix
Use $httpBackend
to mock the API response:
var $httpBackend;
beforeEach(inject(function (_myService_, _$httpBackend_) {
myService = _myService_;
$httpBackend = _$httpBackend_;
}));
it('should return a mock response', function () {
$httpBackend.whenGET('/api/data').respond(200, { value: 100 });
myService.getData().then(function (response) {
expect(response.data.value).toBe(100);
});
$httpBackend.flush(); // Ensure the async call resolves
});
2. Debugging Steps
If you still get the error, use these debugging techniques:
1. Log the Object Before Accessing Properties
console.log(myService); // Should not be undefined
console.log(myService.getValue()); // Should return an object
2. Ensure Module Is Loaded
Check if the module is registered:
angular.module('myApp'); // If this throws an error, the module is not loaded
3. Use expect
to Check for Undefined
Modify your test case:
expect(myService.getValue()).toBeDefined(); // Ensures service returns a valid object
4. Check If Dependencies Are Injected
Log injected dependencies:
beforeEach(inject(function (_myService_) {
console.log(_myService_); // Should not be undefined
}));
3. Summary & Best Practices
Problem | Fix |
---|---|
Service not injected properly | Ensure beforeEach(module('myApp')) is used |
$scope is undefined in the controller test | Initialize $scope using $rootScope.$new() |
Mocking issues | Use $provide.value() with valid mock methods |
Promise response is undefined | Use $httpBackend.flush() to resolve async calls |
Accessing properties on undefined objects | Use console.log() for debugging |