Dart/Flutter: Share common setUp/tearDown methods among test suites - unit-testing

Is there a way to share common setUp/tearDown methods and other helper methods among tests in Dart/Flutter? Usually in other languages/frameworks, like Java or Python, tests are defined in classes which can be extended, but the way Dart approaches testing by using functions inside a main method lets me perplexed. For instance I'm testing io methods, I have the following piece of code in my test:
Directory tempDir;
setUp(() async {
tempDir = await Directory.systemTemp.createTemp();
const MethodChannel('plugins.flutter.io/path_provider').setMockMethodCallHandler((MethodCall methodCall) async {
if (methodCall.method == 'getApplicationDocumentsDirectory') {
return tempDir.path;
}
return null;
});
});
tearDown(() async {
tempDir.delete(recursive: true);
});
If it were any other language I would create a base class or a mixin called TempDirTestCase in which to put the code for the creation/deletion of the temporary directory, then each suite that need such functions would have just to inherit from it... but what I'm supposed to do in dart/flutter to reuse the code and avoid copy/paste?

Create a file called flutter_test_config.dart in the same directory as your test (or at a higher level, it'll affect all tests in that directory and below).
In that file, you can then call the setUp and tearDown methods before your test file's main method runs:
import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
Future<void> testExecutable(FutureOr<void> Function() testMain) async {
setUp(() {
print('Shared setUp');
});
tearDown(() {
print('Shared tearDown');
});
await testMain();
}
Documentation: https://api.flutter.dev/flutter/flutter_test/flutter_test-library.html

Related

How to mock Fastify plugin

I am trying to write unit testing for fastify application which also has custom fastify plugin.
Is there a way we can mock fastify plugin? I tried mocking using Jest and Sinon without much success.
Giorgios link to the file is broken, the mocks folder is now absent from the master branch. I dug the commit history to something around the time of his answer and I found a commit with the folder still there. I leave it here for those who will come in the future!
This is what works for me
Setup your plugin according to Fastify docs https://www.fastify.io/docs/latest/Reference/Plugins/
// establishDbConnection.ts
import fp from 'fastify-plugin';
import {FastifyInstance, FastifyPluginAsync} from 'fastify';
import { initDbConnection } from './myDbImpl';
const establishDbConnection: FastifyPluginAsync = async (fastify: FastifyInstance, opts) => {
fastify.addHook('onReady', async () => {
await initDbConnection()
});
};
export default fp(establishDbConnection);
mock the plugin with jest, make sure you wrap the mock function in fp() so that Fastify recognizes it as a plugin.
// myTest.ts
import fp from 'fastify-plugin';
const mockPlugin = fp(async () => jest.fn());
jest.mock('../../../fastifyPlugin/establishDbConnection', (() => {
return mockPlugin;
}));
Your question is a bit generic but if you are using Jest it must be enough for mocking a fastify plugin. You can take a look in this repo and more specifically this file . This is a mock file of fastify and you add the registered plugins and in the specific example addCustomHealthCheck and then in your test files you can just call jest.mock('fastify').
You do not give a specific use case and there are lot of reasons you might want to mock a plugin. The nature of the plugin to be mocked is important to giving a good answer. Because I don't know that specific information I will show how to mock a plugin that creates a decorator that stores data that can be retrieved with fastify.decorator-name. This is a common use case for plugins that connect to databases or store other widely needed variables.
In the below case, the goal is to test a query function that queries a db; a plugin stores the connection information via a fastify decorator. So, in order to unit test the query we specifically need to mock the client data for the connection.
First create an instance of fastify. Next, set up a mock to return the desired fake response. Then, instead of registering the component with fastify (which you could also do), simply decorate the required variables directly with mock information.
Here is the function to be tested. We need to mock a plugin for a database which creates a fastify decorator called db. Specifically, in the below case the function to be tested uses db.client:
const fastify = require("fastify")({ //this is here to gather logs
logger: {
level: "debug",
file: "./logs/combined.log"
}
});
const HOURS_FROM_LOADDATE = "12";
const allDataQuery = `
SELECT *
FROM todo_items
WHERE a."LOAD_DATE" > current_date - interval $1 hour
`;
const queryAll = async (db) => {
return await sendQuery(db, allDataQuery, [HOURS_FROM_LOADDATE]);
};
//send query to db and receive data
const sendQuery = async (db, query, queryParams) => {
var res = {};
try {
const todo_items = await db.client.any(query, queryParams);
res = todo_items;
} catch (e) {
fastify.log.error(e);
}
return res;
};
module.exports = {
queryByAsv
};
Following is the test case. We will mock db.client from the db plugin:
const { queryAll } = require("../src/query");
const any = {
any: jest.fn(() => {
return "mock response";
})
};
describe("should return db query", () => {
beforeAll(async () => {
// set up fastify for test instance
fastify_test = require("fastify")({
logger: {
level: "debug",
file: "./logs/combined.log",
prettyPrint: true
}
});
});
test("test Query All", async () => {
// mock client
const clientPromise = {
client: any
};
//
fastify_test.decorate("db", clientPromise);
const qAll = await queryAll(fastify_test.db);
expect(qAll).toEqual("mock response");
});
});

Aurelia testing components with transient dependencies never uses a mock

We are using the aurelia component testing as defined here (with jest): https://aurelia.io/docs/testing/components#testing-a-custom-element
The component we are testing has a transient dependency. We are creating a mock for this dependency but when we run the tests using au jest, the real one always gets injected by the DI container and never the mock.
Here is the Transient service:
import { transient } from "aurelia-framework";
#transient()
export class ItemService {
constructor() {
}
getItems(): void {
console.log('real item service');
}
}
Here is the 'Mock' service (we have also tried using jest mocks but we get the same result):
import { transient } from "aurelia-dependency-injection";
#transient()
export class MockItemService{
getItems():void {
console.log('mock item service');
}
}
Here is the component under test:
import {ItemService} from "../services/item-service";
import { autoinject } from "aurelia-dependency-injection";
#autoinject()
export class TestElement {
constructor(private _itemService: ItemService) {
}
attached(): void {
this._itemService.getItems();
}
}
Here is the spec file:
import {TestElement} from "../../src/resources/elements/test-element";
import {ComponentTester, StageComponent} from "aurelia-testing";
import {ItemService} from "../../src/resources/services/item-service";
import {MockItemService} from "./mock-item-service";
import {bootstrap} from "aurelia-bootstrapper";
describe('test element', () => {
let testElement;
const path: string = '../../src/resources/elements/test-element';
beforeEach(() => {
testElement = StageComponent.withResources(path).inView(`<test-element></test-element>`);
testElement.bootstrap(aurelia => {
aurelia.use.standardConfiguration();
aurelia.container.registerTransient(ItemService, MockItemService);
});
});
afterEach(() => {
testElement.dispose();
});
it('should call mock item service', async() => {
await testElement.create(bootstrap);
expect(testElement).toBeTruthy();
})
});
But every-time the test is run, the console logs out the real service and not the mock. I have traced this to the aurelia-dependency-injection.js in the Container.prototype.get function. The issue seems to be around this section of code:
var registration = aureliaMetadata.metadata.get(aureliaMetadata.metadata.registration, key);
if (registration === undefined) {
return this.parent._get(key);
}
The registration object seems to be a bit odd, if it was undefined, the code would work as the correct dependency is registered on the parent and it would get the mock dependency. However, it is not undefined therefore it registers the real service in the DI container on this line:
return registration.registerResolver(this, key, key).get(this, key);
The registration object looks like this:
registration = TransientRegistration {_key = undefined}
Is this a bug in aurelia or is there something wrong with what I am doing?
Many Thanks
p.s. GitHub repo here to replicate the issue: https://github.com/Magrangs/aurelia-transient-dependency-issue
p.p.s Forked the DI container repo and added a quick fix which would fix my particular issue but not sure what the knock on effects would be. If a member of the aurelia team could check, that would be good:
https://github.com/Magrangs/dependency-injection/commit/56c7d96a496e76f330a1fc3f9c4d62700b9ed596
After talking to Rob Eisenberg on the issue there is a workaround for this problem. Firstly remove the #transient decorator on the class and then in your app start (usually main.ts) register the class there as a transient.
See the thread here:
https://github.com/Magrangs/dependency-injection/commit/56c7d96a496e76f330a1fc3f9c4d62700b9ed596
I have also updated the repo posted above: https://github.com/Magrangs/aurelia-transient-dependency-issue
to include the fix.
Hopefully this will help any other devs facing the same issue.

Mocking a Flow.js interface with Jest?

How might a Flow.js interface be mocked with Jest? To my surprise, I haven't found this issue addressed anywhere.
I'm fairly new to both, but the only (untested) option I see is to create a class that inherits from the interface and then mock the implementing class. This seems quite cumbersome and I don't believe I could place the implementing classes (which are what would actually be mocked) inside the __mocks__ folders expected by Jest and still get the expected behavior.
Any suggestions? Is there a more appropriate mocking tool?
Update
Why do I want to create a mock for an interface? This code intends to have a clean separation of the domain and implementation layers, with the domain classes using Flow interfaces for all injected dependencies. I want to test these domain classes. Using a mocking tool could ideally allow me to more easily and expressively modify the behavior of the mocked services and confirm that the domain class being tested is making the appropriate calls to these mocked services.
Here's a simplified example of a class that I would be testing in this scenario. UpdateResources would be the class under test, while ResourceServer and ResourceRepository are interfaces for services that I would like to mock and 'spy' upon:
// #flow
import type { ResourceServer } from '../ResourceServer';
import type { ResourceRepository } from '../ResourceRepository';
/**
* Use case for updating resources
*/
export default class UpdateResources {
resourceServer: ResourceServer;
resourceRepository: ResourceRepository;
constructor(resourceServer: ResourceServer, resourceRepository: ResourceRepository) {
this.resourceServer = resourceServer;
this.resourceRepository = resourceRepository;
}
async execute(): Promise<boolean> {
const updatesAvailable = await this.resourceServer.checkForUpdates();
if (updatesAvailable) {
const resources = await this.resourceServer.getResources();
await this.resourceRepository.saveAll(resources);
}
return updatesAvailable;
}
}
A solution
The approach I've arrived at which seems to work quite well for my purposes is to create a mock implementation of the interface in the __mocks__ directory what exposes jest.fn objects for all implemented methods. I then instantiate these mock implementations with new and skip any use of jest.mock().
__mocks__/MockResourceServer.js
import type { ResourceServer } from '../ResourceServer';
export default class MockResourceServer implements ResourceServer {
getResources = jest.fn(() => Promise.resolve({}));
checkForUpodates = jest.fn(() => Promise.resolve(true));
}
__mocks__/MockResourceRepository.js
import type { ResourceRepository } from '../ResourceRepository';
export default class MockResourceRepository implements ResourceRepository {
saveAll = jest.fn(() => Promise.resolve());
}
__tests__/UpdateResources.test.js
import UpdateResources from '../UpdateResources';
import MockResourceRepository from '../../__mocks__/MockResourceRepository';
import MockResourceServer from '../../__mocks__/MockResourceServer';
describe('UpdateResources', () => {
describe('execute()', () => {
const mockResourceServer = new MockResourceServer();
const mockResourceRepository = new MockResourceRepository();
beforeEach(() => {
jest.clearAllMocks();
});
it('should check the ResourceServer for updates', async () => {
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceServer.checkForUpdates).toHaveBeenCalledTimes(1);
});
it('should save to ResourceRepository if updates are available', async () => {
mockResourceServer.load.mockResolvedValue(true);
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceRepository.saveAll).toHaveBeenCalledTimes(1);
});
it('should NOT save to ResourceRepository if NO updates are available', async () => {
mockResourceServer.load.mockResolvedValue(false);
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceRepository.saveAll).not.toHaveBeenCalled();
});
});
});
If anyone can offer any improvements, I'm open!
The thing is, you don't actually need to mock an implementation of an interface. The purpose of a mock is to 'look like' the real thing, but if you already have an interface that says what the real thing should look like, any implementation that conforms to the interface will automatically serve equally well as a mock. In fact, from the point of view of the typechecker, there won't be a different between the 'real' and the 'mock' implementation.
Personally what I like to do is to create a mock implementation that can be constructed by feeding it mock responses. Then it can be reused in any test case by constructing it directly in that test case with the exact responses it should provide. I.e., you 'script' the mock with what it should say by injecting the responses at the time of construction. The difference between it and your mocking implementation is that if it doesn't have a response, it throws a exception and fails the test. Here's an article I wrote that shows this method: https://dev.to/yawaramin/interfaces-for-scaling-and-testing-javascript-1daj
With this technique, a test case might look like this:
it('should save to ResourceRepository if updates are available', async () => {
const updateResources = new UpdateResources(
new MockResourceServer({
checkForUpdates: [true],
getResources: [{}],
}),
new MockResourceRepository({
saveAll: [undefined],
}),
);
const result = await updateResources.execute();
expect(result).toBeTruthy();
});
What I like about these mocks is that all the responses are explicit, and show you the sequence of calls that are happening.

