My ng app is working fine, but I am trying to write a ngMock test for my controller; I am basically following along the example on angular's website: https://docs.angularjs.org/api/ngMock/service/$httpBackend
The problem I am running into is that it complains about unexpected request even when request is being expected.
PhantomJS 1.9.8 (Windows 8 0.0.0) NotificationsController should fetch notification list FAILED
Error: Unexpected request: GET Not valid for testsapi/AspNetController/AspNetAction Expected GET api/AspNetController/AspNetAction
What I do not get is that, on the error line, why is there a "tests" word appended before my service url? I thought it should be sending to 'api/AspNetController/AspNetAction' What am I doing wrong here. I can't find any one else running into the same problem as me through google.
Edit: I noticed that, if i remove the sendRequest portion from my controller, and have the unit test log my request object in console, i see the following json.
{
"method":"GET",
"url":"Not valid for testsapi/AspNetController/AspNetAction",
"headers":{
"Content-Type":"application/json"
}
}
here is the controller code
angular.module('MainModule')
.controller('NotificationsController', ['$scope', '$location', '$timeout', 'dataService',
function ($scope, $location, $timeout, dataService) {
//createRequest returns a request object
var fetchNotificationsRequest = dataService.createRequest('GET', 'api/AspNetController/AspNetAction', null);
//sendRequest sends the request object using $http
var fetchNotificationsPromise = dataService.sendRequest(fetchNotificationsRequest);
fetchNotificationsPromise.then(function (data) {
//do something with data.
}, function (error) {
alert("Unable to fetch notifications.");
});
}]
);
Test code
describe('NotificationsController', function () {
beforeEach(module('MainModule'));
beforeEach(module('DataModule')); //for data service
var $httpBackend, $scope, $location, $timeout, dataService;
beforeEach(inject(function ($injector) {
$httpBackend = $injector.get('$httpBackend');
$scope = $injector.get('$rootScope');
$location = $injector.get('$location');
$timeout = $injector.get('$timeout');
dataService = $injector.get('dataService');
var $controller = $injector.get('$controller');
createController = function () {
return $controller('NotificationsController', {
'$scope': $scope,
'$location': $location,
'$timeout': $timeout,
'dataService': dataService,
});
};
}));
afterEach(function () {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
it('should fetch notification list', function () {
$httpBackend.expectGET('api/AspNetController/AspNetAction'); //this is where things go wrong
var controller = createController();
$httpBackend.flush();
});
});
Data service code
service.createRequest = function(method, service, data) {
var req = {
method: method, //GET or POST
url: someInjectedConstant.baseUrl + service,
headers: {
'Content-Type': 'application/json'
}
}
if (data != null) {
req.data = data;
}
return req;
}
service.sendRequest = function (req) {
return $q(function (resolve, reject) {
$http(req).then(function successCallback(response) {
console.info("Incoming response: " + req.url);
console.info("Status: " + response.status);
console.info(JSON.stringify(response));
if (response.status >= 200 && response.status < 300) {
resolve(response.data);
} else {
reject(response);
}
}, function failCallback(response) {
console.info("Incoming response: " + req.url);
console.info("Error Status: " + response.status);
console.info(JSON.stringify(response));
reject(response);
});
});
}
ANSWER:
since dataService created the finalized webapi url by someInjectedConstant.baseUrl + whatever_relative_url passed in from controller, In the test that I am writting, I will have to inject someInjectedConstant and
$httpBackend.expectGET(someInjectedConstant.baseUrl + relativeUrl)
instead of just doing a $httpBackend.expectGET(relativeUrl)
Clearly Not valid for tests
is getting prepended to your url somewhere in your code. It's also not adding the hardcoded domain (see note below). Check through all your code and any other parts of the test pipeline that might be adding this to the url.
A couple of points on your code:
someInjectedConstant
could be more explicitly namedthere is no need for you to wrap $http
with $q
, so service.sendRequest
can be:
service.sendRequest = function (req) {
$http(req).then(function (response) { // no need to name the function unless you want to call another function with all success/error code in defined elsewhere
console.info("Incoming response: " + req.url);
console.info("Status: " + response.status);
console.info(JSON.stringify(response));
return response.data; // angular treats only 2xx codes as success
}, function(error) {
console.info("Incoming response: " + req.url);
console.info("Error Status: " + response.status);
console.info(JSON.stringify(response));
});
}