Angular 2 - How to access a service dependency in a unit test - unit-testing

I have the following Angular service which depends on AngularFire:
#Injectable()
export class FirebaseService {
constructor(private af: AngularFire) { }
// more code here
}
I'm unit testing this service with the following setup:
TestBed.configureTestingModule({
imports: [ AngularFireModule.initializeApp(MY_FIREBASE_CONFIG) ],
providers: [ FirebaseService ]
});
const firebaseService = TestBed.get(FirebaseService);
I can then do assertions on the service's methods such as:
// This works great
expect(firebaseService.callFoo()).toEqual('foo');
My question is how do I get a hold of the AngularFire instance that's injected in the constructor of FirebaseService? (TestBed.get(AngularFire) does not work; also, I don't have a component so I can't use the component's injector)
Site note: I know I should mock the backend for unit tests but it's not the point of the question here.

Related

How to write unit test in nestjs and typeorm without connecting to db

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.

Angular2 Component Unit Testing with Service Dependency

So I have this PromptComponent and the content will be updated by a service data.
Now I am trying to do the unit test this component which has this service dependency.
Here is the Component:
export class PromptComponent {
#Input() module: any;
#select() selectedPrompt;
constructor(private moduleService: ModuleService){}
}
Here is the service:
getQuote(): Promise<string> {
return new Promise(resolve => {
setTimeout( () => resolve(this.nextQuote()), 500 );
});
}
private nextQuote() {
if (this.next === quotes.length) { this.next = 0; }
return quotes[ this.next++ ];
}
Here is the unit test code:
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [
PromptComponent,
],
providers: [ ModuleService]
});
fixture = TestBed.createComponent(PromptComponent);
component = fixture.componentInstance;
const moduleService = fixture.debugElement.injector.get(ModuleService);
spy = spyOn(moduleService, 'getQuote').and.returnValue(Promise.resolve(testService));
I referred to the official demo on the angular documentation
https://angular.io/resources/live-examples/testing/ts/eplnkr.html, as inside the TwainComponent.spec.
Something really weird is that as expected, inside the official demo, the TwainComponent test passed smoothly. But I, on the other hand, got this strange injectionError. If I remove the parameter inside the constructor, I am able to create the fixture instance but get injection error when it runs thorught the injector.get() part. But as soon as I put the private moduleService:ModuleService back inside the component constructor, I will get fixture undefined and also with the injection error.
So that means I cannot even execute my code to the actual service injection part (fixture.debugElement.injector.get(ModuleService)), I already got the error.
Does anybody have the same issue?
My code is almost identical to the demo code on Plunker. Have no clue how to move forward....

Angular 2 TestModuleMetadata does not have an EntryComponents property

I'm using Angular CLI 1.0.0-beta.32.2 and am writing a unit test for an Angular 2 service, which dynamically creates a Component and inserts it into another Component.
In my unit test I try to create a mock component to test my service, but the output of ng test throws an error, stating my mock Component is specified in the entryComponents property. When I try to add the Component into an entryComponents property of the TestModuleMetadata object, like this: TestBed.createTestingModule({...entryComponents: [ TestDialogComponent ]...}) I see the following error stating the entryComponents property does not exist.
Chrome 56.0.2924 (Windows 10 0.0.0) DialogService should create a child component when opening FAILED
Error: No component factory found for TestDialogComponent. Did you add it to #NgModule.entryComponents?
Looking at the TestModuleMetadata definition shows that the entryComponents property does not exist. So how do I go about dynamically creating a Component in my unit tests in Angular 2 and Jasmine?
As far as i know it has not supported yet. As workaround you can create fake module with entryComponent and import it to your testing module
#NgModule({
imports: [CommonModule],
declarations: [TestDialogComponent],
entryComponents: [TestDialogComponent]
})
export class FakeTestDialogModule {}
and then
TestBed.configureTestingModule({
imports: [FakeTestDialogModule]
Check TestBed.overrideModule. Please refer to this discussion
https://github.com/angular/angular/issues/12079
I got the error for my DialogBoxComponent. I resolved it as follows
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ NewPracticeQuestionComponent,
DialogBoxComponent,
ShowErrorsComponent],
imports:[ReactiveFormsModule,HttpClientTestingModule],
providers:[WebToBackendInterfaceService,HelperService,AuthService]
})
TestBed.overrideModule(BrowserDynamicTestingModule, {
set: {
entryComponents: [DialogBoxComponent]
}
})
.compileComponents();
}));

Unit testing Angular 2 components inside a common library NPM Package no app context

I am kind of stumped on this. I have used the Angular 2 quick start projects as a reference for unit testing Angular 2 but it seems to assume you have an app in play. In my case we have NPM packages that have Angular 2 modules in them that are shared across various projects in our organization. I would like to be able to unit test the code inside these common libraries in isolation (without them being part of an app).
I am looking for examples or a tutorial or something explaining the best approach to this, Google has not provided any help.
Well I am doing in my Karma test something like:
Create a mock component
#Component({
template: "",
selector: 'mock'
})
export class MockComponent implements OnInit {
constructor() { }
ngOnInit() {
console.log("Is loaduing");
}
}
Create a mock service
class MockSomeService {
public subscribe(){}
public inizialize() {}
}
Create ROUTES array
export var ROUTES = [ {path:"/pathexample", component: MockComponent}]
Create DECLARATIONS array
export var DECLARATIONS:Component[] = [
MockComponent, ExampleComponent
];
Create PROVIDERS
const CONSTANTS_PROVIDERS: Provider[] = [
{ provide: SomeService, useClass: MockSomeService }
];
Write a test
describe('Component: example', () => {
beforeEach(() => {
TestBed.configureTestingModule({ declarations: DECLARATIONS, providers: CONSTANTS_PROVIDERS, imports: [RouterTestingModule.withRoutes(ROUTES)] });
});
it('should create an instance', inject([ExampleComponent], (component: ExampleComponent) => {
expect(component).toBeTruthy();
}));
});
If your component is using route.navigate you should use TestBed.overrideComponent and add template: '<router-outlet></router-outlet>' to your component if not have it yet and actually create the component like this TestBed.createComponent(ExampleComponent);

Angular 2 - Mocking Services in Components that depend on other services

How do I mock a service that depends on another service that is in a component?
Please check code below.
a.component.ts
#Component({
selector: 'my-comp',
templateUrl: './my.component.html',
providers: [ MyServiceA ]
})
export class MyComponent {
my-service-a.service.ts
#Injectable()
export class MyServiceA{
constructor(private myServiceB: MyServiceB) {}
my-service-b.service.ts
export class MyServiceB{
constructor(private myServiceC: MyServiceC,
private myServiceD: MyServiceD) {}
How do I mock the service in the a.component.spec.ts in the TestBed configuration? Please help. Thank you.
You can mock it however you want. The other services don't matter. I think maybe the problem you are facing is with the #Component.providers. Using this, any mocks you configure in the TestBed aren't used as the #Component.providers take precedence, causing Angular to try to create it, instead of using the mock.
To get around that, Angular offers the TestBed.overrideComponent method, so that we can override things like the template and providers of the #Component
beforeEach(() => {
let myMockService = new MyMockService();
TestBed.configureTestingModule({
providers: [
// the following will not be used
{ provide: MyService, useValue: myMockService }
]
});
TestBed.overrideComponent(MyComponent, {
set: {
providers: [
// this will override the #Component.providers:[MyService]
{ provide: MyService, useValue: myMockService }
]
}
});
})