Home > Blockchain >  Not able to set mock and with a subscription for testing
Not able to set mock and with a subscription for testing

Time:11-14

I am getting following errors while do the test:

  1. TypeError: Cannot read properties of undefined (reading 'subscribe')
  2. Error: <toHaveBeenCalled> : Expected a spy, but got Function.

Not able to understand with these erros.

Here is my spec file:

import { HttpClientTestingModule } from '@angular/common/http/testing';
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule } from '@ngx-translate/core';
import { HfsTableComponent } from '../../../../../shared/hfs-table/hfs-table.component';
import { SharedModule } from '../../../../../shared/shared.module';
import { AddClinicDataService } from '../services/personnel-add-clinic-data.service';
import { PersonnelDataService } from '../services/personnel-data.service';
import { PersonnelTranslateService } from '../services/personnel-translate.service';
import { PersonnelViewDataService } from '../services/personnel-view-data.service';

import { DetailsComponent } from './details.component';
import { HFSTaleSchema } from './hfs-table.schema';

fdescribe('DetailsComponent', () => {
    let component: DetailsComponent;
    let fixture: ComponentFixture<DetailsComponent>;

    const mockPersonnelViewDataService = {
        fetchUserProfileWithCode: () => {
            const userProfileWithCodes$ = {
                sjmAccDTO: {
                    sjmAccDTO: {
                        lastUpdtUserId: '',
                        createUserId: '',
                        firstName: null,
                        middleName: '',
                        lastName: '',
                        mainPhone: {
                            phoneNum: '',
                            areaCode: '',
                            countryCode: '',
                        },
                        timeZoneCd: '',
                        status: '',
                        hfsUserCountries: [],
                        hfsUserClinics: [],
                    },
                    customerDTO: [],
                },
                customerDTO: [],
                countryCds: [],
                timeZoneDTO: [],
            };
            return { subscribe: () => userProfileWithCodes$ };
        },
    };

    beforeEach(async(() => {
        TestBed.configureTestingModule({
            declarations: [DetailsComponent, HfsTableComponent],
            imports: [
                SharedModule,
                HttpClientTestingModule,
                BrowserAnimationsModule,
                TranslateModule.forRoot(),
            ],
            providers: [
                {
                    provide: PersonnelViewDataService,
                    useValue: mockPersonnelViewDataService,
                },
                PersonnelDataService,
                PersonnelTranslateService,
                AddClinicDataService,
            ],
        }).compileComponents();
    }));

    beforeEach(() => {
        fixture = TestBed.createComponent(DetailsComponent);
        component = fixture.componentInstance;
        component.tableSchema = HFSTaleSchema;
        fixture.detectChanges();
    });

    it('should create', () => {
        expect(component).toBeTruthy();
    });

    fit('should service available', () => {
        fixture.detectChanges();
        expect(
            mockPersonnelViewDataService.fetchUserProfileWithCode
        ).toHaveBeenCalled();
    });
});

what i missed or what i did not provide here? any one help me to understand please?

component:

import {
    AfterViewInit,
    ChangeDetectionStrategy,
    ChangeDetectorRef,
    Component,
    OnDestroy,
    OnInit,
    ViewChild,
    ViewEncapsulation,
} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { HfsFormComponent } from '../../../../../shared/hfs-form/hfs-form.component';
import { ColumnSorter } from '../../../../../shared/utils/column-sort.helper';
import { AddClinicDataService } from '../services/personnel-add-clinic-data.service';
import { PersonnelTranslateService } from '../services/personnel-translate.service';
import {
    PersonnelViewDataService,
    UserProfileCombinedProps,
} from '../services/personnel-view-data.service';
import { AddClinicComponent } from './add-clinic';
import { HFSPersonnelFormSchema } from './hfs-personnel-form.schema';
import { HFSTaleSchema } from './hfs-table.schema';

