How do I pass a dependency to a Vue component? - javascript

I have a class that is intended to serve as a repository for making AJAX requests using axios:
export default class SomeRepository {
getEncryptedValue (value) {
return axios.get('http://localhost/api/v1/encrypt')
}
}
And I have a component that calls this repository's method inside the methods property in the component, like so:
<template>
...
</template>
<script>
import SomeRepository from '#/classes/SomeRepository'
export default {
data () {
return {
value: '',
result: ''
}
},
methods: {
encrypt () {
let someRepo = new SomeRepository()
someRepo.getEncryptedValue(this.value)
.then(response => {
this.result = response.data.result
})
}
}
}
</script>
How can I pass SomeRepository as a dependency? My goal is that I want to mock it in my unit tests.
I am looking for a straightforward solution, preferably one that does not involve third-party libraries or boilerplate.
I've tried also doing:
import Vue from 'vue'
import SomeRepository from '#/classes/SomeRepository'
Vue.use(SomeRepository)
// and
Vue.use(new SomeRepository())
which clearly don't work (honestly, I didn't expect them to). I also tried following this article https://codeburst.io/dependency-injection-with-vue-js-f6b44a0dae6d but I did not really like the approach of having to create a mixin every time I want to use a dependency.
I've also considered passing it as a prop, but was not sure where to instantiate the object before passing it.

There is a ton of ways to mock functions. The most simplistic that I have found is simply accepting dependencies as parameters of a function and replace the function to use in your tests.
Example of Method
// Function
function func(dependency) {
dependency.use()
}
// In App
func(dependency) // -> runs dependency.use
// In Tests
func(mockDependency) // -> runs mocked dependency.use
Your Example
Once you know this method you can do a ton of different versions of it but to show a super simple version.
encrypt() {
this._encrypt(SomeRepository)
}
_encrypt (SomeRepository) {
let someRepo = new SomeRepository()
someRepo.getEncryptedValue(this.value)
.then(response => {
this.result = response.data.result
})
}
You would test _encrypt with the mock dependency.
You can also do something like this.
encrypt (_SomeRepository=SomeRepository) {
let someRepo = new _SomeRepository()
someRepo.getEncryptedValue(this.value)
.then(response => {
this.result = response.data.result
})
}
If you pass in the mocked version it will use that if you do not it will use the real one.
The idea is to use this method however you see fit, but I think you get the idea. It is super simple no magic and no libraries needed.

You should use a Vue plugin
https://v2.vuejs.org/v2/guide/plugins.html
Then you can access it via e.g. this.$someDeps
For unit testing, you can mock it easily using Vue Test Utils https://vue-test-utils.vuejs.org/api/options.html#mocks

Related

Vue Composition API: Is there a better way in calling `emit` inside consumable file?

What is the correct way (or a better way) to access emit in the separated logic file?
This is what I did currently that works:
foo.js
export default (emit) => {
const foo = () => { emit('bar') };
return { foo };
}
Then on the consuming component:
import { defineComponent } from '#vue/composition-api';
import foo from './foo';
export default defineComponent({
setup(props, { emit }) {
const { foo } = foo(emit);
return { foo };
}
});
But I wonder if there is a more appropriate way on doing this? Or is it a bad practice to call emit inside a consumable file?
You probably have found the solution, but in case you would try similar way (as originally asked in question), there's this option called getCurrentInstance that has an emitter (the Vue 2 plugin for Composition API has one too).
import { getCurrentInstance } from 'vue';
export default () => {
const { emit } = getCurrentInstance();
const foo = () => {
emit('bar');
};
return { foo };
}
But bear in mind that this will only work for calling functions/components that have the SetupContext.
Edit
The above solution will work for Vue 3, yet with earlier version of Vue + the Composition API plugin, there is but a slight difference: As with the rest of the Instance Properties, you'll have to prefix it with $ to become $emit. (The following example now assumes Typescript as the target language, as mentioned on the comment).
import { getCurrentInstance } from '#vue/composition-api';
export default () => {
// Ugly workaround, since the plugin did not have the `ComponentInstance` type exported.
// You could use `any` here, but that would defeat the purpose of having type-safety, won't it?
// And we're marking it as `NonNullable` as the consuming components
// will most likely be (unless desired otherwise).
const { $emit, ...context } = getCurrentInstance() as NonNullable<ReturnType<typeof getCurrentInstance>>;
const foo = () => {
$emit.call(context, 'bar');
};
return { foo };
}
For Vue 3's Composition API, however, they do have this ComponentInternalInstance interface exported.
P.S. It's probably best to stick to the old-school way of assigning the instance to a variable and do context.$emit or vm.$emit rather than having to explicitly specify a context for everything. I initially came up with this idea without realizing that those Instance Properties are probably meant for internal uses, which is not exactly the case with the next Composition API.

