Unit testing sessionStorage value in emberJS - unit-testing

I'm new to ember and trying to figure out how to unit test, using sinon, the sessionStorage based on url parameters when that page is visited. I've tried a few things but still can't get the desired result. It passes even if I change the 'sessionValue' without editing the query param.
Thank you in advance.
ember component
beforeModel(transition) {
//transition will contain an object containing a query parameter. '?userid=1234' and is set in the sessionStorage.
if(transition.queryparam.hasOwnProperty('userid')){
sessionStorage.setItem('user:id', transition.queryparam)
}
}
Ember test
test('Session Storage contains query param value', async assert => {
let sessionKey = "user:id";
let sessionValue = "1234"
let store = {};
const mockLocalStorage = {
getItem: (key) => {
return key in store ? store[key] : null;
},
setItem: (key, value) => {
store[key] = `${value}`;
},
clear: () => {
store = {};
}
};
asserts.expect(1);
let spy = sinon.spy(sessionStorage, "setItem");
spy.calledWith(mockLocalStorage.setItem);
let stub = sinon.stub(sessionStorage, "getItem");
stub.calledWith(mockLocalStorage.getItem);
stub.returns(sessionValue);
await visit('/page?userid=1234');
mockLocalStorage.setItem(sessionKey, sessionValue);
assert.equal(mockLocalStorage.getItem(sessionKey), sessionValue, 'storage contains value');
})

Welcome to Ember!
There are many ways to test, and the below suggestion is one way (how I would approach interacting with the SessionStorage).
Instead of re-creating the SessionStorage API in your test, how do you feel about using a pre-made proxy around the Session Storage? (ie: "Don't mock what you don't own")
Using: https://github.com/CrowdStrike/ember-browser-services/#sessionstorage
Your app code would look like:
#service('browser/session-storage') sessionStorage;
beforeModel(transition) {
// ... details omitted ...
// note the addition of `this` -- the apis are entirely the same
// as SessionStorage
this.sessionStorage.setItem('user:id', ...)
}
then in your test:
module('Scenario Name', function (hooks) {
setupApplicationTest(hooks);
setupBrowserFakes(hooks, { sessionStorage: true });
test('Session Storage contains query param value', async assert => {
let sessionKey = "user:id";
let sessionValue = "1234"
let sessionStorage = this.owner.lookup('browser/session-storage');
await visit('/page?userid=1234');
assert.equal(sessionStorage.getItem(sessionKey), '1234', 'storage contains value');
});
})
With this approach, sinon isn't even needed :)

Related

Dart Testing with Riverpod StateNotifierProvider and AsyncValue as state

