Jest : testing simple class with Store dependency - reactjs

I having a hard time understanding why my mocked method isnt returning the value I specified with mockReturnValue. I am testing a simple method that checks a helper on a store to see if a user is authenticated before proceeding. See connectIfAuth below.
'use strict';
var AuthStore = require('../stores/AuthStore');
console.log('top ' + AuthStore.isAuthenticated());
var TestClass = {
connect() {
//...use a mock
},
connectIfAuth() {
console.log('in connect if: ' + AuthStore.isAuthenticated());
if (AuthStore.isAuthenticated()) {
this.connect();
}
}
};
module.exports = TestClass;
In my test, I want to make the mocked method from the store return true/false and test that the connect() method is called/not-called but the method returns undefined within the method I am testing.
'use strict';
jest.dontMock('../TestClass.js');
var AuthStore = require('../../stores/AuthStore');
describe('TestClass', function() {
var TestClass;
var connectMock;
beforeEach(function() {
//the store should return true
AuthStore.isAuthenticated = jest.genMockFunction().mockReturnValue(true);
//do I have to require this after I mock out the method(s) in AuthStore?
TestClass = require('../TestClass');
connectMock = jest.genMockFunction();
TestClass.connect = connectMock;
});
it('should return the mocked value', function(){
TestClass.connectIfAuth();
expect(connectMock.mock.calls.length).toBe(1); //is 0
});
});
My test output is:
FAIL src/scripts/api/tests/TestClass-tests.js (0.121s)
top undefined
in connect if: undefined
● TestClass › it should return the mocked value
Expected: 0 toBe: 1
at Spec. (/Users/blabla/projects/my-client/src/scripts/api/tests/TestClass-tests.js:20:43)
at Timer.listOnTimeout [as ontimeout] (timers.js:112:15)

In case anyone comes across this same problem - I solved this by putting all of my requires statements outside of the describe/it blocks. Similar to this question, but I also ended up require-ing the module I was testing at the top of the file. I assume I'll have to closely manage the beforeEach and afterEach methods to clean up the mocks I use within my tests.

Related

AngularJS service and mocked tests have conflicting context, when using own methods?

I have an AngularJS service which uses it's own methods (bad practice?). When I go to test this, and mock out the internally used method, I get conflicting context.
Here's an example service
angular.module('myModule', []).service('cardWarsService', function() {
return {floopThePig:floopThePig,
winAtCardWars:winAtCardWars};
function winAtCardWars(cards){
....
floopThePig(pigCard)
....
};
function floopThePig(card){
// flooping happens here
}
});
However when I try to mock out floopThePig I get told it's not called. This is how I'm mocking it.
spyOn(cardWarsService, 'floopThePig').and.callThrough();
If I change the call in the service to use this or with var self = this then it works in the test frame work but not in the application.
angular.module('myModule', []).service('cardWarsService', function() {
var self = this;
return {floopThePig:floopThePig,
winAtCardWars:winAtCardWars};
function winAtCardWars(cards){
....
self.floopThePig(pigCard)
....
};
function floopThePig(card){
// flooping happens here
}
});
Place your return object as a stand alone variable.
The issue is that the methods on either side of your return object are different. Your test only mocks the interface. As your application is not using the interface the spy doesn't pick up that it's being used.
This will guarantee our service will work in and out of the test framework
angular.module('myModule', []).service('cardWarsService', function() {
var self = {floopThePig:floopThePig,
winAtCardWars:winAtCardWars};
return self;
function winAtCardWars(cards){
....
floopThePig(pigCard)
....
};
function floopThePig(card){
// flooping happens here
}
});

Unit testing AngularJS promises with ES6