Stubbing function with jest

I understand that this is the basic function of jest and I should be able to figure it out with the docs and other online resources, but somehow I cannot, so I apologise in advance if this is trivial.
I'm trying to test a function in Javascript that performs a few operations using other modules, as well as localStorage, and I would like to stub out the other modules and the call to localStorage. The documentation that I found for Jest seemed far too simplistic for me to adapt to my use case, like declaring a mock and then calling it inside the test - this doesn't happen in my case, as the function I want to mock is being called internally by my function, I'm not passing it in as a dependency. Let me give some code to explain: file name is dataInLocalStorage.js
import serialize from './serialize'; // simple module that serialises data
import deserialize from './deserialize'; // simple module that deserialises data
import findObject from './findObject'; // find an object in the deserialised data
const addDataToLocalStorage = (data) => {
const dataStored = deserialize(localStorage.getItem('data')); // fetch data from localStorage
const isStored = !!findObject(dataStored, data); // check whether the data I want to store is already there
if (isStored) { return null; } // if data is already stored, skip
const serializedData = serialize(data); // serialise data to be stored
return localStorage.setItem('data', serializedData); // store serialised data in localStorage
};
export { addDataToLocalStorage };
The purpose os this module is just to store data in localStorage in a serialised way, but in an additive way, so that adding data doesn't remove previously stored data, and no duplicates are added either.
Anyway, my test file looks like this: file name is dataInLocalStorage.test.js
import { addDataToLocalStorage } from '../dataInLocalStorage';
describe('addDataToLocalStorage', () => {
const deserialize = jest.fn();
beforeAll(() => {
localStorage.removeItem('data');
});
const data = {
name: 'johnny'
};
addDataToLocalStorage(data);
it('adds the data to local storage', () => {
expect(deserialize).toHaveBeenCalled();
});
});
Here is the rather unsurprising error for this attempt.
expect(jest.fn()).toHaveBeenCalled()
Expected mock function to have been called, but it was not called.
17 |
18 | it('adds the data to local storage', () => {
> 19 | expect(deserialize).toHaveBeenCalled();
| ^
20 | });
21 | });
On top of this I tried importing the deserialize function here in the test file and adding a jest.mock on that, which didn't work either.
Note that this isn't my code 100%, I have modified it for simplicity in order to make it easier to read for you, sorry if there are some slight mismatches, I tried my best to be as diligent as possible while converting it.
If you know what you're looking at, you'll see that this is obviously not working. Using other (more useful) expectations, the test was passing, but adding some console logs in the deserialize file showed that it's still running, when the idea is that I would like to mock it and provide my own return value.
Side note: I came from Ruby on Rails where mocking with RSpec is pretty simple, and I was hoping it would be just as simple with Jest. It likely is, but I can't wrap my head around it, as it doesn't seem possible to make a direct reference to the function/module I want to mock. In RSpec, doing allow(MySerializer).to receive(:call).and_return(...) would do the trick and I wouldn't have to worry about that module being called during the test.
When you set the value of deserialize to a jest mock, you are changing the variable value, not setting a reference that your code is using. To keep it a reference it needs to be a value in an object.
To import an object you can use import * as deserialize from "./deserialize";.
Then you can set the mock on the reference with deserialize.default = jest.fn().
https://codesandbox.io/s/88wlzp6q88
import { useIt } from "./use-default-export";
import * as myfunc from "./default-export-function";
test("use-default-export-function", () => {
expect(useIt()).toEqual("real");
});
test("use-default-export-function with mock", () => {
myfunc.default = jest.fn(() => "unreal");
expect(useIt()).toEqual("unreal");
});
in your test it'll be..
import { addDataToLocalStorage } from '../dataInLocalStorage';
import * as deserialize from './deserialize';
...
deserialize.default = jest.fn();
alternate TS compat version..
(which is actually cleaner all round..)
import { useIt } from "./use-default-export";
import myfunc from "./default-export-function";
jest.mock("./default-export-function", () => jest.fn());
test("use-default-export-function with mock", () => {
useIt();
expect(myfunc).toHaveBeenCalled();
});
return/resolve different values per test
(need to cast to jest.Mock to be able to use jest.fn() functions)
test("use-default-export-function with mock", () => {
const aFunc = myfunc as jest.Mock;
aFunc.mockResolvedValue("bar");
useIt();
expect(useIt()).resolves.toEqual("bar");
});
test("use-default-export-function with mock 2", () => {
const aFunc = myfunc as jest.Mock;
aFunc.mockReturnValue("foo");
useIt();
expect(useIt()).toEqual("foo");
});

Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?