How do you mock a service in AngularJS when unit testing with jasmine?

Let's say I have a service shop that depends on two stateful services schedule and warehouse. How do I inject different versions of schedule and warehose into shop for unit testing?
Here's my service:
angular.module('myModule').service('shop', function(schedule, warehouse) {
return {
canSellSweets : function(numRequiredSweets){
return schedule.isShopOpen()
&& (warehouse.numAvailableSweets() > numRequiredSweets);
}
}
});
Here are my mocks:
var mockSchedule = {
isShopOpen : function() {return true}
}
var mockWarehouse = {
numAvailableSweets: function(){return 10};
}
Here are my tests:
expect(shop.canSellSweets(5)).toBe(true);
expect(shop.canSellSweets(20)).toBe(false);
beforeEach(function () {
module(function ($provide) {
$provide.value('schedule', mockSchedule);
});
});
Module is a function provided by the angular-mocks module. If you pass in a string argument a module with the corresponding name is loaded and all providers, controllers, services, etc are available for the spec. Generally they are loaded using the inject function. If you pass in a callback function it will be invoked using Angular's $injector service. This service then looks at the arguments passed to the callback function and tries to infer what dependencies should be passed into the callback.
Improving upon Atilla's answer and in direct answer to KevSheedy's comment, in the context of module('myApplicationModule') you would do the following:
beforeEach(module('myApplicationModule', function ($provide) {
$provide.value('schedule', mockSchedule);
}));
With CoffeeScript I run in some issues so I use null at the end:
beforeEach ->
module ($provide) ->
$provide.value 'someService',
mockyStuff:
value : 'AWESOME'
null
You can look here for more info
https://docs.angularjs.org/guide/services#unit-testing
You want to utilize the $provide service. In your case
$provide.value('schedule', mockSchedule);
As you are using jasmine, there is an alternative way to mock the calls with jasmine's spies (https://jasmine.github.io/2.0/introduction.html#section-Spies).
Using these you can be targeted with your function calls, and allow call throughs to the original object if required. It avoids clogging up the top of your test file with $provide and mock implementations.
In the beforeEach of your test I would have something like:
var mySchedule, myWarehouse;
beforeEach(inject(function(schedule, warehouse) {
mySchedule = schedule;
myWarehouse = warehouse;
spyOn(mySchedule, 'isShopOpen').and.callFake(function() {
return true;
});
spyOn(myWarehouse, 'numAvailableSweets').and.callFake(function() {
return 10;
});
}));
and this should work in similar fashion to the $provide mechanism, noting you have to provide local instances of the injected variables to spy on.
I recently released ngImprovedTesting module that should make mock testing in AngularJS way easier.
In your example you would only have to replace in your Jasmine test the ...
beforeEach(module('myModule'));
... with ...
beforeEach(ModuleBuilder.forModule('myModule').serviceWithMocks('shop').build());
For more information about ngImprovedTesting check out its introductory blog post:
http://blog.jdriven.com/2014/07/ng-improved-testing-mock-testing-for-angularjs-made-easy/
It is simpler to put the mock on the module like this:
beforeEach(function () {
module('myApp');
module({
schedule: mockSchedule,
warehouse: mockWarehouse
}
});
});
you can use injection to get reference to these mocks for pre test manipulations :
var mockSchedule;
var mockWarehouse;
beforeEach(inject(function (_schedule_, _warehouse_) {
mockSchedule = _schedule_;
mockWarehouse = _warehouse_;
}));
I hope my answer is not that useless, but you can mock services by $provide.service
beforeEach(() => {
angular.mock.module(
'yourModule',
($provide) => {
$provide.service('yourService', function() {
return something;
});
}
);
});

