问题
I'm developing a frontend for a REST API. I'm using Protractor for end-to-end tests with the API mocked out because I want to be able to test the frontend in isolation.
Here's a simple test I got:
describe('partner', function () {
it('should list partners', function () {
var page = new PartnerListPage();
var httpBackendMock = function () {
angular.module('httpBackendMock', ['ngMockE2E'])
.run(function ($httpBackend) {
$httpBackend.whenGET('/api/partners').respond(200, {
_embedded: {
partners: [
{
firstName: 'Elnur',
lastName: 'Abdurrakhimov'
}
]
}
});
$httpBackend.whenGET(/.*/).passThrough();
});
};
browser.addMockModule('httpBackendMock', httpBackendMock);
page.open();
expect(page.isOpen()).toBeTruthy();
expect(page.getPartner(0).firstName).toBe('Elnur');
expect(page.getPartner(0).lastName).toBe('Abdurrakhimov');
});
});
What it does is basically create a mock of the backend so that it returns something I want it to.
It's all fine and dandy except for one thing. I want to be able to call the following methods at the end of the test:
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
The reason I want to do that is to verify that the mock is getting the expected requests. It doesn't make much sense for the test I've provided, but let's say I want to verify that when I submit some form, a particular POST
request with particular data is being executed. I want to do the verification on the backend mock itself so that my test is isolated from outputting the result of the response of that POST
request because that will be tested in another test.
How do I get access to the same $httpBackend
instance that's being used in the httpBackendMock
I've added to browser
?
As far as I understand, Protractor and the app itself run in separate processes. Therefore I can't get access to $httpBackend
directly the same way I can in a unit test.
回答1:
Your application is execute in the browser and protractor in a separate process. So you are right you can't access directly the browser context.
Fortunately, protractor provide a mechanism to execute javascript in the browser. I guess you could use this mechanism to access the $httpBackendMock
.
browser.executeAsyncScript(function() {
var $httpBackend = angular.injector(['httpBackendMock']).get('$httpBackend');
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
});
NB: Code adapted from this answer.
Another option could be to expose a "check URL"
$httpBackend.whenGET('/api/check').respond(function() {
$httpBackend.verifyNoOutstandingExpectation();
$httpBackend.verifyNoOutstandingRequest();
return [200];
});
and use it in your test:
browser.get('/api/check');
回答2:
You should try to avoid httpBackend stubs in your protractor tests. It can make changing your test data at a later date a lot more complicated then it needs to be. Protractor has a nice alternative to the mockHttp and that is Protractor#addMockModule. You can use this to mock out an individual module to have it return the desired result. You need to add this code before you call Protractor#get(). It will load after your application services overriding if it has the same name as an existing service.
You can use it as follows :
var dataUtilMockModule = function () {
// Create a new module which depends on your data creation utilities
var utilModule = angular.module('dataUtil', ['platform']);
// Create a new service in the module that creates a new entity
utilModule.service('EntityCreation', ['EntityDataService', '$q', function (EntityDataService, $q) {
/**
* Returns a promise which is resolved/rejected according to entity creation success
* @returns {*}
*/
this.createEntity = function (details,type) {
// This is your business logic for creating entities
var entity = EntityDataService.Entity(details).ofType(type);
var promise = entity.save();
return promise;
};
}]);
};
browser.addMockModule('dataUtil', dataUtilMockModule);
You can have this run as part of the before setup of you test and then return to the default in the after section by calling Protractor#clearMockModules() to clear the list of mock modules, ready for the next setup.
回答3:
I've found a much better solution than using the $httpBackend
way.
Since you run Protractor tests on Node.js, it makes more sense to just use some Node.js package to mock the backend instead of this ugly and hacky solution I proposed in the question.
Hock worked great for me and hence I recommend using it or something similar.
来源:https://stackoverflow.com/questions/24419310/how-to-verify-httpbackend-mock-expectations-in-a-protractor-test