This question pertains to testing javascript and mocking functions.
Say I have a module that looks like this:
export function alpha(n) {
return `${n}${beta(n)}${n}`;
}
export function beta(n) {
return new Array(n).fill(0).map(() => ".").join("");
}
Then I can't test it the following way:
import * as indexModule from "./index";
//Not what we want to do, because we want to mock the functionality of beta
describe("alpha, large test", () => {
it("alpha(1) returns '1.1'", () => {
expect(indexModule.alpha(1)).toEqual("1.1"); //PASS
});
it("alpha(3) returns '3...3'", () => {
expect(indexModule.alpha(3)).toEqual("3...3"); //PASS
});
});
//Simple atomic test
describe("beta", () => {
it("beta(3) returns '...'", () => {
expect(indexModule.beta(3)).toEqual("..."); //FAIL: received: 'x'
});
});
//Here we are trying to mutate the beta function to mock its functionality
describe("alpha", () => {
indexModule.beta = (n) => "x";
it("works", () => {
expect(indexModule.alpha(3)).toEqual("3x3"); //FAIL, recieved: '3...3'
});
});
However, if split the module into two:
alpha.js
import { beta } from "./beta";
export function alpha(n) {
return `${n}${beta(n)}${n}`;
}
beta.js
export function beta(n) {
return new Array(n).fill(0).map(() => ".").join("");
}
Then I can mutate the beta module, and alpha knows about it:
import { alpha } from "./alpha";
import * as betaModule from "./beta";
describe("alpha", () => {
betaModule.beta = (n) => "x";
it("works", () => {
expect(alpha(3)).toEqual("3x3"); //PASS
});
});
Why is this the case? I'm looking for a technically specific answer.
I have a Github branch with this code here, see the mutateModule and singleFunctionPerModuleAndMutate folders.
As an additional question - in this example I am mutating the module by directly reassigning properties. Am I right in understanding that using jest mock functionality is going to be essentially doing the same thing?
ie. If the reason that the first example doesn't work but the second doesn't is due to the mutation, then it necceserily means that using the jest module mocking functions is similarly not going to work.
As far as I know - there is not way to mock a single function in a module, while testing that module, as this jest github issues talks about. What I'm wanting to know - is why this is.
Why does mutating a module update the reference if calling that module from another module, but not if calling from itself?
"In ES6, imports are live read-only views on exported values".
When you import an ES6 module you essentially get a live view of what is exported by that module.
The live view can be mutated, and any code that imports a live view of the module exports will see the mutation.
That is why your test works when alpha and beta are in two different modules. The test modifies the live view of the beta module, and since the alpha module uses the live view of the beta module, it automatically uses the mocked function instead of the original.
On the other hand, in the code above alpha and beta are in the same module and alpha calls beta directly. alpha does not use the live view of the module so when the test modifies the live view of the module it has no effect.
As an additional question - in this example I am mutating the module by directly reassigning properties. Am I right in understanding that using jest mock functionality is going to be essentially doing the same thing?
There are a few ways to mock things using Jest.
One of the ways is using jest.spyOn which accepts an object and a method name and replaces the method on the object with a spy that calls the original method.
A common way to use jest.spyOn is to pass it the live view of an ES6 module as the object which mutates the live view of the module.
So yes, mocking by passing the live view of an ES6 module to something like jest.spyOn (or spyOn from Jasmine, or sinon.spy from Sinon, etc.) mutates the live view of the module in essentially the same way as directly mutating the live view of the module like you are doing in the code above.
As far as I know - there is not way to mock a single function in a module, while testing that module, as this jest github issues talks about. What I'm wanting to know - is why this is.
Actually, it is possible.
"ES6 modules support cyclic dependencies automatically" which means that the live view of a module can be imported into the module itself.
As long as alpha calls beta using the live view of the module that beta is defined in, then beta can be mocked during the test. This works even if they are defined in the same module:
import * as indexModule from './index' // import the live view of the module
export function alpha(n) {
return `${n}${indexModule.beta(n)}${n}`; // call beta using the live view of the module
}
export function beta(n) {
return new Array(n).fill(0).map(() => ".").join("");
}
What I find interesting is that none of your code would work in a browser.
Module ("./some/path/to/file.js"):
const x = () => "x"
const y = () => "y"
export { x, y }
You cannot modify a named import as they are constants:
import { x } from "./some/path/to/file.js"
x = () => {} //Assignment to constant variable.
You also cannot assign to a readonly property of a namespace import.
import * as stuff from "./some/path/to/file.js"
stuff.y = () => {} //Cannot assign to read only property 'y' of...
Here's a codepen which also shows why indexModule.alpha !== alpha from the module: https://codepen.io/bluewater86/pen/QYwMPa
You are using the module to encapsulate your two functions but for the reasons above, this is a bad idea. You really need to encapsulate those functions in a class so that you can mock them appropriately.
//alphaBeta.js
export const beta = n => new Array(n).fill(0).map(() => ".").join("");
export default class alphaBeta {
static get beta() { return beta }
beta(n) {
beta(n)
}
alpha(n) {
return `${n}${this.beta(n)}${n}`;
}
}
export { alphaBeta }
And finally by moving to default/named imports instead of namespace imports you will have no need to use the cyclic dependency hack. Using default/named imports means that you will be importing the same in-memory view of the exports that the module exported. i.e. importer.beta === exporter.beta
import alphaBetaDefault, { alphaBeta, beta } from "./alphaBeta.js"
alphaBeta.prototype.beta = (n) => "x";
describe("alphaBeta", () => {
it("Imported function === exported function", () => {
expect(alphaBeta.beta).toEqual(beta); //PASS
});
const alphaBetaObject = new alphaBeta
it("Has been mocked", () => {
expect(alphaBetaObject.alpha(3)).toEqual("3x3");
});
alphaBeta.prototype.beta = (n) => "z";
it("Is still connected to its prototype", () => {
expect(alphaBetaObject.alpha(3)).toEqual("3z3");
});
const secondObject = new alphaBetaDefault
it("Will still be mocked for all imports of that module", () => {
expect(secondObject.alpha(3)).toEqual("3z3");
});
});

