ngX async assertion not getting correct expectation - javascript

No matter what I expect in an async assertion the test always passes. Furthermore no log is displayed when calling console.log after the then statement. My code looks as follows..
import { TestBed, async } from '#angular/core/testing'
import { RouterTestingModule } from '#angular/router/testing'
import { HttpModule, XHRBackend } from '#angular/http'
import { MockBackend } from '#angular/http/testing'
import { ApiService } from '../../client/services/api.service '
import { SocketService } from '../../client/services/socket.service'
import { EventService } from '../../client/services/event.service'
import { DirectMessageComponent } from '../../client/components/messages/direct.component'
import { SharedModule } from '../../client/components/common/shared.module'
describe('DirectMessageComponent', () => {
// High order variables
let comp
let fixture
// Asynchronous beforeEach
beforeEach(async(() => {
// Configure the test bed
TestBed.configureTestingModule({
imports: [ SharedModule, HttpModule, RouterTestingModule ],
declarations: [ DirectMessageComponent ],
providers: [
ApiService,
SocketService,
EventService,
{ provide: XHRBackend, useClass: MockBackend }
]
})
.compileComponents() // compile template and css
}))
// Synchronous beforeEach
beforeEach(() => {
// Assignments
fixture = TestBed.createComponent(DirectMessageComponent)
comp = fixture.componentInstance
// Detect changes which fires ngOnInit
fixture.detectChanges()
})
it('should get messages', async(() => {
fixture.whenStable()
.then(() => {
expect(comp.message.length).toBeGreaterThan(0)
})
}))
})
Interestingly it fails when executed with the fakeAsync method, even with the proper call to tick().

Related

How to read subscribe in Angular Unit Test?