This is my first app with Dart/Flutter/Riverpod, so any advice or comment about the code is welcome.
I'm using Hive as embedded db so the initial value for the provider's state is loaded asynchronously and using an AsyncValue of riverpod to wrapped it.
The following code works but I've got some doubts about the testing approach, so I would like to confirm if I'm using the Riverpod lib as It supposed to be used.
This is my provider with its deps (Preferences is a HiveObject to store app general config data):
final hiveProvider = FutureProvider<HiveInterface>((ref) async {
return await App.setUp();
});
final prefBoxProvider = FutureProvider<Box<Preferences>>((ref) async {
final HiveInterface hive = await ref.read(hiveProvider.future);
return hive.openBox<Preferences>("preferences");
});
class PreferencesNotifier extends StateNotifier<AsyncValue<Preferences>> {
late Box<Preferences> prefBox;
PreferencesNotifier(Future<Box<Preferences>> prefBoxFuture): super(const AsyncValue.loading()) {
prefBoxFuture.then((value) {
prefBox = value;
_loadCurrentPreferences();
});
}
void _loadCurrentPreferences() {
Preferences pref = prefBox.get(0) ?? Preferences();
state = AsyncValue.data(pref);
}
Future<void> save(Preferences prefs) async {
await prefBox.put(0, prefs);
state = AsyncValue.data(prefs);
}
Preferences? get preferences {
return state.when(data: (value) => value,
error: (_, __) => null,
loading: () => null);
}
}
final preferencesProvider = StateNotifierProvider<PreferencesNotifier, AsyncValue<Preferences>>((ref) {
return PreferencesNotifier(ref.read(prefBoxProvider.future));
});
And the following is the test case, I'm mocking the Hive box provider (prefBoxProvider):
class Listener extends Mock {
void call(dynamic previous, dynamic value);
}
Future<Box<Preferences>> prefBoxTesting() async {
final hive = await App.setUp();
Box<Preferences> box = await hive.openBox<Preferences>("testing_preferences");
await box.clear();
return box;
}
void main() {
test('Preferences value changes', () async {
final container = ProviderContainer(overrides: [
prefBoxProvider.overrideWithValue(AsyncValue.data(await prefBoxTesting()))
],);
addTearDown(() {
container.dispose();
Hive.deleteBoxFromDisk("testing_preferences");
});
final listener = Listener();
container.listen<AsyncValue<Preferences>>(
preferencesProvider,
listener,
fireImmediately: true,
);
verify(listener(null, const TypeMatcher<AsyncLoading>())).called(1);
verifyNoMoreInteractions(listener);
// Next line waits until we have a value for preferences attribute
await container.read(preferencesProvider.notifier).stream.first;
verify(listener(const TypeMatcher<AsyncLoading>(), const TypeMatcher<AsyncData>())).called(1);
Preferences preferences = Preferences.from(container.read(preferencesProvider.notifier).preferences!);
preferences.currentListName = 'Lista1';
await container.read(preferencesProvider.notifier).save(preferences);
verify(listener(const TypeMatcher<AsyncData>(), const TypeMatcher<AsyncData>())).called(1);
verifyNoMoreInteractions(listener);
final name = container.read(preferencesProvider.notifier).preferences!.currentListName;
expect(name, equals('Lista1'));
});
}
I've used as reference the official docs about testing Riverpod and the GitHub issue related with AsyncValues
Well, I found some problems to verify that the listener is called with the proper values, I used the TypeMatcher just to verify that the state instance has got the proper type and I check ("manually") the value of the wrapped object's attribute if It's the expected one. Is there a better way to achieve this ?
Finally, I didn't find too many examples with StateNotifier and AsyncValue as state type, Is there a better approach to implement providers that are initialized with deferred data ?
I didn't like too much my original approach so I created my own Matcher to compare wrapped values in AsyncValue instances:
class IsWrappedValueEquals extends Matcher {
final dynamic value;
IsWrappedValueEquals(this.value);
#override
bool matches(covariant AsyncValue actual, Map<dynamic, dynamic> matchState) =>
equals(actual.value).matches(value, matchState);
#override
Description describe(Description description) => description.add('Is wrapped value equals');
}
In the test, the final part is a bit different:
Preferences preferences = Preferences.from(container.read(preferencesProvider.notifier).preferences!);
preferences.currentListName = 'Lista1';
await container.read(preferencesProvider.notifier).save(preferences);
// the following line is the new one
verify(listener(IsWrappedValueEquals(Preferences()), IsWrappedValueEquals(preferences))).called(1);
verifyNoMoreInteractions(listener);
}
I prefer my custom Matcher to the original code, but I feel that there are too many custom code to test something, apparently, common.
If anyone can tell me a better solution for this case, It'd be great.

How to properly test functions that return Mongoose queries as Promises

I'm trying to write a basic unit test to work on the function below, but can't get it to work. How do I test that something like a proper npm-express response is returned?
I already looked at Using Sinon to stub chained Mongoose calls, https://codeutopia.net/blog/2016/06/10/mongoose-models-and-unit-tests-the-definitive-guide/, and Unit Test with Mongoose, but still can't figure it out. My current best guess, and the resulting error, is below the function to be tested. If possible, I don't want to use anything but Mocha, Sinon, and Chai.expect (i.e. not sinon-mongoose, chai-as-expected, etc.). Any other advice, like what else I can/should test here, is welcome. Thank you!
The function to be tested:
function testGetOneProfile(user_id, res) {
Profiles
.findOne(user_id)
.exec()
.then( (profile) => {
let name = profile.user_name,
skills = profile.skills.join('\n'),
data = { 'name': name, 'skills': skills };
return res
.status(200)
.send(data);
})
.catch( (err) => console.log('Error:', err));
}
My current best-guess unit test:
const mongoose = require('mongoose'),
sinon = require('sinon'),
chai = require('chai'),
expect = chai.expect,
Profile = require('../models/profileModel'),
foo = require('../bin/foo');
mongoose.Promise = global.Promise;
describe('testGetOneProfile', function() {
beforeEach( function() {
sinon.stub(Profile, 'findOne');
});
afterEach( function() {
Profile.findOne.restore();
});
it('should send a response', function() {
let mock_user_id = 'U5YEHNYBS';
let expectedModel = {
user_id: 'U5YEHNYBS',
user_name: 'gus',
skills: [ 'JavaScript', 'Node.js', 'Java', 'Fitness', 'Riding', 'backend']
};
let expectedResponse = {
'name': 'gus',
'skills': 'JavaScript, Node.js, Java, Fitness, Riding, backend'
};
let res = {
send: sinon.stub(),
status: sinon.stub()
};
sinon.stub(mongoose.Query.prototype, 'exec').yields(null, expectedResponse);
Profile.findOne.returns(expectedModel);
foo.testGetOneProfile(mock_user_id, res);
sinon.assert.calledWith(res.send, expectedResponse);
});
});
The test message:
1) testGetOneProfile should send a response:
TypeError: Profiles.findOne(...).exec is not a function
at Object.testGetOneProfile (bin\foo.js:187:10)
at Context.<anonymous> (test\foo.test.js:99:12)
This is a bit of a tricky scenario. The problem here is that the findOne stub in your test returns the model object - instead, it needs to return an object which contains a property exec which in turn is a promise-returning function that finally resolves into the model value... yeah, as mentioned, it's a bit tricky :)
Something like this:
const findOneResult = {
exec: sinon.stub().resolves(expectedModel)
}
Profile.findOne.returns(findOneResult);
You also need to have the status function on the response object return an object containing a send function
//if we set up the stub to return the res object
//it returns the necessary func
res.status.returns(res);
I think you shouldn't need to change anything else in the test and it might work like that. Note that you sinon 2.0 or newer for the resolves function to exist on the stub (or you can use sinon-as-promised with sinon 1.x)
This post goes into a bit more detail on how you can deal with complex objects like that:
https://codeutopia.net/blog/2016/05/23/sinon-js-quick-tip-how-to-stubmock-complex-objects-such-as-dom-objects/

