I want to test a simple Angular 2 data service. The service uses Http, but nothing else. In the quickstart guide it says:
However, it's often more productive to explore the inner logic of
application classes with isolated unit tests that don't depend upon
Angular. Such tests are often smaller and easier to read, write, and
maintain.
The example of writing an isolated unit test it gives is that for simple services you can just test the service by creating a new instance of it in each test... maybe something like:
beforeEach(() => { service = new EventDataService(); });
it('#getEvents should return an observable', () => {
expect(service.getEvents()).toBe(Observable.from([]);
});
However, my EventDataService uses Http, so I get an error if I don't put Http in the constructor like so:
beforeEach(() => { service = new EventDataService(http: Http); });
But Http doesn't exist unless I import it, which I don't want to do - I don't want to test Http. I tried stubbing http out, but all the ways I tried ended up failing or leading me to import even MORE things to satisfy the Typescript gods...
I'm sure I'm over thinking this. I have tried the suggestions on quite a few sites that talk about testing in Angular 2, but anything older than a few months is suspect to me since the framework has changed so much in the last 6-12 months. I feel like I should be able to keep this simple for such a simple example.
Am I doing something obvious wrong?
I am using Angular 2 V 2.4.10, Webpack 2.3.1, Sinon 2.1.0, and Typescript 2.2.1.
Service:
import { Injectable } from "#angular/core";
import { Http } from "#angular/http";
import { Observable } from "rxjs";
import { Event } from "../event/event.interface";
#Injectable()
export class EventDataService {
events: Event[];
constructor(private http: Http) { }
getEvents(): Observable<Event[]> {
return this.http.get("api/events")
.map((response) => {return response.json(); })
}
};
Spec:
import { Http } from "#angular/http";
import { EventDataService } from "./event-data.service";
import * as sinon from "sinon";
import { expect } from "chai";
describe("Event Data Service", () => {
it("GetEvents", () => {
sinon.stub(Http, "get").returns(Promise.resolve("sinon Event!"));
let eventDataService = new EventDataService();
expect(eventDataService.getEvents()).to.equal("sinon event!");;
});
});
Thank you!
Although i wholeheartedly agree with the general sentiment that you should use TestBed in this scenario to stub out your Http dependency (after all, that's a huge motivator for why Angular has dependency injection in the first place), I'm seeing some errors in your approach which seems to indicate some misunderstanding.
Your EventDataService has a constructor which expects a single parameter of type Http. Therefore, whenever you want to create an instance of your EventDataService manually, you have to use the constructor and pass a single parameter of type Http.
So, you should be doing:
let dataService = new EventDataService(x);
where x is a variable of type Http. What may not be obvious is that Typescript is not a language like Java - Typescript can get out of your way if you want it to. So, you could, for eg, just create a new object, and say it's of type 'Http' and the Typescript compiler will assume you know what you're doing and let you proceed.
So you could do:
let x = ({ } as Http);
let dataService = new EventDataService(x);
The first line is telling the compiler that i want the type of {} to be Http, and Typescript will get out of your way and assume you know what you're doing.
So, you can use that technique to get an 'instance' of Http for your testing - i use the word 'instance' very loosely.
However, if you look at your EventDataService, it expects that the http object that gets passed to its constructor to have a get method that returns an object that you can call map on. In other words, it expects get to return an Observable. So, if you want to fake out Http for testing purposes, your fake Http instance needs to have a get method, and that get method needs to return an Observable.
Putting all of the above together, if i wanted to write my own test without using TestBed, I'd end up with:
let fakeHttp = {
get: (_: any) => {}
};
// I'm not familiar with sinon,
// but i believe this is stubbing the get method of fakeHttp
// and returning a canned response
sinon.stub(fakeHttp, "get").returns(Observable.of("sinon Event!"));
let eventDataService = new EventDataService(fakeHttp);
// remember, getEvents returns an observable,
// so to test it you have to subscribe to it and check its values
eventDataService.getEvents().subscribe(data => {
expect(data).to.equal("sinon Event!");
});
Would i approach this this way? Probably not - I'd just use TestBed to get a fake Http instance (not because this approach is that difficult, but just because TestBed simplifies things when you have multiple dependencies, and services always seem to grow that way). But at the end of the day, constructor dependency injection, like what Angular uses, is pretty easy to understand - pass your dependencies (fake or real) as parameters to the constructor of the class you're trying to create an instance of.
You should need to create a testing module and mock the response to test the service methods provide dependencies in your testing module
import { async, getTestBed, TestBed, inject } from "#angular/core/testing";
import { Response, ResponseOptions, HttpModule, XHRBackend } from "#angular/http";
import { MockBackend, MockConnection } from "#angular/http/testing";
import { EventDataService } from "./event-data.service";
describe("EventDataService", () => {
let mockBackend: MockBackend;
let service: EventDataService;
let injector: Injector;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpModule],
providers: [
{ provide: XHRBackend, useClass: MockBackend },
EventDataService
]
});
injector = getTestBed();
});
beforeEach(() => {
mockBackend = injector.get(XHRBackend);
service = injector.get(EventDataService);
});
Related
I am not sure how to implement unit test in nestjs and typeorm without connecting to db. I have tried a number of technic but non seem to work.
My module looks something like this.
import { HttpModule, Module } from '#nestjs/common'
import moment from 'moment';
import config from '#app/config'
import { OrdersService } from './services/order.service'
import { FraudOrderChecksService } from './services/fraud-order-checks.service'
import { FraudOrderChecksController } from './controllers/fraud-order-checks.controller'
import { HealthcheckController } from './controllers/healthcheck.controller';
import { TypeOrmModule } from '#nestjs/typeorm'
import { ormconfig } from './entities/ormconfig'
#Module({
imports: [
SharedModule,
HttpModule,
LoggerModule,
ConfigModule.forRoot(config),
TypeOrmModule.forRoot(ormconfig.luminskin as any),
TypeOrmModule.forRoot(ormconfig.meridian as any),
TypeOrmModule.forFeature([...ormconfig.luminskin.entities], 'luminskin'),
TypeOrmModule.forFeature([...ormconfig.meridian.entities], 'meridian'),
...
],
controllers: [
MyController,
...
],
providers: [
...
],
})
export class AppModule { }
I import the root module in my test
beforeEach(async () => {
jest.clearAllMocks();
const module: TestingModule = await Test.createTestingModule({
imports: [InternalModule]
}).compile();
...
});
When I try to run my unit test I get
[Nest] 93196 - 06/04/2021, 17:43:50 [ExceptionHandler] Unable to connect to the database (mydb). Retrying (1)...
AlreadyHasActiveConnectionError: Cannot create a new connection named "connectionname", because connection with such name already exist and it now has an active connection session.
How do I decouple the connection from the root module, so it is only ran when needed. Actually cleaner technic will also be accepted.
You don't want to unit test a module, you want to unit test a modules individual components in isolation.
Although you can create a TestModule and simply import your module as you have done above, I would only consider doing that when the module contained a single component (even then I wouldn't as I don't think its very good practice).
The more components you bring into your test:
The more moving parts you need to manage
The more you have to mock
The less portability you have with the unit and its test
The more aspirin you ingest trying resolve self induced headaches that occur every time you modify its parent module
Nests TestingModule enables you to "rig" up an independent module with the bare minimum needed to test your component in isolation. It simplifies your test setups and mock creation/management.
Always try to look at unit testing as a stand alone, independent processes. Limit the scope and dependencies wherever possible to make testing as effective and easy as possible.
Here is an example of the approach I take for unit testing a service where I mock out its dependencies:
// app.service.spec.ts
describe('Testing app.service', () => {
let module: TestingModule;
let service: AppService;
// mock out providers the service depends on
const mockProviders = [
{
provide: ConfigService,
useValue: {
get: jest.fn().mockReturnValue('Mock!'),
},
},
];
beforeAll(async () => {
// build up testing module
module = await Test.createTestingModule({
imports: [],
providers: [...mockProviders, AppService],
})
.compile()
.catch((err) => {
// Helps catch ninja like errors from compilation
console.error(err);
throw err;
});
service = module.get<AppService>(AppService);
});
it('Should return: Hello Mock!', async () => {
const response = service.getHello();
expect(response).toEqual('Hello Mock');
});
});
I try to keep all business logic (wherever possible) in services, leavingcontrollers light and generally reserved for e2e and/or integration testing.
This isn't the only (and maybe not even "the best") approach, but it helps me to keep my tests and services more focused.
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.
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.
I'm playing with mount() from vue-test-utils, have a component that imports services that should be mocked in the unit test.
I see that mount() has a mocks option, but trying to extrapolate the example given at guides, common-tips, mocking injections to the scenario of an injected service is eluding me.
mount(Component, {
mocks: {
...?
}
})
The component simply imports the service, which is plain JS
import DataService from '../services/data.service'
I can get it working using the inject-loader which is detailed here Testing With Mocks
The code that does work
const MyComponentInjector = require('!!vue-loader?inject!./MyComponent.vue')
const mockedServices = {
'../services/data.service': {
checkAll: () => { return Promise.resolve() }
},
}
const MyComponentWithMocks = MyComponentInjector(mockedServices)
const wrapper = mount(MyComponentWithMocks, { store: mockStore, router })
What is the syntax for mount(MyComponent, { mocks: ... })?
Since mount() has a mocks option, should it not be possible to pass mockedServices to it in some form?
mocks refers to the Vue instance. You're trying to mock a file dependency, which is a different problem. As you said, one solution is inject-loader. Another is the babel-plugin-rewire.
Let me clear up what the mocks option does.
mocks adds properties to the Vue instance.
If you have an app that injects $route, you might have a component that tries to access it: this.$route.path:
...
methods: {
logPath() {
console.log(this.$route.path)
}
}
...
If you try to mount this component without installing Vue router, it will throw an error. To solve this, you can use the mocks mount option to inject a mock $route object to the Vue instance:
const $route = { path: 'some/mock/value' }
mount(Component, {
mocks: {
$route
}
})
Little exhausted here, may be that is why my title is not so accurate.
I am writing a unit test for my DummyService:
import {Injectable} from '#angular/core';
#Injectable()
export class DummyService {
getAllDataSources():Promise<Array<DummyData>> {
return new Promise<DummyData[]>(resolve =>
setTimeout(()=>resolve([]), 1000) // 1 seconds
);
}
}
Please assume am returning a list of DummyData objects from getAllDataSources.
Now, I have a structure/interface for the dummy data in the same service file:
export interface DummyData{
Name:string;
IsActive:boolean;
}
I tried to write unit test for this service:
import {DummyService, DummyData} from './dummy.service';
import {
beforeEachProviders
} from '#angular/core/testing';
import {provide} from '#angular/core';
export function main() {
describe('dummy.service', () => {
let dsService:DummyService;
it('should fetch data', ()=> {
dummyData: DummyData = new DummyData(); // >>>> culprit...
expect(1).toEqual(1);
});
});
}
This unit test seem little funny, as I am really not calling DummyServices function to get the list of DummyData.
I am doing this because I was getting some issue, due to which I was not able to see my test. I did some research, spent a whole day and finally found that this structure DummyData is the CULPRIT. I proved this to myself when I tried creating an object of it in my unit test (in the code above) and I got the following error:
FAILED TESTS:
dummy.service
✖ should fetch data
PhantomJS 2.1.1 (Linux 0.0.0)
Chrome 50.0.2661 (Linux 0.0.0)
ReferenceError: **DummyData is not defined**
at eval (/home/aodev/WebstormProjects/Data Federation App/data-mapping-app/dist/dev/app/shared/datasource.service.spec.js:8:28)
at Object.eval (/home/aodev/WebstormProjects/Data Federation App/data-mapping-app/node_modules/#angular/core/testing/testing.js:80:25)
So, can someone tell me please, what am I doing wrong?
Why I cannot create the object of DummyData inside my unit test?
Please help!
TypeScript interfaces exist only during compile time, runtime knows nothing about interfaces.
This is how you create instance that implements interface:
interface itest
{
success:boolean;
}
let a:itest = {success: true}; //compiler checks that object matches interface itest