Angular in-memory-web-api method always returns 404 NotFound in the brower's console even if the tests passed - jasmine

I'm new to unit testing in Angular (using Jasmine and Karma)
I'm trying to create some tests for my httpService, apparently the tests are OK.
But sometimes when I either run ng test, or refresh the browser, I found that one of the test in one of the 3 test suites has failed with this message : Uncaught [object Object] thrown.
Another annoying thing is that no matter whether all of the tests pass or any of them fail, if you check the browser's console, you'll ALWAYS find this message :
I'm attaching the code in a zip file (uploaded to Drive). You only need to run npm install and npm start.
I really hope you can help me understand why this testing behaves like a Russian roulette.

The issue is calculator.component.spec.ts. You are not mocking loanService where it is going out and making HTTP calls. You should always mock external services.
Change calculator.component.spec.ts to:
import { NO_ERRORS_SCHEMA } from '#angular/core';
import { FormBuilder } from '#angular/forms';
import { async, ComponentFixture, TestBed } from '#angular/core/testing';
import { CalculatorComponent } from './calculator.component';
import { LoanService } from '../loan.service';
import { Campaign } from '../campaign';
import { of } from 'rxjs/internal/observable/of';
describe('CalculatorComponent', () => {
let component: CalculatorComponent;
let fixture: ComponentFixture<CalculatorComponent>;
let mockLoanService: any;
beforeEach(async(() => {
// mockLoanService object, first parameter ('loanService') is optional, second paramter => array of methods needing
// mock for component
mockLoanService = jasmine.createSpyObj('loanService', ['getCurrentCampaign', 'getMonthlyAmount']);
TestBed.configureTestingModule({
declarations: [ CalculatorComponent ],
imports: [],
// NO_ERRORS_SCHEMA to ignore child components, if you need the
// painting of the DOM of the child components/directives, put them in declarations
schemas: [NO_ERRORS_SCHEMA],
providers: [
FormBuilder,
// provide the mock for LoanService
{ provide: LoanService, useValue: mockLoanService },
]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(CalculatorComponent);
component = fixture.componentInstance;
// getCurrentCampaig is related to ngOnInit so we have to mock it
mockLoanService.getCurrentCampaign.and.returnValue(of({
id: 1,
campaign_name: 'Donald Trump 2020',
min_quota: -200000000,
max_quota: 0,
max_amount: 0,
min_amount: 0,
tea: 1,
payment_date: new Date(),
currency: 'Fake News',
} as Campaign))
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});
I have written some comments in the file itself. By the way, Donald Trump 2020 and Fake News are just jokes, I have no political affiliation but I like writing jokes in my unit tests for other developers :).
Some notes:
1.) Whenever you are injecting a service, always mock it. You are testing the component and component alone, you have to assume that the service will do its job because it is already being tested.
2.) Check out NO_ERRORS_SCHEMA. It basically ignores all components/directives in your HTML that is not in the declarations array. If you are writing a test where you click the button of a child component and it affects this component, then declare it in declarations (basically if you need the actual implementation of the child component, declare it). Otherwise, use NO_ERRORS_SCHEMA.
3.) Importing SharedModule in all unit tests is not good in my opinion. It will make your unit tests slow. Instead, take advantage of declarations and providers and give the component what it needs and JUST what it needs (not extra stuff).
4.) A really good class in PluralSight called Unit Testing in Angular.
Taking that class, you will have a better understanding of Unit/Integration testing. Maybe buy a subscription to PluralSight or start a free trial.

Related

Angularfire conditionally provide appcheck

