I see two ways of mocking services in NestJS for unit testing, the first one is the same as we define providers in real modules like:
const module = await Test.createTestingModule({
providers: [
UserService,
{
provide: getRepositoryToken(User),
useValue: mockUsersRepository,
}
],
}).compile();
And the other way with overrideProvider method. As following:
const module = await Test.createTestingModule({
imports: [UserModule]
})
.overrideProvider(getRepositoryToken(User))
.useValue(mockUsersRepository)
.compile();
What is the difference?
So let me try to explain it this way:
overrideProvider is useful when you've imported an entire module and need to override something it has as a provider. A use case, like the answer mentioned, would be overriding a logger. So say you have
const modRef = await Test.createTestingModule({
import: [AuthModule]
}).compile();
And assume that AuthModule has imports: [ LoggerModule ]. In our test, we don't really want to see all the logs created, but we can't provide a custom provider for the LoggerService because it's being imported and used via the LoggerModule (overriding an injection token isn't really a common practice). So to provide our own implementation forLoggerService (let's say we just need a noop log method) we can do the following
const modRef = await Test.createTestingModule({
import: [AuthModule]
})
.overrideProvider(LoggerService)
.useValue({ log: () => { /* noop */ } })
.compile();
And now when our AuthService calls this.logger.log() it will just call this noop and be done with it.
On the flip side, if we're doing unit testing, usually you don't need to overrideProvider because you just set up the provider and the custom provider directly in the testing module's metadata and use that.
The overrideProvider is really useful when you have to use imports (like integration and e2e tests), otherwise, generally, it's better to use a custom provider
The difference is pretty simple.
With the first approach (array of providers), you create custom testing module to test (probably) the UserService.
With second approach, you use complete module in the very same shape as it is used in the application itself.
The result is exactly the same - your mock is being injected into the constructor of UserService.
The first approach is better for small, mostly unit tests, but these tests can be also done without using NestJS test tooling at all (just pass mock manually to the ctor), while the second one does a great job in integration tests.
Repository is not great example to use to explain, but think about Logger.
You are performing some integration tests of 2 or more modules.
You do not want to manually create big testing module (which also is breaking the connection with real shape of your modules), but you want to just import your modules which are being tested together and .overrideProvider for Logger with e.g. loggerMock which lets you to assert all logger calls across all tested modules.
Example:
#Module({providers: [LoggerService], exports: [LoggerService]})
export class LoggerModule {}
#Module({imports: [LoggerModule], providers: [FooService]})
export class FooModule {}
#Module({imports: [LoggerModule], providers: [BarService]})
export class BarModule {}
#Module({imports: [FooModule, BarModule]}
export class AppModule {}
// TEST
const testModule = await Test.createTestingModule({
import: [AppModule]
})
.overrideProvider(LoggerService)
.useValue(/* your logger mock will be provided in both FooService and BarService and you can easily test all related to logs then */)
.compile();
I hope it is clear. If not, please leave a comment and I will try to explain more.
Related
I'm reading through the Vue Testing Cookbook and Vue Test Utils's docs, where they touch on testing components with Vuex. Both sources advise using createLocalVue, but I don't fully understand why. We already have a couple of tests that use Vuex but don't use createLocalVue, and they work. So why do these sources suggest using createLocalVue?
Here's the test that appears to be a bad practice according to those sources. Is this code going to break something down the road? Is it causing unwanted side-effects we're not aware of?
import { mount } from '#vue/test-utils';
import Vuex from 'vuex';
import Component from 'somewhere';
// We don't use this and yet the code seems to work
// const localVue = createLocalVue()
// localVue.use(Vuex)
describe('Foo Component', () => {
let wrapper;
beforeEach(() => {
wrapper = mount(Component, {
// localVue, <-- We don't use this
store: new Vuex.Store({
modules: {
app: {
namespaced: true,
// ... more store stuff
}
}
})
})
});
it('should contain foo', () => {
expect(wrapper.contains('.foo')).toBe(true);
});
});
From docs:
localVue: A local copy of Vue created by createLocalVue to use when mounting the component. Installing plugins on this copy of Vue prevents polluting the original Vue copy.
In your tests, you might want to make particular changes and install plugins on the tested Vue instance. Using localVue ensures those changes are reset for every test.
An obvious advantage is you don't have to install all plugins for all tests. So your tests will be faster.
Also, when your tests get a bit more complex, if you don't use localVue you'll experience tests failing erratically based on their order, because the previously ran test, even though it passed, modified the Vue instance in a way that breaks your next test. But the next test passes when ran in isolation.
The type of errors which typically imply hair loss.
Using localVue provides a type of certainty most developers welcome when running tests. If you feel adventurous, don't use it.
It's a recommendation, not an imposition.
I am trying to test a function in one of my component which consists following two lines:
this.rzp1 = new Razorpay(orderDetails);
this.rzp1.open();
I am trying to understand how to mock Razorpay in my test cases for this function.
This is how I am declaring Razorpay in my component:
export declare var Razorpay: any;
I have already tried various methods like:
var stub = sinon.createStubInstance(MyConstructor)
Any leads will be helpful.
Why not use jasmine's built in spy facility instead of relying on another library (sinon)?
In your before each block, you can do something like this:
beforeEach(() => {
jasmine.spyOnAllFunctions(Razorypay.prototype);
Razorypay.prototype.open.and.returnValue('foo');
});
You can find more information about spyOnAllFuntions in the documentation.
How would one test that a piece of custom middleware is actually called from a standard HTTP event?
ie. The middleware is called from:
MyController.js
router.get('/some/endpoint', [myMiddleware()], (req, res, next) => {
// Code to do whatever here
});
The middleware itself can be defined as:
MyMiddleware.js
module.exports = () => {
// Middleware code in here
}
My quest is to check that the middleware is called once from my unit test, but I cannot find documentation around this.
MyTest.test.js
it('Should return whatever from GET call', () => {
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {res.body.should.deep.equal(bodyValue)});
// How would I place code in here to check that MyMiddleware is called?
// ie. sinon.assert.calledOnce(MyMiddleware)
});
I have thought about using Sinon's spy, but I can't think of how to hook into the middleware... My attempt was this:
const mwSpy = sinon.spy(require('path to middleware file'));
sinon.assert(calledOnce(mwSpy));
The usual way of going about this is splitting this into two tests, an integration test and a unit test.
Will the middleware I specified in the router.get call end up being called when someone hits this endpoint?
Does my middleware do the right thing?
The first part is basically testing that the Express API is doing what the documentation says. That's not what unit tests are for (this was tagged unit-testing), but since you are already using HTTP requests to test the endpoint, I guess that's not what you are after anyway: you are basically creating verification tests for your system.
You could still test the Express routing without HTTP, though, as I detail in the answer to this question, concerning how to test the router programmatically (faster tests, no http), but less just stick to what you have.
So the basic question is: "My quest is to check that the middleware is called once from my unit test". You don't seem to concern yourself with whether the middleware is doing the right thing or not, just that it's called, which calls for the question on whether we should test the middleware or the layer using the middleware.
In both cases, you need to find a way of injecting a test spy. Either you write a small utility method that will inject that spy: function setMiddleware(module){ middleware = module; } or you use some tooling like proxyquire. See this tutorial on Sinon's homepage for background.
I would just do this (in the test code):
it('Should return whatever from GET call', () => {
var middlewareFake = sinon.fake();
// I am assuming it's the actual app object you are referencing below in the request(app) line
var app = proxyquire('../app/index.js', { './my-middleware': middlewareFake });
//
return request(app).get('/some/endpoint')
.expect(200)
.expect(res => {
res.body.should.deep.equal(bodyValue)
expect(middlewareFake).was.called;
});
});
I have the following services that return Bookshelf model data. The server is built on express. My core question is: I want to write tests for the services. Secondarily, I'm not sure if the services tests should include interaction with db; as you can see below, the services are currently intertwined with express.
// services.js
import Region from "../../models/region";
import Subregion from "../../models/subregion";
import bookshelf from "../../bookshelf.config";
/** Regions **/
export const getRegions = (req, res, next) => {
Region.forge()
.fetchAll()
.then(regions => {
log.info("Got all regions");
res.status(200).json(regions);
})
.catch(next);
};
/** Subegions **/
export const getSubregions = (req, res, next) => {
Subregion.forge()
.fetchAll({
columns: ["id", "name"],
})
.then(subregions => {
res.status(200).json(subregions);
})
.catch(next);
};
Questions
1. Whats is the proper way to test a function like getRegions?
2. Do best practices require getRegions and getSubregions to be extracted from the express context?
You have to analyze what your function does and what is the best way to test it. Looking at the getRegions function it just fetches all models of a certain type and returns the result as JSON to the user.
Given this, you have very little logic of your own, it's just a little bit of glue between two modules, so it doesn't make sense to unit test that function because you would just be testing if the used modules (Bookshelf and Express) are working properly, which should be beyond the scope of your project.
However, you probably do want to test if your API is responding correctly with various user inputs, so you should do some integration testing with something like SuperTest.
As for testing with an actual database or mocking it I think that's just a matter of personal opinion or project goals.
getRegions is just a function. You'd test it like a normal function. You would need to mock the Express' res, req, and next objects. In addition to the Express objects, you would need to mock Bookshelf/knex as well since you don't want to depend on an actual database. See this answer for bookshelf testing.
It is already extracted from Express' context since you defined it as a function. If you had defined it as app.post('/example' (req, res, next) => {}), then that would be coupled with Express.
I have a function in angular 2 service which I would like to test.
service.ts
upload(){
let file = new Transfer();
file.upload(myfile).then( // my callback );
}
I would like to mock Transfer in my test using jasmine. I tried this in my
sevice.spec.ts
import { TransferMock as Transfer } from '../mocks/mocks' to mock it. But it is not working. This is how my test is instantiated .
describe('authentication service' , () => {
beforeEach(() => {
auth = new Service(<any>new HttpMock(), <any>new StorageMock())
});
it('initialize authentication',() => {
expect(auth).not.toEqual(null);
auth.upload('file'); //it fails here
});
})
edit
Transfer is not injected in the service. Only one function uses Transfer . So not injecting can reduce the initial loading time of the app i guess(would be happy to know other opinions) . So I would like to know if there is anyway to mock if its constructed this way ?
edit
Although I had accepted Martin's answer as it is the best practice, it has one issue which can happen when you use ionic-native plugins.If the plugin doesnt have browser support it can fail. In this case it happened when I inject it, with error FileTransfer is not defined . So I am back again, looking for suggestions.
In order to provide a mock for a class in a test, you need to inject the class in your implementation.
In your ngModule add Transfer to your providers. Then simply inject it into your service.
Then in your test you can use { provide: Transfer, useClass: TransferMock } in your TestBed providers.
Update
The primary purpose of Dependency Injection is to make code testable and to allow mocking - faking - stubbing of services.
Update
With Dependancy Injection you can configure a different set of providers for different environments.
For example, if you are running your application in the browser, and in a native mobile environment you can swap out your configuration.
In your module you could have something like this:
const TRANSFER_PROVIDER: any;
if (environment.browser) {
TRANSFER_PROVIDER = Transfer;
} else {
TRANSFER_PROVIDER = { provide: Transfer, useClass: NativeTransfer }
}
...
providers: [ TRANSFER_PROVIDER ]
NativeTransfer could be a simple stub that does nothing but prevent errors, or it could let the user know that this feature is not supported in their browser.