I am building an angular app which uses Firebase as the front-end. But i am unable to see how to implement unit tests for the Firebase code in my app.
In conclusion, my question is:
How do you implement Firebase testing in an angular app?
As an example could someone unit test the following method?
service.subscribeForEvent = function (eventKey) {
service.dbRef = firebase.database().ref(eventKey);
service.dbRef.on('value', function (result) {
onMessage(result.val());
});
};
I have created a fake of firebase:
firebase = {
__ref: {
events: {
'child_added': [],
'child_changed': []
},
push: function (data) {
angular.forEach(this.events["child_added"], function (each) {
each(data);
});
},
update: function (data) {
angular.forEach(this.events["child_changed"], function (each) {
each(data);
});
},
child: function () {
return {
on: function (eventType, callback) {
window.firebase.__ref.events[eventType].push(callback);
}
};
}
},
database: function () {
return {
ref: function () {
return window.firebase.__ref;
},
$clean: function () {
window.firebase.__ref.events['child_added'] = [];
window.firebase.__ref.events['child_changed'] = [];
}
};
};
and expect after new child is added in firebase, the callback function to be called:
it('subscribe for added event and receive message for it', function () {
var eventName = 'new event';
var callback = jasmine.createSpy('callback');
pushApi.bind(eventName, callback);
ref.push({
val: function () {
return {event: {key: eventName}};
}
});
expect(callback).toHaveBeenCalledWith({key: eventName});
};
But when run test it expected default firebase to be created.
Related
Aws.config.js
import AWS from 'aws-sdk';
AWS.config.update({
region: process.env.REACT_APP_AWS_REGION,
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: process.env.REACT_APP_IDENTITY_POOL_ID
})
});
const AwsCognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
export default AwsCognitoIdentityServiceProvider;
I have a function in ListUser.js
import AwsCognitoIdentityServiceProvider from 'components/aws/AwsConfig';
const userList = () =>{
var params = {
UserPoolId: process.env.REACT_APP_USERPOOL_ID,
AttributesToGet: null,
Filter:""
};
AwsCognitoIdentityServiceProvider.listUsers(params,function (err, data) {
if(data) {
//fetching data here successfully
} else {
console.log("error",err);
}
})
}
My test file ListUser.test.js
const mockListUsers = jest.fn((params) => {
return {
promise() {
return Promise.resolve('mock response');
}
};
});
jest.mock('aws-sdk', () => {
return {
CognitoIdentityServiceProvider: jest.fn(() => ({
listUsers: mockListUsers
})),
config: {
update: jest.fn()
}
};
});
describe('ListUser', () => {
test('renders ListUser component', () => {
act(() => {
render(<ListUser />);
});
});
});
I am not able to mock this function and return response I am getting error below:
TypeError: _AwsConfig.default.listUsers is not a function
90 | Filter:""
91 | };
> 92 | AwsCognitoIdentityServiceProvider.listUsers(params,function (err, data) {
I have also tried with keeping aws.sdk.js file inside mocks folder but no luck
_mocks_/aws.sdk.js
class AWS {
CognitoIdentityServiceProvider = class {
listUsers = jest.fn(() =>{
return { promise: ()=> Promise.resolve({mockresponse})}
});
};
}
module.exports = AWS;
I need to mock listUsers function but not able to do so. I have followed so many links but no luck :(
This one works for me
const AWS = require('aws-sdk');
jest.mock('aws-sdk');
AWS.CognitoIdentityServiceProvider.prototype.listUsers = jest.fn().mockReturnValue({
promise: jest.fn().mockResolvedValue({})
});
I have tried this way and worked for me.
jest.mock("aws-sdk", () => {
const cognito = { listUsers: jest.fn() };
return {
CognitoIdentityServiceProvider: jest.fn(() => cognito),
config: {
update: jest.fn(),
},
};
});
const mCognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider();
mCognitoIdentityServiceProvider.listUsers.mockImplementationOnce(() => {
return {
promise() {
return Promise.resolve('your mock data');
},
};
});
I have the following code, using async - await... that works in HTML + JavaScript environment, except if I use it inside EXTJS App, component listener.
...
onListInitialize: function(component, eOpts)
{
const citiesRef= db.collection("cities");
// defining async function
async function getIsCapitalOrCountryIsItaly() {
const isCapital = citiesRef.where('capital', '==', true).get();
const isItalian = citiesRef.where('country', '==', 'Italy').get();
const [capitalQuerySnapshot, italianQuerySnapshot] = await Promise.all([
isCapital,
isItalian
]);
const capitalCitiesArray = capitalQuerySnapshot.docs;
const italianCitiesArray = italianQuerySnapshot.docs;
const citiesArray = capitalCitiesArray.concat(italianCitiesArray);
return citiesArray;
}
//We call the asychronous function
getIsCapitalOrCountryIsItaly().then(result => {
result.forEach(docSnapshot => {
console.log(docSnapshot.data());
});
});
}
...
I'm getting the error: Expected an assigment or function call and instead saw an expression.
I tried Ext.Promise without success.
SOLVED! Using one promise for each query.
Sample of code:
Ext.Promise.all([
new Ext.Promise(function(resolve, reject) {
setTimeout(function() {
resolve('one');
}, 5000);
}),
new Ext.Promise(function(resolve, reject) {
setTimeout(function() {
resolve('two');
}, 4000);
})
])
.then(function(results) {
console.log('first function result', results[0]);
console.log('second function result', results[1]);
Ext.Msg.alert('Success!', 'All promises returned!');
});
Parent Service:
module proj.Stuff {
export class ParentService {
//...properties, constructor, etc
public refreshStuff(id: number) {
this.childService
.getStuff(id)
.then((response) => this.stuff = response);
}
}
}
Child service:
module proj.Stuff {
export class ChildService{
//... properties, constructor, etc
public getStuff(id: number) {
var request: IPromise<any> = this.$http.get(
ChildService.apiUrlBase + "getStuff/" + id
);
return request
.then(response => {
return response.data.value;
}, response => {
this.$log.error("unable to get...");
});
}
}
}
Tests for the parent service:
describe("ParentService", () => {
// (property declarations omitted for brevity)
beforeEach(angular.mock.module(["$provide", ($provide) => {
var obj = {
getStuff: (id: number) => {
functionCalled = true;
return {
then: (callback) => {
return callback(["result"]);
}
};
}
};
$provide.value("ChildService", obj);
}]));
beforeEach(mock.inject((_$http_, _$log_, _$q_, _$httpBackend_, _$rootScope_, _ChildService_) => {
cService = _ChildService_;
pService = new ParentService(cbService);
}));
it("can be created", () => {
expect(pService).toBeDefined();
expect(pService).not.toBeNull();
});
it("can refresh stuff", () => {
pService.refreshStuff(1);
expect(pService.stuff).toEqual(["result"]);
expect(functionCalled).toBeTruthy();
// ***** what I want to do: *****
// expect(cService.getStuff).toHaveBeenCalled();
});
});
I'm wondering how can I spy on cService.getStuff instead of using the 'functionCalled' boolean?
When I try to spy on it, it complains that .then isn't defined - e.g. in the first beforeEach if I try spyOn(obj, "getStuff") it doesn't like it.
The tests pass as is, but would rather spyOn instead of using the boolean.
then method mocks are rarely justified, Angular DI allows to use unmocked promises and to focus on unit testing.
beforeEach(angular.mock.module(["$provide", ($provide) => {
// allows to inject $q, while $provide.value doesn't
$provide.factory("ChildService", ($q) => ({
// fresh promise on every call
getStuff: jasmine.createSpy('getStuff').and.callFake(() => $q.when('result'))
}));
}]));
Works best with Jasmine promise matchers, otherwise routine promise specs should be involved:
var result;
...then((_result) => { result = _result; })
$rootScope.$digest();
expect(result)...
I'm mocking a function called isLoggedIn():
auth = {
isLoggedIn: function () {
return true;
}
};
and apply is in a beforeEach loop
beforeEach(function () {
angular.mock.module(function ($provide) {
$provide.value('Auth', auth);
});
});
At the moment isLoggedIn() will always return true. This function needs to be able to return false for some particular tests.
Is there a way to pass a variable into the mock from my tests.
E.g. something like
var loggedIn = ;
auth = {
isLoggedIn: function () {
return loggedIn;
}
};
Yes, you can for instance put it inside your test:
var loggedIn;
beforeEach(function () {
auth = {
isLoggedIn: function () {
return loggedIn;
}
};
angular.mock.module(function ($provide) {
$provide.value('Auth', auth);
});
});
It is also possible to have it as a separate file, but for this example it seems overkill.
I'm trying to wrap the PushPlugin in a Angular factory, based on devgirls post, but so far without success.
angular.module('phonegap', [])
.factory('phonegapReady', function ($rootScope, $q) {
var loadingDeferred = $q.defer();
document.addEventListener('deviceready', function () {
$rootScope.$apply(loadingDeferred.resolve);
});
return function phonegapReady() {
return loadingDeferred.promise;
};
})
.factory('push', function ($rootScope, phonegapReady) {
return {
registerPush: phonegapReady().then(function (onSuccess, onError) {
// stripped handlers
if (device.platform === 'android' || device.platform === 'Android') {
pushNotification.register(
function () {
var that = this,
args = arguments;
if (onSuccess) {
$rootScope.$apply(function () {
onSuccess.apply(that, args);
});
}
},
function () {
var that = this,
args = {
'senderID': '123',
'ecb': 'onNotificationGCM'
};
if (onError) {
$rootScope.$apply(function () {
onError.apply(that, args);
});
}
}
);
} else {
pushNotification.register(
function () {
var that = this,
args = arguments;
if (onSuccess) {
$rootScope.$apply(function () {
onSuccess.apply(that, args);
});
}
},
function () {
var that = this,
args = {
'badge': 'true',
'sound': 'true',
'alert': 'true',
'ecb': 'onNotificationAPN'
};
if (onError) {
$rootScope.$apply(function () {
onError.apply(that, args);
});
}
}
);
}
})
};
});
Getting an error:
TypeError: '[object Object]' is not a function (evaluating 'e.registerPush(function(a){console.log("fun"),console.log(a)})')
What am I doing wrong?
When a you call then on a promise, it returns the promise so you can chain the callbacks.
I think wrapping registerPush with a function would work, like:
registerPush: function(onSuccess, onError) {
phonegapReady().then(function () {
// Do something with closured onSuccess and onError
});
},..