I am trying to write test cases of a component where at first I tried to check if a basic test case is working or not. There I encountered some errors related to Dependency Injection and solved those. Now the first issue that I am facing is mocking the API calls.
Below is my component code for which I am writing test cases:
import { Component, OnInit } from '#angular/core';
import { FormBuilder, FormGroup, Validators } from '#angular/forms';
import { HttpClientService } from '#service/http-client.service';
import { CustomErrorStateMatcher } from '#validator/error-state-matcher';
import { ToastrService } from 'ngx-toastr';
#Component({
selector: 'app-prep-card',
templateUrl: './prep-card.component.html',
styles: ['legend { width: fit-content !important; }']
})
export class PrepCardComponent implements OnInit {
constructor(private fb: FormBuilder, private _http: HttpClientService, private toastr: ToastrService) { }
foamTypeList: string[] = [];
colorList: string[] = [];
ngOnInit() { this.getFoam_ColorList() }
private getFoam_ColorList() {
this._http.getFoamType_ColorList('ABC').subscribe(response => {
this.foamTypeList = response.data.foamTypesAndColors.foamTypes;
this.colorList = response.data.foamTypesAndColors.colors;
});
}
}
At first, I tried to run a simple test case to check if it is working correctly or not. Below is the test case I wrote:
import { ComponentFixture, inject, TestBed } from "#angular/core/testing";
import { FormsModule, ReactiveFormsModule } from "#angular/forms";
import { RouterTestingModule } from '#angular/router/testing';
import { HttpClientService } from "#service/http-client.service";
import { ToastrService } from "ngx-toastr";
import { MaterialModule } from "src/app/material/material.module";
import { PrepCardComponent } from "./prep-card.component";
describe('PrepCardComponent', () => {
let component: PrepCardComponent, fixture: ComponentFixture<PrepCardComponent>,
_httpSpy: jasmine.SpyObj<HttpClientService>, _toastrSpy: jasmine.SpyObj<ToastrService>;
beforeEach(async () => {
const _httpSpyObj = jasmine.createSpyObj('HttpClientService', ['getFoamType_ColorList']),
_toastrSpyObj = jasmine.createSpyObj('ToastrService', ['success', 'error']);
await TestBed.configureTestingModule({
declarations: [PrepCardComponent],
imports: [
RouterTestingModule, FormsModule, ReactiveFormsModule, MaterialModule
],
providers: [
{ provide: HttpClientService, useValue: _httpSpyObj },
{ provide: ToastrService, useValue: _toastrSpyObj },
]
}).compileComponents();
fixture = TestBed.createComponent(PrepCardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('test', () => expect('a').toBe('a'));
});
The above code snippet threw the following error:
TypeError: Cannot read properties of undefined (reading 'subscribe')
at PrepCardComponent.getFoam_ColorList (http://localhost:9876/karma_webpack/webpack:/src/app/pages/prep-card/prep-card.component.ts:57:52)
at PrepCardComponent.setFormFieldValidator (http://localhost:9876/karma_webpack/webpack:/src/app/pages/prep-card/prep-card.component.ts:65:10)
at PrepCardComponent.createForm (http://localhost:9876/karma_webpack/webpack:/src/app/pages/prep-card/prep-card.component.ts:48:10)
at PrepCardComponent.ngOnInit (http://localhost:9876/karma_webpack/webpack:/src/app/pages/prep-card/prep-card.component.ts:28:10)
at callHook (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/fesm2015/core.mjs:2542:1)
at callHooks (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/fesm2015/core.mjs:2511:1)
at executeInitAndCheckHooks (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/fesm2015/core.mjs:2462:1)
at refreshView (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/fesm2015/core.mjs:9499:1)
at renderComponentOrTemplate (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/fesm2015/core.mjs:9598:1)
at tickRootContext (http://localhost:9876/karma_webpack/webpack:/node_modules/#angular/core/fesm2015/core.mjs:10829:1)
To solve this, I searched different questions here and saw common solution mentioned below to get an observable.
it('Call Service', inject([HttpClientService], (_http: HttpClientService) => {
spyOn(_http, 'getFoamType_ColorList').and.returnValue({subscribe: () => {}})
}));
Writing the above test case was throwing an error (mentioned below) in the subscribe position.
Type 'void' is not assignable to type 'Subscription'.ts(2322)
Service code
getFoamType_ColorList(cardType: string) {
return this.http.get<{ message: string, data: { foamTypesAndColors: { foamTypes: string[], colors: string[] } } }>
(`${this.URL}/validate/foam-type-and-color/${cardType}`);
}
Can anyone help me out with how I can solve this issue?
UPDATE
As per dmance's answer am not getting any error while writing the test case but the test cases are still failing giving the same error. For reference refer below the full error log:
I will suggest this:
import { of } from 'rxjs';
spyOn(httpclientservice, 'getFoamType_ColorList').and.returnValue(of(YOUR VALUE));
This is a more complete answer:
describe('PrepCardComponent', () => {
let component: PrepCardComponent, fixture: ComponentFixture<PrepCardComponent>;
let httpClientService: HttpClientService;
let toastService: ToastrService;
beforeEach(async () => {
await TestBed.configureTestingModule({
declarations: [PrepCardComponent],
imports: [
RouterTestingModule, FormsModule, ReactiveFormsModule, MaterialModule
]
}).compileComponents();
httpClientService = TestBed.inject(HttpClientService);
toastService= TestBed.inject(ToastrService);
fixture = TestBed.createComponent(PrepCardComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('test', () => {
spyOn(httpClientService, 'getFoamType_ColorList').then.returnValue(of(YOUR VALUE));
// MAKE YOUR TEST
});
});
Try this maybe
spyOn(_http,'getFoamType_ColorList').and.returnValue(Observable.of(Your_Value));

How to write the test cases in angular if there is a init.spec.ts file present?

I have a test folder in which there are 3 files,
1)nav-bar.init.spec.ts
2)nav-bar.scaffold.ts
3)nav-bar.spec.ts
I have the following codes respectively,
In nav-bar.init.spec.ts,
import { TestBed, async } from '#angular/core/testing';
import { Navbarcomponent } from './app.component';
describe('Navbarcomponent', () => {
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [
Navbarcomponent
],
}).compileComponents();
}));
In nav-bar.spec.ts,
import { TestBed, async } from'#angular/core/testing';
import { Navbarcomponent } from './app.component';
describe('Navbarcomponent', () => {
let component: Navbarcomponent;
beforeEach(() => {
component = new Navbarcomponent();
}));
it (should create,()=>{
expect(conponent).toBeTruthy()
}); //// I added this test case here
When I run the test cases,the karma screen shows 'NO SPEC FOUND'
My Karma.config file,
karma-config-1
[karma-config-2][2]
I am confused with this modularity. Can anyone please suggest me help.Thanks.
Based off a quick look, I think the issue is that you haven't configured your testbed properly:
import { ComponentFixture, TestBed } from '#angular/core/testing';
describe('AppComponent', () => {
let component: AppComponent;
let fixture: ComponentFixture<AppComponent>;
TestBed.configureTestingModule({
schemas: [NO_ERRORS_SCHEMA],
declarations: [AppComponent],
providers: [
{ provide: Router, useValue: routerStub },
{ provide: ActivatedRoute, useValue: activatedRouteStub },
{ provide: I18nService, useValue: i18nServiceStub },
{ provide: PlatformLocation, useValue: platformLocationStub },
...
]
});
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
});

how to test a function with condition in ngOnInit

I want to test a function with condition that comes from a service in ngOnInit. I tried many way but no success. i have all kinds of mistakes.
my component
export class MainSectionComponent implements OnInit {
propertiesFrDb: PropertyPost[];
constructor(
private getPropertiesFrDbService: GetPropertiesFrDbService,
private propertyWarehouseService: PropertyWarehouseService,
private router: Router,
config: NgbCarouselConfig,
private userService: UserService,
private sharedFunctionService: SharedFunctionService,
private returnResponseAfterUserLoginService: ReturnResponseAfterUserLoginService,
private localStorageService: LocalStorageServiceService,
private dialogLoginService: DialogLoginService,
#Inject(PLATFORM_ID) private platformId: Object
) {
// this.isBrowser = isPlatformBrowser(platformIds);
}
ngOnInit() {
this.getPropertiesFrDb();
}
getPropertiesFrDb() {
if (this.propertyWarehouseService.currentValuesProperties) {
this.propertyWarehouseService.getPropertyHome$.subscribe(
prop => {
console.log(prop);
return this.propertiesFrDb = prop
}
)
} else {
this.getPropertiesFrDbService.getHomeProperties()
.subscribe(property => {
// console.log(property['results']);
this.propertyWarehouseService.setPropertiesHome(property['results'])
return this.propertiesFrDb = property['results']
},
)
}
}
I want to test this.getPropertiesFrDb() in ngOnInit
i would like to test the case with this.propertyWarehouseService.currentValuesProperties !== ''and checked that this.getPropertiesFrDbService.getHomeProperties() is called and checked the value of propertiesFrDb
and my spec.ts file
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '#angular/core/testing';
import { MainSectionComponent } from './home-properties.component';
import { CUSTOM_ELEMENTS_SCHEMA } from '#angular/core';
import { MaterialModule } from 'src/app/material/material.module';
import { HttpClientTestingModule } from '#angular/common/http/testing';
import { RouterTestingModule } from '#angular/router/testing';
import { GetPropertiesFrDbService } from 'src/app/services/getPropertiesFromDb/get-properties-fr-db.service';
import { MOCKPROPERTIES, MockPropertyWarehouseService } from 'src/app/mocks/property-post';
import { NgxPaginationModule, PaginatePipe } from 'ngx-pagination';
import { PropertyWarehouseService } from 'src/app/services/propertyWarehouse/property-warehouse.service';
import { BsDropdownModule } from 'ngx-bootstrap';
import { NgbModule } from '#ng-bootstrap/ng-bootstrap';
import { StorageServiceModule } from 'angular-webstorage-service';
import { of } from 'rxjs/internal/observable/of';
fdescribe('MainSectionComponent', () => {
let component: MainSectionComponent;
let fixture: ComponentFixture<MainSectionComponent>;
const PROPERTYMODEL = MOCKPROPERTIES;
const spyPropertyWarehouseService = jasmine.createSpyObj('spypropertyWarehouseService', ['currentValuesProperties', 'getPropertyHome$']);
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
MaterialModule,
HttpClientTestingModule,
RouterTestingModule.withRoutes([]),
NgxPaginationModule,
BsDropdownModule.forRoot(),
NgbModule,
StorageServiceModule,
],
declarations: [
MainSectionComponent,
],
providers: [
{
provide: PropertyWarehouseService,
useValue: spyPropertyWarehouseService
}
],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(MainSectionComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', (() => {
// console.log('properties', component);
expect(component).toBeTruthy();
}));
it('Should get propertiesFrDb from GetPropertiesFrDbService', async(() => {
spyPropertyWarehouseService.currentValuesProperties.and.returnValue(PROPERTYMODEL);
spyPropertyWarehouseService.getPropertyHome$.and.returnValue(of(PROPERTYMODEL));
expect(component.propertiesFrDb).toBe(PROPERTYMODEL);
console.log('spy',spyPropertyWarehouseService);
}));
});
Try creating a stub as below:
export class PropertyWarehouseServiceStub{
currentValuesProperties = '';
getPropertyHome$ = new BaheviorSubject<any>('someObj');
setPropertiesHome(){ }
}
export class GetPropertiesFrDbServiceStub{
getHomeProperties(){
return of({results: 'val'})
}
}
in component file make the service public in constructor so that we can override some of its behaviors:
constructor(...,
public propertyWarehouseService: PropertyWarehouseService,
public getPropertiesFrDbService: GetPropertiesFrDbService,
....)
and in spec file as :
providers: [
{
provide: PropertyWarehouseService,
useClass: PropertyWarehouseServiceStub
},{
provide: GetPropertiesFrDbService,
useClass: GetPropertiesFrDbServiceStub
}
],
......
....
..
it('should call getPropertiesFrDb() in ngOnInit',()=>{
spyOn(component,'getPropertiesFrDb').and.callThrough();
component.ngOnInit();
expect(component.getPropertiesFrDb).toHaveBeenCalled();
})
it('inside getPropertiesFrDb() should call getPropertiesFrDbService.getHomeProperties() when "propertyWarehouseService.currentValuesProperties" is empty,()=>{
spyOn(component.getPropertiesFrDbService,'getHomeProperties').and.callThrough();
spyOn(component.propertyWarehouseService,'setPropertiesHome').and.callThrough();
component.getPropertiesFrDb();
expect(component.getPropertiesFrDbService.getHomeProperties).toHaveBeenCalled();
expect(component.propertyWarehouseService.setPropertiesHome).toHaveBeenCalledWith('val');
expect(component.propertiesFrDb).toBe('someObj');
})
it('inside getPropertiesFrDb() should not call getPropertiesFrDbService.getHomeProperties() when "propertyWarehouseService.currentValuesProperties" is NOT empty,()=>{
component.propertyWarehouseService.currentValuesProperties = 'Not empty';
spyOn(component.getPropertiesFrDbService,'getHomeProperties').and.callThrough();
spyOn(component.propertyWarehouseService,'setPropertiesHome').and.callThrough();
component.getPropertiesFrDb();
expect(component.getPropertiesFrDbService.getHomeProperties).not.toHaveBeenCalled();
expect(component.propertyWarehouseService.setPropertiesHome).not.toHaveBeenCalledWith('val');
expect(component.propertiesFrDb).toBe('val');
})
You can refer to this intro article written by me on Karma-jasmine which contains more article references for several test use cases.
This one is very much similar to what you are looking for. I am planning to write some more articles, in case you wanna follow.
Also, I have no clue on why you are using return as below inside getPropertiesFrDb()
return this.propertiesFrDb = prop
which is of no use because no value of this function has been assigned to any variable inside ngOnInit.

Angular2 unit test. Check if a function calls router.navigate

I'm new to testing. Have a function that, when it's called, navigates to a new page. But the test is failing with an error:
Expected spy navigate to have been called with [ [
'./../admin/documents/12345' ] ] but it was never called.
public showProfile(id) {
this.router.navigate(['./../admin/documents/' + id]);
}
In my test file I have:
import {ComponentFixture, TestBed, fakeAsync, tick } from '#angular/core/testing';
import { AppComponent} from './app.component';
import {FormsModule} from "#angular/forms";
import {HttpClientTestingModule} from "#angular/common/http/testing";
import {Router} from "#angular/router";
describe('AppComponent', () => {
let component: AppComponent;
let fixture: ComponentFixture<AppComponent>;
let mockRouter;
beforeEach(() => {
TestBed.configureTestingModule({
declarations: [ AppComponent],
imports: [FormsModule, HttpClientTestingModule],
providers: [
{ provide: Router, useValue: mockRouter }]
});
fixture = TestBed.createComponent(AppComponent);
component = fixture.componentInstance;
mockRouter = {
navigate: jasmine.createSpy('navigate')
};
});
describe('when showProfile() is called', () => {
it('navigates to a profile page',() => {
const id= 12345;
component.showProfile(id);
expect(mockRouter.navigate)
.toHaveBeenCalledWith(['./../admin/documents/' + id]);
});
});
});
You need to initialize the mockRouter before you use it in TestBed.configureTestingModule.
Also, expect(mockRouter.navigate).toHaveBeenCalledWith(['./../admin/documents/' + id]); won't work because ['a'] !== ['a']. As a workaround, you can do something like:
expect(mockRouter.navigate.calls.mostRecent().args[0][0])
.toEqual('./../admin/documents/' + id);
I've created a Stackblitz for your test.

ngModel not updated with input value angular 2 test karma

I have a problem during my units test with angular 2. I just want to test an input to check if my model can't be more than 16 chars. (I have indeed use a maxlength in my html). So I enter a new value in my input and use dispatchEvent to set the input in the model but the model is nether update...
Here is my test :
/* tslint:disable:no-unused-variable */
import { EditRequestComponent } from './edit-request.component';
import 'rxjs/add/operator/map';
import 'rxjs/add/observable/of';
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '#angular/core/testing';
import { By } from '#angular/platform-browser';
import { __platform_browser_private__ as _ } from '#angular/platform-browser';
import { DebugElement } from '#angular/core';
import { MvpSelector } from './mvpSelector/mvpSelector.component';
import { MockBackend, MockConnection } from '#angular/http/testing';
import { FormsModule } from '#angular/forms';
import { ActivatedRoute } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import { HttpModule, Http, BaseRequestOptions, Response } from '#angular/http';
import { InMemoryWebApiModule } from 'angular-in-memory-web-api';
import { InMemoryWebApiService } from '../../services/in-memory.service';
import { MyDatePickerModule } from 'mydatepicker/dist/my-date-picker.module';
import { ReferentialService, RequestService, ConfigurationService } from '../../services';
import { MvpRequestClass } from '../../models';
//////// SPECS /////////////
describe('EditRequestComponent', function () {
let comp: EditRequestComponent;
let fixture: ComponentFixture<EditRequestComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
ReferentialService,
ConfigurationService,
RequestService,
MockBackend,
BaseRequestOptions,
{
provide: Http,
useFactory: (backend: MockBackend, options: BaseRequestOptions) => {
return new Http(backend, options);
},
deps: [ MockBackend, BaseRequestOptions ]
},
{
provide: ActivatedRoute,
useValue: {
snapshot: {
params: {id: 1},
data: {request: new MvpRequestClass()}
}
}
}
],
declarations: [ MvpSelector, EditRequestComponent ],
imports: [ FormsModule, FormsModule, MyDatePickerModule,
HttpModule ]
}).compileComponents();
fixture = TestBed.createComponent(EditRequestComponent);
comp = fixture.componentInstance;
});
it('should create component', () => expect(comp).toBeDefined() );
it('should have a mvpRequestComponent', () =>
{
fixture.detectChanges();
expect(comp.mvpReq).toBeDefined();
});
it('should not have more than 16 char in moInCharge input', fakeAsync(() => {
comp.mvpReq.moInCharge = "oldValue";
fixture.detectChanges();
var field = fixture.debugElement.query(By.css('.input-moInCharge')).nativeElement;
field.value = "aaaaaaaaaaaaaaaaaaaaaa";
_.getDOM().dispatchEvent(field, _.getDOM().createEvent("input"));
tick();
console.log(field.value);
expect(comp.mvpReq.moInCharge.length).toBeLessThanOrEqual(16);
}));
});
Here are the Log print in my console:
LOG LOG: 'oldValue'
You can also see that I have a weird way to use dispatchEvent. That's because when I import and use it directly from '#angular/platform-browser/testing/browser_util' I have an error message :
Uncaught SyntaxError: Unexpected token import
at webpack:///~/#angular/platform-browser/testing/browser_util.js:8:0 <- karma.entry.js:49907
It seems that there was imports directly in the file browser_util.d.ts that our webpack doesn't appreciate. But the dispatchEvent function is sensibly used the same way. So I don't understand why the model doesn't update.
Here are few test that I made to make it work:
use async() function rather than fakeAsync
use the dispatchEvent function of MDN
use fixture.whenStable() with a then()
change the number of 'a' to check if the value doesn't update because
there are too many
a combination of all this points
And that's all. I must admit that I don't know how to proceed differently to make it work. If you can help me or you want more details, don't hesitate.
Thanks in advance.

Categories