I'm trying to test an async method in an AngularJS service that calls another async function internally using Jasmine and Karma.
Here's how my service looks like:
export default class SearchUserAPI {
constructor(BaseService, $q) {
this.q_ = $q;
this.service_ = BaseService;
}
isActive(email) {
const params = {'email': email};
return this.service_.getUser(params).then(isActive => {
// This part cannot be reached.
console.log('Is Active');
// I need to test the following logic.
return isActive ? true : this.q_.reject(`User ${email} is not active.`);
});
}
}
And here's how my test looks like:
import SearchUserApi from './api.service';
let service,
mockedService,
$q;
const email = 'chuck.norris#openx.com';
const expectedParams = {email: email};
describe('Search API unit tests', function() {
beforeEach(inject(_$q_ => {
$q = _$q_;
mockedService = {};
service = new SearchUserApi(mockedService, $q);
}));
// This test passes, but it doesn't reach the logging statement in main method.
it('is verifying that Chuck Norris should be active', () => {
// Trying to mock getUser() to return a promise that resolves to true.
mockedService.getUser = jasmine.createSpy('getUser').and.returnValue($q.when(true));
service.isActive(email).then(result => {
// The following should fail, but since this part is called asynchronously and tests end before this expression is called, I never get an error for this.
expect(result).toBe(false);
});
// This test passes, but I'm not too sure how I can verify that isActive(email) returns true for user.
expect(mockedService.getUser).toHaveBeenCalledWith(expectedParams);
});
});
I see in a lot of tutorials, they talk about using $scope and apply to see if a scope variable has been changed. But in my case, I'm not manipulating any instance(scope) variable to use $scope.apply().
Any idea how I can make the test to wait for my async calls to be resolved before they end?
I figured out how to go through the async method. All I have to do was to inject $rootScope and use $rootScope.$digest() after I call the async method, even if I'm not touching scope variables inside my test.

Jasmine unit test with angular mock

I have created a mock service that would act just like my real service but return a defined set of values for my unit test. If I add that module to my apps. it intercepts the calls and returns the things I have in the mock just like its suppose to.
but when I add this module to my jasmine test I cant seem to get a value back
beforeEach(module('VideoConference'));//my app
beforeEach(module('lookupServiceMock')); //the mock service
beforeEach(inject(function(Mlb) { // Mlb is a factory that submits requests
mlb = Mlb;
spyOn(mlb, 'submitRequest').and.callThrough();
}));
...
it("Should Create a new conference", function () {
childScope = $scope.$new();
vm = $controller('TitleAndDateCtrl', { $scope: childScope });
childScope.requiredFieldsMet = function () { return true }
vm.conference.Title = "Test";
vm.createConference();
expect(mlb.submitRequest).toHaveBeenCalled();//this expectation comes back true
});
it("Should have returned a value", function() {
var id = mlb.get("id");//at this point expect that the id has been set
expect(id).toBe(123);
});
When I step through, the controller does go to where the $http.post(...) is made but it does not hit my mock or return a value. it does not go into the success of the post.
any idea how to make it hit my mock, return the value
You should add httpMock = $httpBackend; to your beforeEach.
Then in your test:
it("Should have returned a value", function() {
httpMock.expectGET('PATH_TO_YOUR_HTTP_GET').respond({id: 123});
var id = mlb.get("id");//at this point expect that the id has been set
httpMock.flush();
expect(id).toBe(123);
});
I don't know how your respond should look like, so you should change it.

Jasmine: Trying to test an AngularJS factory function