I have two angular projects:
Main app
Webcomponent (angular elements)
Webcomponent is used in the main app. Both are using angularfire for executing Firebase functions, working with Firestore and more.
Also I am enforcing verified request to the Functions and Firestore by AppCheck.
The web component needs to work separately. To be able to request Firebase servers I need to provide the AppCheck in both projects like this:
#NgModule({
...
imports: [
...
provideAppCheck(() => initializeAppCheck(getApp(), {
provider: new ReCaptchaV3Provider(environment.firebase.appCheck.recaptcha3SiteKey),
isTokenAutoRefreshEnabled: environment.firebase.appCheck.isTokenAutoRefreshEnabled,
}))
...
],
...
})
This works just fine when webcomponent is not included in the main app. However when so, the AppCheck is initialized two times and it throws an error:
Unhandled Promise rejection: reCAPTCHA has already been rendered in this element ; Zone: <root> ; Task: Promise.then ; Value: Error: reCAPTCHA has already been rendered in this element
So the webcomponent needs to check if appcheck already exists in document and add it only if it does not. I tried to work with appCheckInstance$ but that is an observable and provideAppCheck requires only AppCheck type.
When I try to move provideAppCheck to component which would handle the logic, I get an error saying that calling it can not be done outside module:
Either AngularFireModule has not been provided in your AppModule (this can be done manually or implictly using
provideFirebaseApp) or you're calling an AngularFire method outside of an NgModule (which is not supported).
I have no other ideas how this could be done other than building two webcomponents (one with appcheck, other without), but thats just not an option.
It turned out that the problem was elsewhere. I thought that conditional appcheck loading would help, but it didn't, because then angularfire(in webcomponent) didn't use the appcheck that the main app initialized. And hence connections to firebase were blocked (as if there was no appcheck initialized).
Solution I've figured out that works:
In webcomponent initialize all firebase services under different name.
So instead of:
#NgModule({
...
imports: [
...
provideFirebaseApp(() => initializeApp(environment.firebase)),
provideFirestore(() => getFirestore(getApp())),
provideAppCheck(() => initializeAppCheck(getApp(), {
provider: new ReCaptchaV3Provider(environment.firebase.appCheck.recaptcha3SiteKey),
isTokenAutoRefreshEnabled: environment.firebase.appCheck.isTokenAutoRefreshEnabled,
})),
...
],
...
})
do:
#NgModule({
...
imports: [
...
provideFirebaseApp(() => initializeApp(environment.firebase, 'webcomponent-app')),
provideFirestore(() => getFirestore(getApp('webcomponent-app'))),
provideAppCheck(() => initializeAppCheck(getApp('webcomponent-app'), {
provider: new ReCaptchaV3Provider(environment.firebase.appCheck.recaptcha3SiteKey),
isTokenAutoRefreshEnabled: environment.firebase.appCheck.isTokenAutoRefreshEnabled,
})),
...
],
...
})
This will initialize two instances (one for main app, other for webcomponent) with different names. And now initializing two appchecks is not problematic.

Cypress asynchronous execution returns 4 same assertion

I have a question.
I am using Cypress for my automation and I started using async and await for my tests.
I am using POM design pattern.
My question:
If I execute the following test:
test.spec.ts class (test class)
import { login_po } from "../pom/1.Chiquito/login_po";
const pom = new login_po()
describe("Put some name here.", async() => {
it('TestCase1', async () => {
await pom.navigateTo();
});
});
My POM class.
export class login_po {
navigateTo() {
cy
.visit(`https://chiquito-qa.omnifitrgsites.co.uk/`)
.url()
.should('be.equal', 'https://chiquito-qa.omnifitrgsites.co.uk/').then(() => this.verifyAfterLogin());
}
verifyAfterLogin() {
cy.get('.header__logo-img');
}
}
When I execute the test - Cypress makes 4 (same) assertions.
If I remove 'async' - 'await' from the test class - Cypress makes 1 assertion.
import { login_po } from "../pom/1.Chiquito/login_po";
const pom = new login_po()
describe("Put some name here.", () => {
it('TestCase1', () => {
pom.navigateTo();
});
});
Why this is happening?
Cypress commands are not promises, and their interaction with async/await will not happen as you expect. Additionally, while POM is feasible and reasonable to do within Cypress, it is recommended that you use App Actions instead of a POM.

No Code Coverage for Fastify Integration Tests Using NYC/Istanbul written in Typescript