How to mock DialogService.open(...).whenClosed(...) with Jasmine?

We have some TypeScript code using the Aurelia framework and Dialog plugin that we are trying to test with Jasmine, but can't work out how to do properly.
This is the source function:
openDialog(action: string) {
this._dialogService.open({ viewModel: AddAccountWizard })
.whenClosed(result => {
if (!result.wasCancelled && result.output) {
const step = this.steps.find((i) => i.action === action);
if (step) {
step.isCompleted = true;
}
}
});
}
We can create a DialogService spy, and verify the open method easily - but we can't work out how to make the spy invoke the whenClosed method with a mocked result parameter so that we can then assert that the step is completed.
This is the current Jasmine code:
it("opens a dialog when clicking on incomplete bank account", async done => {
// arrange
arrangeMemberVerificationStatus();
await component.create(bootstrap);
const vm = component.viewModel as GettingStartedCustomElement;
dialogService.open.and.callFake(() => {
return { whenClosed: () => Promise.resolve({})};
});
// act
$(".link, .-arrow")[0].click();
// assert
expect(dialogService.open).toHaveBeenCalledWith({ viewModel: AddAccountWizard });
expect(vm.steps[2].isCompleted).toBeTruthy(); // FAILS
done();
});
We've just recently updated our DialogService and ran into the same issue, so we've made this primitive mock that suited our purposes so far. It's fairly limited and doesn't do well for mocking multiple calls with different results, but should work for your above case:
export class DialogServiceMock {
shouldCancelDialog = false;
leaveDialogOpen = false;
desiredOutput = {};
open = () => {
let result = { wasCancelled: this.shouldCancelDialog, output: this.desiredOutput };
let closedPromise = this.leaveDialogOpen ? new Promise((r) => { }) : Promise.resolve(result);
let resultPromise = Promise.resolve({ closeResult: closedPromise });
resultPromise.whenClosed = (callback) => {
return this.leaveDialogOpen ? new Promise((r) => { }) : Promise.resolve(typeof callback == "function" ? callback(result) : null);
};
return resultPromise;
};
}
This mock can be configured to test various responses, when a user cancels the dialog, and scenarios where the dialog is still open.
We haven't done e2e testing yet, so I don't know of a good way to make sure you wait until the .click() call finishes so you don't have a race condition between your expect()s and the whenClosed() logic, but I think you should be able to use the mock in the test like so:
it("opens a dialog when clicking on incomplete bank account", async done => {
// arrange
arrangeMemberVerificationStatus();
await component.create(bootstrap);
const vm = component.viewModel as GettingStartedCustomElement;
let mockDialogService = new MockDialogService();
vm.dialogService = mockDialogService; //Or however you're injecting the mock into the constructor; I don't see the code where you're doing that right now.
spyOn(mockDialogService, 'open').and.callThrough();
// act
$(".link, .-arrow")[0].click();
browser.sleep(100)//I'm guessing there's a better way to verify that it's finished with e2e testing, but the point is to make sure it finishes before we assert.
// assert
expect(mockDialogService.open).toHaveBeenCalledWith({ viewModel: AddAccountWizard });
expect(vm.steps[2].isCompleted).toBeTruthy(); // FAILS
done();
});