I am totally new to testing in AngularJS. I have setup karma, and am now attempting to test a certain function in a factory I have written.
Here is a snippet of my factory:
app.factory('helpersFactory', ['constants', function (constants) {
return {
someFunction: function() {
},
is24x24Icon: function (iconNum) {
return ((iconNum >= 10090 && iconNum <= 10125) ;
}
};
}]);
I then have this test:
describe('Factory: helpersFactory', function () {
beforeEach(module('ppMobi'));
var fct;
beforeEach(inject(function ($factory) {
fct = $factory('helpersFactory');
}));
it('should detect iconNum 10090 is a 24 x 24 icon', function () {
var iconNum = 10090;
var is24x24Icon = fct.is24x24Icon(iconNum);
expect(is24x24Icon).toBeTruthy();
});
});
I get an error from Karma telling me it cannot read 'is24x24icon' of undefined. Therefore I can only assume my factory has not been created properly during the test. I do have a dependency on constants in the factory used by other functions. This is just an angular.constant() I have setup on my main application module.
I have found some other posts, but am unsure how to proceed, do I need to inject my constants dependency into my test?
Kind of new myself but I think you need to use the underscore name underscore trick to inject your factory:
var fct;
beforeEach(inject(function (_helpersFactory_) {
fct = _helpersFactory_;
}));
This blog uses mocha but I found it useful and the Karma stuff should be the same: https://www.airpair.com/angularjs/posts/testing-angular-with-karma
And yes you will need to inject the constants as well (the link shows how) but your posted code does not seem to use constants so you won't need it for this particular test.

Angular Tests always fail on $logProvider when getting constants in tests

Very new to Angular testing... using 1.3.0.rc0. To get started I'm trying to do something simple: get the value of a constant I set. Within a config.js, I have the following:
(function () {
'use strict';
var app = angular.module('app');
// create app configuration
var appConfig = {
version = '0.0.1.0',
debugMode = true
};
app.constant('config', appConfig);
app.config([function ($logProvider, config) {
// set the debugging setting of the app > same setting for the app
if ($logProvider.debugEnabled) {
$logProvider.debugEnabled(config.debugMode);
}
}]);
})();
I'm tried numerous things to write my tests (using jasmine & karma), but I keep getting an error that:
Error: [$injector:modulerr] Failed to instantiate module app due to:
TypeError: 'undefined' is not an object (evaluating '$logProvider.debugEnabled').
I get that this was a bug a while ago in the angular-mocks.js file but has since been resolved. Regardless, no matter the test I write, it doesn't work. Here's what i'm working with now, knowing that there are issues with it.
'use strict';
describe('config.js', function () {
var logProvider;
beforeEach(module(inject(function ($log) {
logProvider = $log;
})));
beforeEach(module('app', logProvider));
it('should set the config constant to the app global configuration settings', function () {
var $injector = angular.injector(['ng', 'app']);
var settings = $injector.get('config');
//var settings = inject(config);
expect(settings.debugMode).toBe(true);
});
});
Am I doing this right? If so, is there no way to get around the test issue with $logProvider?
There is much to learn about how modules work in Angular, especially under testing with ngMocks. I'll try to be brief.
One always begins by calling module (from ngMocks) one (or more times) to build up the module "cookbook" for a test run.
In any of these module calls you have an opportunity to access and stash away a previously defined provider.
The first time you call inject (from ngMocks) in a given test path, the module "cookbook" is "baked" for that path and the injector is populated based on recipes in that "cookbook".
Subsequent calls to module are irrelevant. Your expression beforeEach(module('app', logProvider)); executes too late (even if it did what you wanted, which it would not).
In fact, I'm surprised that you didn't get the error: "Error: Injector already created, can not register a module!".
inject always returns the thing created by the provider, never the provider itself. Your first beforeEach ...
beforeEach(module(inject(function ($log) {
logProvider = $log;
})));
... actually sets logProvider to the $log service, not the $logProvider.
Does this help?
Here is a sample from my forthcoming course on Ng testing that shows how to access a provider (in this case, the $logProvider). It was inspired by your question.
First, the config2 constant (I already had a value called config:
// my sample application module definition is called 'basics'
var basics = angular.module('basics', []);
/* define 'config2' constant - which is available in Ng's config phase */
basics.constant('config2', {
debugMode: true
});
// use constant in config phase
basics.config(function ($logProvider, config2) {
$logProvider.debugEnabled(config2.debugMode);
})
Now the spec (using Mocha and Chai):
describe('Basics - constant:', function() {
'use strict';
beforeEach(module('basics'));
// other stuff
describe("the $logProvider", function(){
var configConstant;
var $log;
var $logProvider;
beforeEach(module(
// Could combine with module('basics') definition in outer describe
// but only need it here in this describe
// This module definition function has access to any previously defined provider
// which in this case is any provider defined in ng, ngMocks, or basics
function( _$logProvider_) {
$logProvider = _$logProvider_;
}
));
// inject triggers injector creation; module definition now "baked"
beforeEach(inject(function(config2, _$log_){
configConstant = config2;
$log = _$log_;
}));
it("is accessible via the module function", function(){
expect($logProvider).to.exist;
});
it("is not the same as the log service", function(){
expect($logProvider).not.to.equal($log);
});
it("has same debugEnabled value as config2.debugMode", function(){
expect($logProvider.debugEnabled()).to.equal(configConstant.debugMode);
});
});
});

Resources