I'm currently trying to get code coverage on my fastify routes using Mocha and NYC.
I've tried instrumenting the code beforehand and then running the tests on the instrumented code as well as just trying to setup NYC in various ways to get it to work right.
Here is my current configuration. All previous ones produced the same code coverage output):
nyc config
"nyc": {
"extends": "#istanbuljs/nyc-config-typescript",
"extension": [
".ts",
".tsx"
],
"exclude": [
"**/*.d.ts",
"**/*.test.ts"
],
"reporter": [
"html",
"text"
],
"sourceMap": true,
"instrument": true
}
Route file:
const routes = async (app: FastifyInstance, options) => {
app.post('/code', async (request: FastifyRequest, response: FastifyReply<ServerResponse>) => {
// route logic in here
});
};
The integration test:
import * as fastify from fastify;
import * as sinon from 'sinon';
import * as chai from 'chai';
const expect = chai.expect;
const sinonChai = require('sinon-chai');
chai.use(sinonChai);
describe('When/code POST is called', () => {
let app;
before(() => {
app = fastify();
// load routes for integration testing
app.register(require('../path/to/code.ts'));
});
after(() => {
app.close();
});
it('then a code is created and returned', async () => {
const {statusCode} = await apiTester.inject({
url: '/code',
method: 'POST',
payload:{ code: 'fake_code' }
});
expect(statusCode).to.equal(201);
});
});
My unit test call looks like the following:
nyc mocha './test/unit/**/*.test.ts' --require ts-node/register --require source-map-support/register --recursive
I literally get 5% code coverage just for the const routes =. I'm really banging my head trying to figure this one out. Any help would be greatly appreciated! None of the other solutions I have investigated on here work.
I have a detailed example for typescript + mocha + nyc. It also contains fastify tests including route tests (inject) as well as mock + stub and spy tests using sinon. All async await as well.
It's using all modern versions and also covers unused files as well as VsCode launch configs. Feel free to check it out here:
https://github.com/Flowkap/typescript-node-template
Specifically I think that
instrumentation: true
messes up the results. Heres my working .nycrc.yml
extends: "#istanbuljs/nyc-config-typescript"
reporter:
- html
- lcovonly
- clover
# those 2 are for commandline outputs
- text
- text-summary
report-dir: coverage
I have proper coverage even for mocked ans tub parts of fastify in my above mentioned example.

What is the proper way to unit test Service with NestJS/Elastic