How to mock an asynchronous function call in another class

I have the following (simplified) React component.
class SalesView extends Component<{}, State> {
state: State = {
salesData: null
};
componentDidMount() {
this.fetchSalesData();
}
render() {
if (this.state.salesData) {
return <SalesChart salesData={this.state.salesData} />;
} else {
return <p>Loading</p>;
}
}
async fetchSalesData() {
let data = await new SalesService().fetchSalesData();
this.setState({ salesData: data });
}
}
When mounting, I fetch data from an API, which I have abstracted away in a class called SalesService. This class I want to mock, and for the method fetchSalesData I want to specify the return data (in a promise).
This is more or less how I want my test case to look like:
predefine test data
import SalesView
mock SalesService
setup mockSalesService to return a promise that returns the predefined test data when resolved
create the component
await
check snapshot
Testing the looks of SalesChart is not part of this question, I hope to solve that using Enzyme. I have been trying dozens of things to mock this asynchronous call, but I cannot seem to get this mocked properly. I have found the following examples of Jest mocking online, but they do not seem to cover this basic usage.
Hackernoon: Does not use asychronous calls
Wehkamp tech blog: Does not use asynchronous calls
Agatha Krzywda: Does not use asynchronous calls
GitConnected: Does not use a class with a function to mock
Jest tutorial An Async Example: Does not use a class with a function to mock
Jest tutorial Testing Asynchronous Code: Does not use a class with a function to mock
SO question 43749845: I can't connect the mock to the real implementation in this way
42638889: Is using dependency injection, I am not
46718663: Is not showing how the actual mock Class is implemented
My questions are:
How should the mock class look like?
Where should I place this mock class?
How should I import this mock class?
How do I tell that this mock class replaces the real class?
How do set up the mock implementation of a specific function of the mock class?
How do I wait in the test case for the promise to be resolved?
One example that I have that does not work is given below. The test runner crashes with the error throw err; and the last line in the stack trace is at process._tickCallback (internal/process/next_tick.js:188:7)
# __tests__/SalesView-test.js
import React from 'react';
import SalesView from '../SalesView';
jest.mock('../SalesService');
const salesServiceMock = require('../SalesService').default;
const weekTestData = [];
test('SalesView shows chart after SalesService returns data', async () => {
salesServiceMock.fetchSalesData.mockImplementation(() => {
console.log('Mock is called');
return new Promise((resolve) => {
process.nextTick(() => resolve(weekTestData));
});
});
const wrapper = await shallow(<SalesView/>);
expect(wrapper).toMatchSnapshot();
});
Sometimes, when a test is hard to write, it is trying to tell us that we have a design problem.
I think a small refactor could make things a lot easier - make SalesService a collaborator instead of an internal.
By that I mean, instead of calling new SalesService() inside your component, accept the sales service as a prop by the calling code. If you do that, then the calling code can also be your test, in which case all you need to do is mock the SalesService itself, and return whatever you want (using sinon or any other mocking library, or even just creating a hand rolled stub).
You could potentially abstract the new keyword away using a SalesService.create() method, then use jest.spyOn(object, methodName) to mock the implementation.
import SalesService from '../SalesService ';
test('SalesView shows chart after SalesService returns data', async () => {
const mockSalesService = {
fetchSalesData: jest.fn(() => {
return new Promise((resolve) => {
process.nextTick(() => resolve(weekTestData));
});
})
};
const spy = jest.spyOn(SalesService, 'create').mockImplementation(() => mockSalesService);
const wrapper = await shallow(<SalesView />);
expect(wrapper).toMatchSnapshot();
expect(spy).toHaveBeenCalled();
expect(mockSalesService.fetchSalesData).toHaveBeenCalled();
spy.mockReset();
spy.mockRestore();
});
One "ugly" way I've used in the past is to do a sort of poor-man's dependency injection.
It's based on the fact that you might not really want to go about instantiating SalesService every time you need it, but rather you want to hold a single instance per application, which everybody uses. In my case, SalesService required some initial configuration which I didn't want to repeat every time.[1]
So what I did was have a services.ts file which looks like this:
/// In services.ts
let salesService: SalesService|null = null;
export function setSalesService(s: SalesService) {
salesService = s;
}
export function getSalesService() {
if(salesService == null) throw new Error('Bad stuff');
return salesService;
}
Then, in my application's index.tsx or some similar place I'd have:
/// In index.tsx
// initialize stuff
const salesService = new SalesService(/* initialization parameters */)
services.setSalesService(salesService);
// other initialization, including calls to React.render etc.
In the components you can then just use getSalesService to get a reference to the one SalesService instance per application.
When it comes time to test, you just need to do some setup in your mocha (or whatever) before or beforeEach handlers to call setSalesService with a mock object.
Now, ideally, you'd want to pass in SalesService as a prop to your component, because it is an input to it, and by using getSalesService you're hiding this dependency and possibly causing you grief down the road. But if you need it in a very nested component, or if you're using a router or somesuch, it's becomes quite unwieldy to pass it as a prop.
You might also get away with using something like context, to keep everything inside React as it were.
The "ideal" solution for this would be something like dependency injection, but that's not an option with React AFAIK.
[1] It can also help in providing a single point for serializing remote-service calls, which might be needed at some point.

