Multiple Manual Mocks of CommonJS Modules with Jest - unit-testing

I saw the documentation for Jest's mocks using the mocks folder, but I want to be able to mock a module with one mock in one test and mock that same module with another mock in another test.
For example, with rewire and jasmine, you could do something like this:
//module2.js
module.exports = {
callFoo: function () {
require('moduleToMock').foo();
}
};
//module2Test.js
describe("test1", function () {
var mock;
beforeEach(function () {
var rewire = require('rewire');
mock = jasmine.createSpyObj('mock', ['foo']);
});
it("should be mocked with type1", function () {
mock.foo.and.returnValue("type1");
rewire('moduleToMock', mock);
var moduleUsingMockModule = require('module2');
expect(moduleUsingMockModule.callFoo()).toEqual("type1");
});
});
describe("test2", function () {
it("should be mocked with type2", function () {
mock.foo.and.returnValue("type2");
rewire('moduleToMock', mock);
var moduleUsingMockModule = require('module2');
expect(moduleUsingMockModule.callFoo()).toEqual("type2");
});
});
Is this possible to do with Jest? The difference is I define the mock within the test, not in some external folder that is used for all tests.

Yes, your mock will look like this:
module.exports = {
foo: jest.genMockFunction();
}
Then you will be able to configure a custom behaviour in your test cases:
var moduleToMock = require('moduleToMock');
describe('...', function() {
it('... 1', function() {
moduleToMock.foo.mockReturnValue('type1')
expect(moduleToMock.foo).toBeCalled();
expect(moduleUsingMockModule.callFoo()).toEqual("type1");
});
it('... 2', function() {
moduleToMock.foo.mockReturnValue('type2')
expect(moduleToMock.foo).toBeCalled();
expect(moduleUsingMockModule.callFoo()).toEqual("type2");
});
});

Related

How do I unit test a helper that uses a service?