Im trying to unit test a Service that uses elastic search. I want to make sure I am using the right techniques.
I am new user to many areas of this problem, so most of my attempts have been from reading other problems similar to this and trying out the ones that make sense in my use case. I believe I am missing a field within the createTestingModule. Also sometimes I see providers: [Service] and others components: [Service].
const module: TestingModule = await Test.createTestingModule({
providers: [PoolJobService],
}).compile()
This is the current error I have:
Nest can't resolve dependencies of the PoolJobService (?).
Please make sure that the argument at index [0]
is available in the _RootTestModule context.
Here is my code:
PoolJobService
import { Injectable } from '#nestjs/common'
import { ElasticSearchService } from '../ElasticSearch/ElasticSearchService'
#Injectable()
export class PoolJobService {
constructor(private readonly esService: ElasticSearchService) {}
async getPoolJobs() {
return this.esService.getElasticSearchData('pool/job')
}
}
PoolJobService.spec.ts
import { Test, TestingModule } from '#nestjs/testing'
import { PoolJobService } from './PoolJobService'
describe('PoolJobService', () => {
let poolJobService: PoolJobService
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [PoolJobService],
}).compile()
poolJobService = module.get<PoolJobService>(PoolJobService)
})
it('should be defined', () => {
expect(poolJobService).toBeDefined()
})
I could also use some insight on this, but haven't been able to properly test this because of the current issue
it('should return all PoolJobs', async () => {
jest
.spyOn(poolJobService, 'getPoolJobs')
.mockImplementation(() => Promise.resolve([]))
expect(await poolJobService.getPoolJobs()).resolves.toEqual([])
})
})
First off, you're correct about using providers. Components is an Angular specific thing that does not exist in Nest. The closest thing we have are controllers.
What you should be doing for a unit test is testing what the return of a single function is without digging deeper into the code base itself. In the example you've provided you would want to mock out your ElasticSearchServices with a jest.mock and assert the return of the PoolJobService method.
Nest provides a very nice way for us to do this with Test.createTestingModule as you've already pointed out. Your solution would look similar to the following:
PoolJobService.spec.ts
import { Test, TestingModule } from '#nestjs/testing'
import { PoolJobService } from './PoolJobService'
import { ElasticSearchService } from '../ElasticSearch/ElasticSearchService'
describe('PoolJobService', () => {
let poolJobService: PoolJobService
let elasticService: ElasticSearchService // this line is optional, but I find it useful when overriding mocking functionality
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
PoolJobService,
{
provide: ElasticSearchService,
useValue: {
getElasticSearchData: jest.fn()
}
}
],
}).compile()
poolJobService = module.get<PoolJobService>(PoolJobService)
elasticService = module.get<ElasticSearchService>(ElasticSearchService)
})
it('should be defined', () => {
expect(poolJobService).toBeDefined()
})
it('should give the expected return', async () => {
elasticService.getElasticSearchData = jest.fn().mockReturnValue({data: 'your object here'})
const poolJobs = await poolJobService.getPoolJobs()
expect(poolJobs).toEqual({data: 'your object here'})
})
You could achieve the same functionality with a jest.spy instead of a mock, but that is up to you on how you want to implement the functionality.
As a basic rule, whatever is in your constructor, you will need to mock it, and as long as you mock it, whatever is in the mocked object's constructor can be ignored. Happy testing!
EDIT 6/27/2019
About why we mock ElasticSearchService: A unit test is designed to test a specific segment of code and not make interactions with code outside of the tested function. In this case, we are testing the function getPoolJobs of the PoolJobService class. This means that we don't really need to go all out and connect to a database or external server as this could make our tests slow/prone to breaking if the server is down/modify data we don't want to modify. Instead, we mock out the external dependencies (ElasticSearchService) to return a value that we can control (in theory this will look very similar to real data, but for the context of this question I made it a string). Then we test that getPoolJobs returns the value that ElasticSearchService's getElasticSearchData function returns, as that is the functionality of this function.
This seems rather trivial in this case and may not seem useful, but when there starts to be business logic after the external call then it becomes clear why we would want to mock. Say that we have some sort of data transformation to make the string uppercase before we return from the getPoolJobs method
export class PoolJobService {
constructor(private readonly elasticSearchService: ElasticSearchService) {}
getPoolJobs(data: any): string {
const returnData = this.elasticSearchService.getElasticSearchData(data);
return returnData.toUpperCase();
}
}
From here in the test we can tell getElasticSearchData what to return and easily assert that getPoolJobs does it's necessary logic (asserting that the string really is upperCased) without worrying about the logic inside getElasticSearchData or about making any network calls. For a function that does nothing but return another functions output, it does feel a little bit like cheating on your tests, but in reality you aren't. You're following the testing patterns used by most others in the community.
When you move on to integration and e2e tests, then you'll want to have your external callouts and make sure that your search query is returning what you expect, but that is outside the scope of unit testing.

Is there a way to define providers, declarations, imports for multiple tests

I'm starting writing unit tests for our projects, for some reason we don't have any unit tests before. I'm currently trying to make the default test to pass, and I have to define some imports, declarations, and providers. We have 44 components/services now, and for 3 of the unit tests I work on, they use same imports, declarations, and providers.
I'm just wondering if there is a way to define imports, declarations, and providers for all of the tests.
it('should create', () => {
expect(component).toBeTruthy();
});
You can create testing module with set of common dependencies like this:
#NgModule({
// ...whatever you need
providers: [
{ provide: Dependency, useClass: DependencyMock },
]
})
export class CommonTestingModule {
}
and then in tests
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
CommonTestingModule,
...
],
...
})
...
});
You can create more modules for parts of applications that can be used in a few places its similar to modules provided by angular like HttpClientTestingModule

Resources