const hfsUserClinics = [
    {
        hfsUserCountryId: 2895,
        countryCd: 140,
    },
];
@Component({
    selector: 'app-details',
    templateUrl: './details.component.html',
    styleUrls: ['./details.component.scss'],
    encapsulation: ViewEncapsulation.None,
    changeDetection: ChangeDetectionStrategy.OnPush,
})
export class DetailsComponent
    extends AddClinicComponent
    implements OnInit, AfterViewInit, OnDestroy
{
    personnelFormSchema;
    tableSchema;

    @ViewChild('profileForm', { static: false })
    profileForm: HfsFormComponent;
    error;
    show: boolean = false;

    constructor(
        private personnelViewDataService: PersonnelViewDataService,
        private personnelTranslateService: PersonnelTranslateService,
        private cdr: ChangeDetectorRef,
        public dialog: MatDialog,
        protected aAddClinicDataService: AddClinicDataService
    ) {
        super(aAddClinicDataService);
    }

    ngOnInit(): void {
        this.personnelFormSchema = HFSPersonnelFormSchema;
        this.tableSchema = HFSTaleSchema;
        this.personnelViewDataService.fetchUserProfileWithCode();
    }

    handleRemoveClinic(): void {
        const rowsUpdate = this.tableSchema.rows.filter((row) => !row.checked);
        const headerUpdate = this.tableSchema.headers.map((item) => ({
            ...item,
            ...(item.checked === undefined ? undefined : { checked: false }),
        }));

        this.tableSchema = {
            ...this.tableSchema,
            headers: headerUpdate,
            rows: rowsUpdate,
        };
    }

    formValueUpdater(field, response, countryCds, timeZoneDTO): void {
        switch (field.controlName) {
            case 'userName':
                field.value = response.lastUpdtUserId;
                return field;
            case 'areacode':
                field.value = response.mainPhone.areaCode;
                return field;
            case 'countrycode':
                field.value = response.mainPhone.countryCode;
                return field;
            case 'mainphone':
                field.value = response.mainPhone.phoneNum;
                return field;
            case 'firstName':
                field.value = response.firstName;
                return field;
            case 'middleName':
                field.value = response.middleName;
                return field;
            case 'lastName':
                field.value = response.lastName;
                return field;
            case 'countries':
                field.options =
                    this.personnelTranslateService.translator(countryCds);
                console.log('response.hfsUserClinics', response.hfsUserClinics);
                field.value = response.hfsUserClinics;
                return field;
            case 'timezone':
                field.options =
                    this.personnelTranslateService.translator(timeZoneDTO);
                field.value = response.timeZoneCd || 228;
                return field;
            case 'slide':
                field.value = !response.timeZoneCd ? false : true;
                return field;
            default:
                return field;
        }
    }

    rowGenerator(rows) {
        if (!rows) return;
        return rows.reduce((p, c) => {
            p.push({
                adminUserName: c.adminUserName,
                customerName: c.customer.customerName,
                address: Object.values(c.customer.address).filter(
                    (v) => v != null
                ),
                phoneNum: c.customer.mainPhone.phoneNum,
                checked: false,
            });
            return p;
        }, []);
    }

    formGenerator(sjmAccDTO, countryCds, timeZoneDTO): void {
        const { formProps } = this.personnelFormSchema;

        const fieldMaker = (field) => {
            const { group } = field;
            if (group) {
                return field.groupElements.map((field) =>
                    this.formValueUpdater(
                        field,
                        sjmAccDTO,
                        countryCds,
                        timeZoneDTO
                    )
                );
            }
            field = this.formValueUpdater(
                field,
                sjmAccDTO,
                countryCds,
                timeZoneDTO
            );
            return field;
        };

        const nwF = formProps.map((row, i) => {
            row.column?.map((column) => {
                column?.fields.map((field, i) => {
                    return fieldMaker(field);
                });
                return column;
            });
            return row;
        });
        return { ...this.personnelFormSchema, formProps: nwF };
    }
    ngAfterViewInit(): void {
        this.setPage();
    }
    ngOnDestroy() {}

    setPage(): void {
        this.personnelViewDataService.userProfileWithCodes$.subscribe(
            (userProfile: UserProfileCombinedProps) => {
                if (!userProfile) return;

                const { sjmAccDTO, customerDTO, countryCds, timeZoneDTO } =
                    userProfile;
                this.tableSchema = {
                    ...this.tableSchema,
                    rows: this.rowGenerator(customerDTO),
                };
                this.personnelFormSchema = this.formGenerator(
                    sjmAccDTO,
                    countryCds,
                    timeZoneDTO
                );
                this.error = null;
                this.show = true;
                this.cdr.detectChanges();
            },
            (err) => this.errorHandler(err)
        );
    }

    errorHandler(err): void {
        const { status, statusText } = err;
        this.error = { status, statusText };
        this.cdr.detectChanges();
    }

    handleFormSubmit(formValues): void {
        console.log('form-values', formValues);
    }

    resetPage(): void {
        this.setPage();
    }

    postProfile(): void {
        this.profileForm.formOnSubmit();
    }
    handleColumSort(column): void {
        ColumnSorter(
            this.tableSchema.rows,
            column.label,
            column.dir,
            column.sortType
        );
    }

    protected rowTransformer(inRow): void {
        console.log(this.tableSchema.rows.length);
        this.tableSchema = {
            ...this.tableSchema,
            rows: this.tableSchema.rows.concat(inRow),
        };
        this.cdr.detectChanges();
    }
}