I'm trying to unit test a helper that uses a service.
This is how I inject the service:
export function initialize(container, application) {
application.inject('view', 'foobarService', 'service:foobar');
}
The helper:
export function someHelper(input) {
return this.foobarService.doSomeProcessing(input);
}
export default Ember.Handlebars.makeBoundHelper(someHelper);
Everything works until here.
The unit test doesn't know about the service and fails. I tried to:
test('it works', function(assert) {
var mockView = {
foobarService: {
doSomeProcessing: function(data) {
return "mock result";
}
}
};
// didn't work
var result = someHelper.call(mockView, 42);
assert.ok(result);
});
The error:
Died on test #1 at http://localhost:4200/assets/dummy.js:498:9
at requireModule (http://localhost:4200/assets/vendor.js:79:29)
TypeError: undefined is not a function
Everything is correct, the only change needed was:
var result = someHelper.call(mockView, "42");

Testing asynchrone function gives Unexpected request

The unittest:
"use strict";
var usersJSON = {};
describe("mainT", function () {
var ctrl, scope, httpBackend, locationMock,
beforeEach(module("testK"));
beforeEach(inject(function ($controller, $rootScope, $httpBackend, $location, $injector) {
scope = $rootScope.$new();
httpBackend = $httpBackend;
locationMock = $location;
var lUrl = "../solr/users/select?indent=true&wt=json",
lRequestHandler = httpBackend.expect("GET", lUrl);
lRequestHandler.respond(200, usersJSON);
ctrl = $controller("mainT.controller.users", { $scope: scope, $location: locationMock});
httpBackend.flush();
expect(scope.users).toBeDefined();
}));
afterEach(function () {
httpBackend.verifyNoOutstandingRequest();
httpBackend.verifyNoOutstandingExpectation();
});
describe("method test", function () {
it('should test', function () {
expect(true).toBeFalsy();
});
});
});
controller I'm testing (working):
Asynchrone function in init who's giving me trouble (uses ../solr/users/select?indent=true&wt=json):
$scope.search = function () {
var lStart = 0,
lLimit = privates.page * privates.limit;
Search.get({
collection: "users",
start: lStart,
rows: lLimit)
}, function(records){
$scope.users= records.response.docs;
});
};
What I think happens:
1. inform backend what request he will receive
2. inform backend to response on that request with empty JSON
3. create a controller (Search.get get's executed)
4. inform backend to receive all requests and answer them (flush)
Yet I always get the following error:
Error: Unexpected request: GET : ../solr/users/select?indent=true&wt=json
Am I not handling the asynchrone search function well? how should this be done?
That's not really a "unit" test, it's more of a behavioral test.
This should really be a few tests:
Test your service Search.get to make sure it's calling the proper URL and returning the result.
Test your controller method to make sure it's calling Search.get
Test your controller method to make sure it's putting the result in the proper spot.
The code you've posted is a little incomplete, but here are two unit tests that should cover you:
This is something I've blogged about extensively, and the entries go into more detail:
Unit Testing Angular Controllers
Unit Testing Angular Services
Here's an example of what I'm talking about:
describe('Search', function () {
var Search,
$httpBackend;
beforeEach(function () {
module('myModule');
inject(function (_Search_, _$httpBackend_) {
Search = _Search_;
$httpBackend = _$httpBackend_;
});
});
describe('get()', function () {
var mockResult;
it('should call the proper url and return a promise with the data.', function () {
mockResult = { foo: 'bar' };
$httpBackend.expectGET('http://sample.com/url/here').respond(mockResult);
var resultOut,
handler = jasmine.createSpy('result handler');
Search.get({ arg1: 'wee' }).then(handler);
$httpBackend.flush();
expect(handler).toHaveBeenCalledWith(mockResult);
$httpBackend.verifyNoOutstandingRequest();
$httpBackend.verifyNoOutstandingExpectation();
});
});
});
describe('myCtrl', function () {
var myCtrl,
$scope,
Search;
beforeEach(function () {
module('myModule');
inject(function ($rootScope, $controller, _Search_) {
$scope = $rootScope.$new();
Search = _Search;
myCtrl = $controller('MyCtrl', {
$scope: scope
});
});
});
describe('$scope.foo()', function () {
var mockResult = { foo: 'bar' };
beforeEach(function () {
//set up a spy.
spyOn(Search, 'get').andReturn({
then: function (fn) {
// this is going to execute your handler and do whatever
// you've programmed it to do.. like $scope.results = data; or
// something.
fn(mockResult);
}
});
$scope.foo();
});
it('should call Search.get().', function () {
expect(Search.get).toHaveBeenCalled();
});
it('should set $scope.results with the results returned from Search.get', function () {
expect(Search.results).toBe(mockResult);
});
});
});
In a BeforeEach you should use httpBackend.when instead of httpBackend.expect. I don't think you should have an assertion (expect) in your BeforeEach, so that should be moved to a separate it() block. I also don't see where lRequestHandler is defined. The 200 status is sent by default so that is not needed. Your httpBackend line should look like this:
httpBackend.when("GET", "/solr/users/select?indent=true&wt=json").respond({});
Your test should then be:
describe("method test", function () {
it('scope.user should be defined: ', function () {
expect(scope.user).toEqual({});
});
});
Your lUrl in the unit test, shouldn't be a relative path, i.e., instead of "../solr/users/select?indent=true&wt=json" it should be an absolute "/solr/users/select?indent=true&wt=json". So if your application is running at "http://localhost/a/b/index.html", lUrl should be "/a/solr/...".
Note that you can also use regular expressions in $httpBackend.expectGET(), that could be helpful here in case you are not entirely sure how the absolute path will look like later on.

Angularjs injecting $resource into factory for Testing with jasmine

I am writing my unit tests to test factory developed as below
angular.module("MyApp.Factory",[]).factory("factory",function($resource, baseUrl) {
return {
someService: function () {
return $resource(baseUrl + "/someUri");
}
};
});
jasmine spec for the same
describe("My App Factory Tests", function () {
var $factory;
beforeEach(function () {
module("MyApp.Factory");
inject(function (factory) {
$factory = factory;
});
});
it("Factory must be defined", function () {
expect($factory).toBeDefined();
});
});
but i end up with the below error from the jasmine html test page
Error: Unknown provider: $resourceProvider <- $resource <- eligibilityService
Please suggest the right way to test the factory
ngResource is defined in the separate module. Please check the documentation http://docs.angularjs.org/api/ngResource
In other words you have to include <script src="angular-resource.js"> and add ngResource dependency to MyApp.Factory module - angular.module("MyApp.Factory", ["ngResource"]).
ps. in the specs you could surround service name with underscores so that we can locally assign a local variable of the same name as the service. For example:
var factory;
beforeEach(function () {
module("MyApp.Factory");
inject(function (_factory_) {
factory = _factory_;
});
});
According to angularjs coding standards $ should not be used for naming custom directives.

Faking a Angular Factory in a directive in jasmine

Question: How do I fake my pointFactory so I can Jasmine Unit Test it.
I have the Following Directive.
It takes the html sends it to a factory and the uses the response for some logic
CommonDirectives.directive('TextEnrichment',['PointFactory','appSettings', function (pointFactory,settings) {
return {
restrict: 'A',
link : function (scope, element, attrs) {
var text = element.html();
pointFactory.getPoints(text).then(function(response){
})}}}]);
So far my unit tests looks like this, however it doesn't work since I'm not injecting the factory.
beforeEach(module('app.common.directives'));
beforeEach(function () {
fakeFactory = {
getPoints: function () {
deferred = q.defer();
deferred.resolve({data:
[{"Text":"Some text"}]
});
return deferred.promise;
}
};
getPointsSpy = spyOn(fakeFactory, 'getPoints')
getPointsSpy.andCallThrough();
});
beforeEach(inject(function(_$compile_, _$rootScope_,_$controller_){
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it('Factory to have been Called', function () {
var element = $compile('<div data-text-enrichment=""> Text </div>')($rootScope)
expect(getPointsSpy.callCount).toBe('1');
});
Update
Following advice from Felipe Skinner I have updated the test with the following
beforeEach(function(){
module(function($provide){
$provide.factory('PointFactory',getPointsSpy)
})
});
However I get the following error:
TypeError: 'undefined' is not a function (evaluating
'pointFactory.getPoints(text)')
You can use the $provide to inject your controller dependencies.
Here's my beforeEach for example:
describe('MyCtrl', function() {
var $controller,
$scope,
$httpBackend,
windowMock,
registerHtmlServiceMock,
mixPanelServiceMock,
toastMock;
beforeEach(function() {
windowMock = { navigator: {} };
registerHtmlServiceMock = {};
mixPanelServiceMock = jasmine.createSpyObj('mixpanel', ['track']);
toastMock = jasmine.createSpyObj('toast', ['error']);
module('myModule');
module(function($provide) {
$provide.value('$window', windowMock);
$provide.value('RegisterHtmlService', registerHtmlServiceMock);
$provide.value('MixPanelService', mixPanelServiceMock);
$provide.value('ToastService', toastMock);
});
inject(function(_$controller_, _$rootScope_, _$httpBackend_) {
$scope = _$rootScope_.$new();
$controller = _$controller_('CourseSelectionCtrl', { $scope: $scope });
$httpBackend = _$httpBackend_;
});
});
// my test cases
});
I haven't tried mocking a function that returns some value. Those two mocks (mixpanel-track and toast-error) are for "void" functions.
UPDATE:
Try changing the previous $provide with this type of injection then.
Change from this:
module(function($provide) {
$provide.value('$window', windowMock);
$provide.value('RegisterHtmlService', registerHtmlServiceMock);
$provide.value('MixPanelService', mixPanelServiceMock);
});
inject(function(_$controller_, _$rootScope_, _$httpBackend_) {
$scope = _$rootScope_.$new();
$controller = _$controller_('CourseSelectionCtrl', { $scope: $scope });
$httpBackend = _$httpBackend_;
});
To this:
beforeEach(inject(function(_$controller_, _$rootScope_, _$httpBackend_) {
mixPanelService = mixPanelServiceMock;
$scope = _$rootScope_.$new();
$controller = _$controller_('MyCtrl', { $scope: $scope, MixPanelService: mixPanelService });
$httpBackend = _$httpBackend_;
}));
The rest of the code should be the same, except for that. Let me know if this works

How to write unit test for AngularJS model

I've got a basic model that I am trying to write a simple unit test suite for, and I'm clearly missing something...
The code for the model looks like this:
angular.module('AboutModel', [])
.factory(
'AboutModel',
[
function () {
var paragraphs = [];
var AboutModel = {
setParagraphs: function (newParagraphs) {
paragraphs = newParagraphs;
},
getParagraphs: function () {
return paragraphs;
}
};
return AboutModel;
}
]
);
The requirement is simple: provide a getter and a setter method for the private array called paragraphs.
And here is as far as I have got with the test suite code:
describe('Testing AboutModel:', function () {
describe('paragraphs setter', function () {
beforeEach(module('AboutModel'));
it('sets correct value', inject(function (model) {
// STUCK HERE
// don't know how to access the model, or the setParagraphs() method
}));
});
describe('paragraphs getter', function () {
// not implemented yet
});
});
I've been doing quite a bit of google research on the web, but so far no joy.
The solution must be simple; please help!
And it might even be the case that there's a better way of implementing the Model... open to suggestions to make it better.
For anyone interested, the full source code is here:
https://github.com/mcalthrop/profiles/tree/imp/angular
thanks in advance
Matt
You need to run a beforeEach in your test to inject the model instance and then assign it to a variable which you can then re-use through out your tests.
var AboutModel;
beforeEach(inject(function (_AboutModel_) {
AboutModel = _AboutModel_;
}));
You can then access your getter like so:
AboutModel.getParagraphs();
I have tweaked your original model slightly as I feel it reads a little better (my preference):
'use strict';
angular.module('anExampleApp')
.factory('AboutModel', function () {
var _paragraphs;
// Public API here
return {
setParagraphs: function (newParagraphs) {
_paragraphs = newParagraphs;
},
getParagraphs: function () {
return _paragraphs;
}
};
});
And then for testing I would use a combination of the standard Jasmine tests and spies:
'use strict';
describe('Service: AboutModel', function () {
beforeEach(module('anExampleApp'));
var AboutModel, paragraphs = ['foo', 'bar'];
beforeEach(inject(function (_AboutModel_) {
AboutModel = _AboutModel_;
}));
it('should set new paragraphs array', function () {
AboutModel.setParagraphs([]);
expect(AboutModel.getParagraphs()).toBeDefined();
});
it('should call setter for paragraphs', function () {
spyOn(AboutModel, 'setParagraphs');
AboutModel.setParagraphs(paragraphs);
expect(AboutModel.setParagraphs).toHaveBeenCalledWith(paragraphs);
});
it('should get 2 new paragraphs', function () {
AboutModel.setParagraphs(['foo', 'bar']);
expect(AboutModel.getParagraphs().length).toEqual(2);
});
});