Write global functions to use in all components in angular

Note : Its not for global variable but for a global common function to perform a functionality on all components
I am working on an angular app where I have around 400 components in different modules, almost all components have one same kind of functionality as mentioned below
There is a sections on many pages which shows a "How to work section" which can be closed by users and will remain closed unless they open it again, I have done it with cookies which I set on click on close or open icon but this function is written in a component and this needs to be imported in other components
I want to create a functions somewhere which perform this functionality on click on icon and can be called without importing any component in others.
One way to do it ( as I thought ) could be create a JavaScript function in a file and load it in index file and then call this function on click on close and open icon
Not sure if this is the best way to do this. Hope someone will come up with a better solution.
1. create your global function service, i.e. 'funcs.services.ts' under 'services' directory:
import { Injectable } from '#angular/core';
#Injectable({
providedIn: 'root'
})
export class FuncsService {
constructor() { }
myGlobalAddFunction(a){
return a++;
}
mySecondFunc(){
// add more... and so on
}
}
2. Import the function in your component:
// your path may different
import { FuncsService } from './../services/funcs/funcs.service';
//...
constructor(
private funcs: FuncsService
) {}
ngOnInit(): void {
let x = 1;
myResult = this.funcs.myGlobalAddFunction(x);
// Then you are expecting 2 for return value
}
3. Hope that works... :)
You can export a function that is a written in .ts file and then call it in all your components.
export function myFunction(){
// Do something
}
And then import the function myFunction() in other components. That works fine for me and can be useful in certain cases
This isn't the best solution (in my opinion). The best solution would be to either create a service, or an utils class.
But if you want to do this, I suggest you make a JS file, that you declare in your angular-cli.json file under the scripts property, containing your functions.
EDIT Now that you've came back to reason, here is a code sample to make utils classes.
export const IMG_UTILS = {
convertPngToJpg = (picture: any) => {
// Your logic here
}
};
export const VIEW_MANAGER = {
isAdblockActive = () => {
// test if an ad-blocker is active
}
};
You can make any utils class you want in a const, then put it into a file. Then, you can put this file in an utils folder, and request it with
import { VIEW_MANAGER } from 'src/app/utils/view-manager';
Otherwise, you can make a service, which is handled by Angular, with a console command such as
ng g s services/view-manager/view-manager
And it will behave the exact same way : you will import it in your components to use it.
Hope this helps !
The most recommended way is to use a service and inject it whenever needed, but there is a way to have a function available globally.
Although I don't think it's a really good idea, you can add the function in the index.html file, then whenever you want to use it, you have to use #ts-ignore to avoid an error from being thrown.
e.g
index.html
<script>
function globalFunc(){
alert(2)
}
</script>
anywhere else on the app
// #ts-ignore
globalFunc();
List item
Just to chime in with possibly a duplicate answer albeit more fleshed out... I have a utilities class which I use.
For example:
export class Utilities {
// Simple promise wrapper for setTimeout. See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises#creating_a_promise_around_an_old_callback_api
public static Wait = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
}
The class is referenced in a component via the import statement:
import { Utilities } from 'path/to/Utilities';
And then you can call your static methods thus:
Utilities.Wait(30000)
.then(() => DoStuff())
.catch(() => console.log('Darn!'));
I would tend to use RxJs but I've written it this way to keep things a little cleaner.

Categories

Resources