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();
}));
Related
Got this weird bug when running the jest test, one of the UI component from a self defined UI package keeps throwing error, saying that an object in that package is undefined...
The component itself works perfectly fine, and the same component's testing logic works in another repo without nextjs, and that repo utilize #swc/jest for js transform in jest.config file.
I've also added that package itself to transformIgnorePatterns in jest-config file, but somehow the bug still presents...
The project itself is in nextjs, and below is a snapshot of the jest.config file
/** #type {import('ts-jest/dist/types').InitialOptionsTsJest} */
module.exports = {
testPathIgnorePatterns: [
'<rootDir>/.next/',
'<rootDir>/node_modules/',
'<rootDir>/e2e/'
],
preset: 'ts-jest',
testEnvironment: 'jsdom',
setupFilesAfterEnv: ['#testing-library/jest-dom/extend-expect'],
setupFiles: [require.resolve('whatwg-fetch')],
transform: {
'^.+\\.(js|jsx|ts|tsx)$': ['babel-jest', { presets: ['next/babel'] }]
},
transformIgnorePatterns: ['/node_modules/myPackage', 'jest-runner'],
testMatch: ['**/*.spec.{js,jsx,ts,tsx}'],
};
and the error itself goes Error: Uncaught [TypeError: Cannot read property 'object' of undefined], which tracks down to /node_modules/myPackage
how the package is used
import { InputBox } from 'myPackage';
const MyComponent = () => {
return (
<div>
<InputBox />
</div>
);
}
export default MyComponent;
and here's the test:
import { act, render } from '#testing-library/react';
import React from 'react';
describe('show component', () => {
it('should render', async () => {
await act(async () => {
render(
<MyComponent/>
);
});
});
});
I've found a similar question on stackoverflow Jest: Cannot read property of undefined when importing from own package
but that one is using regular js, and this one being next.js, there's really nowhere I can update .babelrc to update those configs...
Any input would be appreciated.
Update: it turns out the component that causes me bug is built on top of react-popper library, which is built on top of popper.js library. popper.js library doesn't support jsdom by default, and it requires to do jest mock. But my library is 2 layers abstractions on top of popper.js library, I'm not sure how to do that, or even if that is doable...
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.
I need to be able test my component (methods, computed properties, data, ...). However, when I import my vue component in my unit test:
import Pagination from 'src/components/shared/pagination.vue'
import { newComponent } from '../component-factory'
describe('pagination.vue', () => {
const propsData = {
metadata: {
page: 2,
records: 155,
total: 11,
},
perPage: 15,
}
it('should have correct number of records', () => {
const ctor = Vue.extend(Pagination)
const vm = new ctor({propsData}).$mount()
expect(vm.firstRecord).toBe(16)
expect(vm.lastRecord).toBe(30)
})
...
vm is of type Vue, and thus does not have the firstRecord/lastRecord properties. Running the test with karma shows a success, but the typescript compiler spits out Errors:
ERROR in ./tests/shared/pagination.spec.ts
(16,19): error TS2339: Property 'firstRecord' does not exist on type 'Vue'.
ERROR in ./tests/shared/pagination.spec.ts
(17,19): error TS2339: Property 'lastRecord' does not exist on type 'Vue'.
I tried casting:
...
const vm = new ctor({propsData}).$mount() as Pagination
...
But that results in a warning in VSCode:
[ts] Cannot find name 'Pagination'.
And has the effect of treating vm as type any which is totally counterproductive.
I think this all stems from the fact that when using .vue files you have to add the declaration:
declare module '*.vue' {
import Vue from 'vue'
export default typeof Vue
}
Which clearly sets the type of all .vue files to Vue, which isn't exactly a lie, but isn't helpful either... Any suggestions? What am I doing wrong?
For future reference, I have attempted to use vuetype which generates .d.ts files for each .vue file, but ran into this issue. Also, there is a request to make .vue a first class citizen in the typescript ecosystem, which would eliminate this problem. And, I just added a request for a vue language service extension
Up until Vue 2.5, their TypeScript documentation page recommended exporting an interface that extends Vue if you were not going to use vue-class-component. You can export this interface to use in your tests, to cast your component instance. The recommendation has been removed from the docs, but I have not been able to figure out how to change my tests to not need the interface.
It looks like vuetype could generate these interfaces for you, but I've just been creating them manually.
Here is a greatly simplified example, but you can define anything in your interface that you would reference on vm, ie data, props, methods:
// NOTE: Make sure your interface extends `Vue`!
export interface PaginationComponent extends Vue {
firstRecord: number,
lastRecord: number
}
export default {
name: 'Pagination',
data: function() {
return {
firstRecord: 16,
lastRecord: 30,
}
}
}
For your test, you can cast the component instance to the type of your exported interface:
import Pagination, {PaginationComponent} from 'src/components/shared/pagination.vue'
describe('pagination', () => {
it('should know about component data fields', () => {
const ctor = Vue.extend(Pagination)
const vm : PaginationComponent = new ctor().$mount()
expect(vm.firstRecord).toBe(16)
expect(vm.lastRecord).toBe(30)
})
})
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.
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);