How do I mock an Angular service using jasmine?

This may be a duplicate but I have looked at a lot of other questions here and they usually miss what I am looking for in some way. They mostly talk about a service they created themselves. That I can do and have done. I am trying to override what angular is injecting with my mock. I thought it would be the same but for some reason when I step through the code it is always the angular $cookieStore and not my mock.
I have very limited experience with jasmine and angularjs. I come from a C# background. I usually write unit tests moq (mocking framework for C#). I am use to seeing something like this
[TestClass]
public PageControllerTests
{
private Mock<ICookieStore> mockCookieStore;
private PageController controller;
[TestInitialize]
public void SetUp()
{
mockCookieStore = new Mock<ICookieStore>();
controller = new PageController(mockCookieStore.Object);
}
[TestMethod]
public void GetsCarsFromCookieStore()
{
// Arrange
mockCookieStore.Setup(cs => cs.Get("cars"))
.Return(0);
// Act
controller.SomeMethod();
// Assert
mockCookieStore.VerifyAll();
}
}
I want mock the $cookieStore service which I use in one of my controllers.
app.controller('PageController', ['$scope', '$cookieStore', function($scope, $cookieStore) {
$scope.cars = $cookieStore.get('cars');
if($scope.cars == 0) {
// Do other logic here
.
}
$scope.foo = function() {
.
.
}
}]);
I want to make sure that the $cookieStore.get method is invoked with a 'garage' argument. I also want to be able to control what it gives back. I want it to give back 0 and then my controller must do some other logic.
Here is my test.
describe('Controller: PageController', function () {
var controller,
scope,
cookieStoreSpy;
beforeEach(function () {
cookieStoreSpy = jasmine.createSpyObj('CookieStore', ['get']);
cookieStoreSpy.get.andReturn(function(key) {
switch (key) {
case 'cars':
return 0;
case 'bikes':
return 1;
case 'garage':
return { cars: 0, bikes: 1 };
}
});
module(function($provide) {
$provide.value('$cookieStore', cookieStoreSpy);
});
module('App');
});
beforeEach(inject(function(_$httpBackend_, $rootScope, $controller) {
scope = $rootScope.$new();
controller = $controller;
}));
it('Gets car from cookie', function () {
controller('PageController', { $scope: scope });
expect(cookieStoreSpy.get).toHaveBeenCalledWith('cars');
});
});
This is a solution for the discussion we had in my previous answer.
In my controller I'm using $location.path and $location.search. So to overwrite the $location with my mock I did:
locationMock = jasmine.createSpyObj('location', ['path', 'search']);
locationMock.location = "";
locationMock.path.andCallFake(function(path) {
console.log("### Using location set");
if (typeof path != "undefined") {
console.log("### Setting location: " + path);
this.location = path;
}
return this.location;
});
locationMock.search.andCallFake(function(query) {
console.log("### Using location search mock");
if (typeof query != "undefined") {
console.log("### Setting search location: " + JSON.stringify(query));
this.location = JSON.stringify(query);
}
return this.location;
});
module(function($provide) {
$provide.value('$location', locationMock);
});
I didn't have to inject anything in the $controller. It just worked. Look at the logs:
LOG: '### Using location set'
LOG: '### Setting location: /test'
LOG: '### Using location search mock'
LOG: '### Setting search location: {"limit":"50","q":"ani","tags":[1,2],"category_id":5}'
If you want to check the arguments, spy on the method
// declare the cookieStoreMock globally
var cookieStoreMock;
beforeEach(function() {
cookieStoreMock = {};
cookieStoreMock.get = jasmine.createSpy("cookieStore.get() spy").andCallFake(function(key) {
switch (key) {
case 'cars':
return 0;
case 'bikes':
return 1;
case 'garage':
return {
cars: 0,
bikes: 1
};
}
});
module(function($provide) {
$provide.value('cookieStore', cookieStoreMock);
});
});
And then to test the argument do
expect(searchServiceMock.search).toHaveBeenCalledWith('cars');
Here is an example https://github.com/lucassus/angular-seed/blob/81d820d06e1d00d3bae34b456c0655baa79e51f2/test/unit/controllers/products/index_ctrl_spec.coffee#L3 it's coffeescript code with mocha + sinon.js but the idea is the same.
Basically with the following code snippet you could load a module and substitute its services:
beforeEach(module("myModule", function($provide) {
var stub = xxx; //... create a stub here
$provide.value("myService", stub);
}));
Later in the spec you could inject this stubbed service and do assertions:
it("does something magical", inject(function(myService) {
subject.foo();
expect(myService).toHaveBeenCalledWith("bar");
}));
More details and tips about mocking and testing you could find in this excellent blog post: http://www.yearofmoo.com/2013/09/advanced-testing-and-debugging-in-angularjs.html
Why mock cookieStore when you may use it directly without modification? The code below is a partial unit test for a controller which uses $cookieStore to put and get cookies. If your controller has a method known as "setACookie" that uses $cookieStore.put('cookieName', cookieValue) ... then the test should be able to read the value that was set.
describe('My controller', function() {
var $cookieStore;
describe('MySpecificController', function() {
beforeEach(inject(function(_$httpBackend_, $controller, _$cookieStore_) {
$cookieStore = _$cookieStore_;
// [...] unrelated to cookieStore
}));
it('should be able to reference cookies now', function () {
scope.setACookie();
expect($cookieStore.get('myCookieName')).toBe('setToSomething');
});
});

How can i test a AngularJS provider?

I need to test my own angular provider, and I need to test it in both config and run phase to check that config methods work and that the instantiated provider is indeed configured with the correct parameters.
When I ask dependancy injection for the provider, it can't find the APIResourceFactoryProvider, only the APIResourceFactory, and I haven't found any examples of this on the repositories I've looked trough so far.
It's actually a lot simpler than it would at first seem to test a provider in AngularJS:
describe('Testing a provider', function() {
var provider;
beforeEach(module('plunker', function( myServiceProvider ) {
provider = myServiceProvider;
}));
it('should return true on method call', inject(function () {
expect( provider.method() ).toBeTruthy();
}));
});
```
The proof is in the Plunker: http://plnkr.co/edit/UkltiSG8sW7ICb9YBZSH
Just in case you'd like to have a minification-proof version of your provider, things become slightly more complicated.
Here is the provider code:
angular
.module('core.services')
.provider('storageService', [function () {
function isLocalStorageEnabled(window) {
return true;
}
this.$get = ['$window', 'chromeStorageService', 'html5StorageService',
function($window, chromeStorageService, html5StorageService) {
return isLocalStorageEnabled($window) ? html5StorageService : chromeStorageService;
}];
}]);
The test case:
describe('Storage.Provider', function() {
var chrome = {engine: 'chrome'};
var html5 = {engine: 'html5'};
var storageService, provider;
beforeEach(module('core.services'));
beforeEach(function () {
module(function (storageServiceProvider) {
provider = storageServiceProvider;
});
});
beforeEach(angular.mock.module(function($provide) {
$provide.value('html5StorageService', html5);
$provide.value('chromeStorageService', chrome);
}));
// the trick is here
beforeEach(inject(function($injector) {
storageService = $injector.invoke(provider.$get);
}));
it('should return Html5 storage service being run in a usual browser', function () {
expect(storageService).toBe(html5);
});
});
In this case $get is an array and you can't just call it as a usual function providing dependencies as arguments. The solution is to use $injector.invoke().
That's strange that most tutorials and samples miss this detail.
here is a little helper that properly encapsulates fetching providers, hence securing isolation between individual tests:
/**
* #description request a provider by name.
* IMPORTANT NOTE:
* 1) this function must be called before any calls to 'inject',
* because it itself calls 'module'.
* 2) the returned function must be called after any calls to 'module',
* because it itself calls 'inject'.
* #param {string} moduleName
* #param {string} providerName
* #returns {function} that returns the requested provider by calling 'inject'
* usage examples:
it('fetches a Provider in a "module" step and an "inject" step',
function() {
// 'module' step, no calls to 'inject' before this
var getProvider =
providerGetter('module.containing.provider', 'RequestedProvider');
// 'inject' step, no calls to 'module' after this
var requestedProvider = getProvider();
// done!
expect(requestedProvider.$get).toBeDefined();
});
*
it('also fetches a Provider in a single step', function() {
var requestedProvider =
providerGetter('module.containing.provider', 'RequestedProvider')();
expect(requestedProvider.$get).toBeDefined();
});
*/
function providerGetter(moduleName, providerName) {
var provider;
module(moduleName,
[providerName, function(Provider) { provider = Provider; }]);
return function() { inject(); return provider; }; // inject calls the above
}
the process of fetching the provider is fully encapsulated: no need for closure variables that compromise isolation between tests.
the process can be split in two steps, a 'module' step and an 'inject' step, which can be appropriately grouped with other calls to 'module' and 'inject' within a unit test.
if splitting is not required, retrieving a provider can simply be done in a single command!