Unit testing the config phase in AngularJS

I'm trying to learn how to write unit tests for AngularJS. I started at the beginning, with
angular.module( ... ).config( ... )
I wanna test what's inside config. Here's how the relevant portions look like:
angular.module('ogApp', ['ngCookies','ui.router','ogControllers','ogServices','ogDirectives','ogMetricsData'])
.config([
'$stateProvider', '$locationProvider',
function ($stateProvider, $locationProvider) {
$stateProvider.
state('login', {
templateUrl: 'connect.html'
}).state('addViews', {
templateUrl: 'add-views.html'
}).state('dashboard', {
templateUrl: 'dashboard.html'
});
$locationProvider.
html5Mode(true).
hashPrefix('!');
}
]);
I'm thinking the easiest way to test this code is to inject mocks for $stateProvider and $locationProvider. Then execute the config phase. After that, assert how $stateProvider and $locationProvider should look like.
If my thinking is right, my problem then is, I have no idea how to inject those mocks into the module and execute its config phase from a test.
Could you show me how to test this code?
Here's how to access your provider for unit testing:
describe('yourProvider', function () {
var provider;
// Get the provider
beforeEach(module('app', function (yourProvider) {
// This callback is only called during instantiation
provider = yourProvider;
});
// Kick off the above function
beforeEach(inject(function () {}));
it('does its thing', function () {
expect(provider.someMethod()).toEqual('your results');
});
});
I have not yet figured out a really simple way to inject a mock, but you can easily spy on methods and that's close enough. If you need a mock returned from the dependency provider's .$get() method you can do that with another spy as well. This example illustrates returning a mock and setting up an additional spy.
describe('yourProvider', function () {
var dependency, mock, provider;
beforeEach(module('app', function (dependencyProvider) {
dependency = dependencyProvider;
mock = jasmine.createSpyObj('dependency', [
'methodsGoHere'
]);
spyOn(dependency, 'methodName');
spyOn(dependency, '$get').andReturn(mock);
}, function (yourProvider) {
provider = yourProvider;
});
beforeEach(inject(function () {}));
it('does its thing', function () {
expect(provider.someMethod()).toEqual('your results');
expect(dependency.methodName).toHaveBeenCalled();
});
it('returns the mock from $get', function () {
expect(dependency.$get).toBe(mock);
});
});
You can use Jasmine's createSpy and createSpyObj to create the mock services, and
angular-mocks.js to inject them.
More instructions on injecting mocks here: Injecting a mock into an AngularJS service
In this test I wrote for a directive of mine you can see the following:
Line 9 Include angular-mock from the google cdn
Line 19 & 20 Create a fake rootScope object
Line 21 & 22 Create a fake q service
Line 42 Setup the provider to inject the fakes into the service
Line 48 Instantiate the service that has the fakes (this service is injected into the directive under test)
Line 53 Call the method being tested
Line 55 - 59 Assert on the state of the fakes
I would create a factory that points to a function... that function is then also called within the config function. That way you can unit test the factory:
angular.module('ogApp', ['ngCookies','ui.router','ogControllers','ogServices','ogDirectives','ogMetricsData']);
// Configuration factory for unit testing
angular.module('ogApp')
.factory('configuration', configuration);
configuration.$inject = ['$stateProvider', '$locationProvider'];
function configuration($stateProvider, $locationProvider) {
return {
applyConfig: function () {
$stateProvider.
state('login', {
templateUrl: 'connect.html'
}).state('addViews', {
templateUrl: 'add-views.html'
}).state('dashboard', {
templateUrl: 'dashboard.html'
});
$locationProvider.
html5Mode(true).
hashPrefix('!');
};
}
// Call above configuration function from Angular's config phase
angular.module('ogApp')
.config([
'$stateProvider', '$locationProvider',
function ($stateProvider, $locationProvider) {
var config = configuration($stateProvider, $locationProvider);
config.applyConfig();
}
]);
You can unit test the configuration factory and inject mocks just like you would with any other factory.