Service:

import { Location } from '@angular/common';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { combineLatest, Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { environment } from '../../../../../../environments/environment';
import { PersonnelDataService } from './personnel-data.service';

export interface UserCountriesProps {
    [key: string]: number;
}
export interface UserClinicsProps {
    [key: string]: number;
}

export interface CountryCdProps {
    defaultOrder: number;
    label: string;
    codeId: number;
    code: string;
    codeDesc: string;
    codeQualifier: string;
}

export interface TimeZoneDTOProps extends CountryCdProps {
    defaultOrder: number;
    label: string;
    codeId: number;
    code: string;
    codeDesc: string;
    codeQualifier: string;
}

export interface customerDTOProps {
    adminUserName: string;
    customer: {
        customerName: string;
        mainPhone: {
            phoneNum: string;
        };
        address: {
            stateProvince: string | null;
            streetAddress: string | null;
            streetAddress2: string | null;
            streetAddress3: string | null;
            city: string;
            zipCode: string;
            countryCd: number;
            stateCd: number;
        };
    };
}
export interface UserProfileProps {
    sjmAccDTO: {
        lastUpdtUserId: string;
        createUserId: string;
        firstName: string | null;
        middleName: string | null;
        lastName: string | null;
        mainPhone: {
            countryCode: string;
            areaCode: string;
            phoneNum: string;
        };
        timeZoneCd: string | number | null;
        status: string | number;
        hfsUserCountries: UserCountriesProps[];
        hfsUserClinics: UserClinicsProps[];
    };
    customerDTO: customerDTOProps[];
}
export interface UserProfileCombinedProps {
    sjmAccDTO: UserProfileProps;
    customerDTO: customerDTOProps[];
    countryCds: CountryCdProps[];
    timeZoneDTO: TimeZoneDTOProps[];
}

@Injectable()
export class PersonnelViewDataService {
    private URL = environment.api_url;
    private URLLocal = 'https://d1-hfcloudapim.products.abbott/';
    public userProfile$: Observable<UserProfileCombinedProps | null>;
    public userCodes$: Observable<TimeZoneDTOProps | null>;
    public userProfileWithCodes$: Observable<UserProfileCombinedProps>;

    constructor(
        private http: HttpClient,
        private personnelDataService: PersonnelDataService,
        private _location: Location
    ) {
        if (!this.UserDetails$) {
            this._location.back();
        }
    }

    get UserDetails$() {
        return this.personnelDataService.userDetails$.getValue();
    }

    fetchUserProfileWithCode() {
        this.userProfileWithCodes$ = combineLatest([
            this.fetchUserDetails(),
            this.fetchUserCode(),
        ]).pipe(map((data: any[]) => ({ ...data[0], ...data[1] })));
        return this.userProfileWithCodes$;
    }

    fetchUserDetails() {
        this.userProfile$ = this.http
            .get<UserProfileCombinedProps>(
                this.URL  
                    `hfs-admin/hfs-users/user-details?sjmAccountId=108843`
                // `hfs-admin/hfs-users/user-details?sjmAccountId=${this.UserDetails$?.sjmAccountId}`
                // `hfs-admin/hfs-users/user-details?sjmAccountId=108843`
            )
            .pipe(
                map((profile) => profile),
                catchError(this.handleError)
            );

        return this.userProfile$;
    }

    fetchUserCode() {
        this.userCodes$ = this.http
            .get<TimeZoneDTOProps>(this.URL   `hfs-admin/codes`)
            .pipe(
                map((userCode) => userCode),
                catchError(this.handleError)
            );

        return this.userCodes$;
    }

    handleError(err: HttpErrorResponse) {
        return throwError(err);
    }
}

CodePudding user response:

Try the following things

  • Inject the service into the testbed
  • Use spyon in the test case to spy on the service method
  • Inside mockPersonnelViewDataService add userProfileWithCodes$ as a property
let mockService: PersonnelViewDataService;

beforeEach(() => {
  fixture = TestBed.createComponent(FileUploadComponent);
  component = fixture.componentInstance;
  mockService = TestBed.get(PersonnelViewDataService);
  fixture.detectChanges();
});

it('should service available', () => {
  const spy = spyOn(mockService, 'fetchUserProfileWithCode');
  component.ngOnInit();
  expect(spy).toHaveBeenCalled();
});

const mockPersonnelViewDataService = {
    userProfileWithCodes$: of(),
    fetchUserProfileWithCode: () => {
      const userProfileWithCodes$ = {
        ...
      }
    }
}
  • Related