0

I am doing unit testing with Jasmin and Istanbul.

I try to do unit testing on the following method:

 getDiploma(spinnerMessage: string): Observable<DiplomaModel> {
        const diploma = this.diplomasState.diploma;
        if (diploma) {
            // Return diploma info from store
            return observableOf(diploma);
        } else {
            // Get and return diploma info from backend
            return this.http.get<DiplomaModel>(this.apiDiploma, spinnerMessage).pipe(map((result) => {
                return this.setDiplomasInStore(result);
            }));
        }
    }

And my unit test looks like this:

 it('It should return diploma from the backend when snapshot exist ', () => {

            diplomaStoreServiceMock.returnState(Object.assign({}, this.diplomaState));
            const diplomaState = diplomaService.getDiplomaSnapshot();
            expect(diplomaState).toEqual(Object.assign({}, this.diplomaState));

            // tslint:disable-next-line:no-commented-code
            /*  const response = [{ mock: 'mock' } as any];
             diplomaService['getDiplomaSnapshot'] = response;
             diplomaService.getDiplomaState().subscribe(i => {
                 expect(i).toEqual(response);
             }); */
        });

it('It should return diploma if snapshot doenst exist', () => {
    const apiString = 'api/register/teachers/current/diploma';
    const spyHttp = spyOn(afwHttp, 'get').and.callThrough();
    const spyDocument = spyOn(diplomaService, 'getDiploma').and.callThrough();

    diplomaStoreServiceMock.returnState({});
    const response = { 'mock': 'mockResult' };
    afwHttp.setupOnlyResponse(response, 200);
    diplomaService.getDiploma('spinnerText').subscribe(result => expect(result['mock']).toEqual('mockResult'));
    afwHttp.returnOnlyResponse();
    expect(spyHttp).toHaveBeenCalledWith(apiString, 'spinnerText');
    expect(spyDocument).toHaveBeenCalledWith('spinnerText');
});

But in Istanbul I get still a red line on this line:

 return observableOf(diploma);

with a I and if I hover over the I is says:

if Path not taken.

So how to solve this?

Thank you.

but that is why I have two methods. But you mean this method:

it('It should return diploma from the backend when snapshot exist ', () => {



     diplomaStoreServiceMock.returnState(Object.assign({}, this.diplomaState));
        const diplomaState = diplomaService.getDiplomaSnapshot();
        expect(diplomaState).toEqual(Object.assign({}, this.diplomaState));
    });

What I have to change about this test method?

You mean like this:

 it('It should return diploma from the backend when snapshot exist ', () => {

        diplomaStoreServiceMock.returnState(Object.assign({}, this.diplomaState));
        const diplomaState = diplomaService.getDiplomaSnapshot();
        diplomaService.getDiploma('spinnerText');
        expect(diplomaState).toEqual(Object.assign({}, this.diplomaState));
    });

I also tried like this:

it('It should return diploma from the backend when snapshot exist ', () => {
    const response = [{ mock: 'mock' } as any];
    diplomaStoreServiceMock.returnState(Object.assign({}, this.diplomaState));
    const diplomaState = diplomaService.getDiplomaSnapshot();
    diplomaService.getDiploma('spinnerText').subscribe( i => {
        expect(i).toEqual(response);

    });
    expect(diplomaState).toEqual(Object.assign({}, this.diplomaState));
});

And if I add this method:

  it('It should return diploma', () => {
        const response = [{ mock: 'mock' } as any];
        diplomaService.getDiploma('spinnerText').subscribe( i => {
            expect(i).toEqual(response);

        });
    });

I get this error:

ERROR in apps/register/src/app/shared/services/src/diploma.service.spec.ts(67,31): error TS2345: Argument of type 'any[]' is not assignable to parameter of type 'Expected'. Type 'any[]' is not assignable to type 'ObjectContaining'. Property 'jasmineMatches' is missing in type 'any[]'.

So I have now three methods:

 it('It should return diploma from the backend when snapshot exist ', () => {
        diplomaStoreServiceMock.returnState(Object.assign({}, this.diplomaState));
        const diplomaState = diplomaService.getDiplomaSnapshot();
        expect(diplomaState).toEqual(Object.assign({}, this.diplomaState));
    });

    it('It should return diploma', () => {
        const response = [{ mock: 'mock' } as any];
        diplomaService.getDiploma('spinnerText').subscribe( i => {
            expect(i).toEqual(response);

        });
    });


    it('It should return diploma if snapshot doenst exist', () => {
        const apiString = 'api/register/teachers/current/diploma';
        const spyHttp = spyOn(afwHttp, 'get').and.callThrough();
        const spyDocument = spyOn(diplomaService, 'getDiploma').and.callThrough();
        const response = { 'mock': 'mockResult' };
        afwHttp.setupOnlyResponse(response, 200);
        diplomaService.getDiploma('spinnerText').subscribe(result => expect(result['mock']).toEqual('mockResult'));
        afwHttp.returnOnlyResponse();
        expect(spyHttp).toHaveBeenCalledWith(apiString, 'spinnerText');
        expect(spyDocument).toHaveBeenCalledWith('spinnerText');
    });

This is the hole test file:

describe('Services: DiplomaService', () => {


    let afwHttp: AfwHttpMock;
    let diplomaService: DiplomaService;
    let diplomaStoreServiceMock: DiplomaStoreServiceMock;
    let feedbackStoreServiceMock: FeedbackStoreServiceMock;
    let multiFileUploadServiceMock: MultiFileUploadServiceMock;




    beforeEach(() => {
        afwHttp = new AfwHttpMock();
        diplomaStoreServiceMock = new DiplomaStoreServiceMock();
        multiFileUploadServiceMock = new MultiFileUploadServiceMock();
        feedbackStoreServiceMock = new FeedbackStoreServiceMock();
        diplomaService = new DiplomaService(afwHttp as any,
            diplomaStoreServiceMock as any,
            feedbackStoreServiceMock as any,
            multiFileUploadServiceMock as any);
    });

    it('should create an instance', () => {
        expect(diplomaService).toBeTruthy();
    });


    it('It should return diplomadocumentList', () => {
        // tslint:disable-next-line:no-duplicate-string
        const apiString = 'api/register/teachers/current/diploma';
        const spyHttp = spyOn(afwHttp, 'get').and.callThrough();
        const spyDocumentList = spyOn(diplomaService, 'getDiplomaDocumentList').and.callThrough();
        const response = { 'mock': 'mockResult' };
        afwHttp.setupOnlyResponse(response, 200);

        const result2 = diplomaService.getDiplomaDocumentList('spinnerText');
        result2.subscribe(() => {
            expect(spyHttp).toHaveBeenCalledWith(apiString, 'spinnerText');
            expect(spyDocumentList).toHaveBeenCalledWith('spinnerText');
        });
        afwHttp.returnOnlyResponse();
    });


    // tslint:disable-next-line:no-commented-code

    it('It should return diploma from the backend when snapshot exist ', () => {
        diplomaStoreServiceMock.returnState(Object.assign({}, this.diplomaState));
        const diplomaState = diplomaService.getDiplomaSnapshot();

        diplomaService.getDiploma('spinnerText').subscribe(result => expect(result['mock']).toEqual('mockResult'));

        expect(diplomaState).toEqual(Object.assign({}, this.diplomaState));
    });

   // tslint:disable-next-line:no-commented-code
   /*  it('It should return diploma', () => {
        const response = [{ mock: 'mock' } as any];
        diplomaService.getDiploma('spinnerText').subscribe( i => {
            expect(i).toEqual(response);

        });
    }); */


    it('It should return diploma if snapshot doenst exist', () => {
        const apiString = 'api/register/teachers/current/diploma';
        const spyHttp = spyOn(afwHttp, 'get').and.callThrough();
        const spyDocument = spyOn(diplomaService, 'getDiploma').and.callThrough();
        const response = { 'mock': 'mockResult' };
        afwHttp.setupOnlyResponse(response, 200);
        diplomaService.getDiploma('spinnerText').subscribe(result => expect(result['mock']).toEqual('mockResult'));
        afwHttp.returnOnlyResponse();
        expect(spyHttp).toHaveBeenCalledWith(apiString, 'spinnerText');
        expect(spyDocument).toHaveBeenCalledWith('spinnerText');
    });

    // tslint:disable-next-line:no-identical-functions
    it('It should save diploma record to database', () => {

        const apiString = 'api/register/teachers/current/diploma';
        const spyHttp = spyOn(afwHttp, 'post').and.callThrough();
        const spyDocument = spyOn(diplomaService, 'saveDiplomaMetaData').and.callThrough();
        const response = { 'mock': 'mockResult' };
        afwHttp.setupOnlyResponse(response, 200);

        const result2 = diplomaService.saveDiplomaMetaData(false, 'spinnerText');
        result2.subscribe(() => {
            expect(spyHttp).toHaveBeenCalledWith(apiString, 'spinnerText');
            // tslint:disable-next-line:no-commented-code
            expect(spyDocument).toHaveBeenCalledWith(false, 'spinnerText');
        });
        afwHttp.returnOnlyResponse();
    });

    it('It should save documents for  diploma record', fakeAsync(() => {
        const spy = spyOn(multiFileUploadServiceMock, 'syncFilesWithBackend').and.callThrough();
        const spyFeedback = spyOn(feedbackStoreServiceMock, 'addErrorMessage');

        multiFileUploadServiceMock.setResponse([{ error: 'error' }]);


        diplomaService.saveDocumentForDiploma([{} as any], () => { }, MultiFileUploadResourcesModel.keys, { key: 'diploma', value: 'document' });

        expect(spy).toHaveBeenCalledWith('api/register/teachers/current/diploma/documents', [{}],
            MultiFileUploadResourcesModel.keys.bijlageToevoegenSpinnerTekst,
            MultiFileUploadResourcesModel.keys.bijlageVerwijderenSpinnerTekst
        );

        tick();
        expect(spyFeedback).toHaveBeenCalled();
    }));

    // tslint:disable-next-line:no-identical-functions
    it('should return document list ', fakeAsync(() => {
        const apiString = 'api/register/teachers/current/diploma';
        const spyHttp = spyOn(afwHttp, 'get').and.callThrough();
        const spyDocumentList = spyOn(diplomaService, 'getDiplomaDocumentList').and.callThrough();
        const response = { 'mock': 'mockResult' };

        multiFileUploadServiceMock.setResponse([{}]);
        afwHttp.setupOnlyResponse(response, 200);

        const result2 = diplomaService.getDiplomaDocumentList('spinnerText');
        result2.subscribe(() => {
            expect(spyHttp).toHaveBeenCalledWith(apiString, 'spinnerText');
            expect(spyDocumentList).toHaveBeenCalledWith('spinnerText');
            expect(spyDocumentList).not.toBeNull();

        });
        afwHttp.returnOnlyResponse();

    }));

    // tslint:disable-next-line:no-identical-functions
    it('It should return diplomaSnapshot', () => {
        diplomaStoreServiceMock.returnState(Object.assign({}, this.diplomaState));
        const diplomaState = diplomaService.getDiplomaSnapshot();
        expect(diplomaState).toEqual(Object.assign({}, this.diplomaState));
    });
});

I have it now like this:

it('It should return diploma from the snapshot as an observable ', () => {

        const expected = { code: '00100-00101', test: 'test' };
        afwHttp.setupOnlyResponse(expected, 200);
        const spyHttp = spyOn(afwHttp, 'get').and.callThrough();
        diplomaStoreServiceMock.returnState({diploma: expected});


        diplomaService.getDiploma('spinnerText').subscribe(result => expect(result['mock']).toEqual('mockResult'));
        afwHttp.returnOnlyResponse();
        expect(spyHttp).not.toHaveBeenCalledWith();

    });

But then I will get this error:

Services: DiplomaService > It should return diploma from the snapshot as an observable Expected undefined to equal 'mockResult'. Error: Expected undefined to equal 'mockResult'. at at SafeSubscriber._next (http://localhost:9877/src/app/shared/services/src/diploma.service.spec.ts?:65:93) at SafeSubscriber.push.../../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.__tryOrUnsub (http://localhost:9877/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/rxjs/_esm5/internal/Subscriber.js?:196:1) at SafeSubscriber.push.../../node_modules/rxjs/_esm5/internal/Subscriber.js.SafeSubscriber.next (http://localhost:9877/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/rxjs/_esm5/internal/Subscriber.js?:134:1)

Ah, oke.

I have it now like this:

 it('It should return diploma from the snapshot as an observable ', () => {

        const expected = { 'mock': 'mockResult' };
        afwHttp.setupOnlyResponse(expected, 200);
        const spyHttp = spyOn(afwHttp, 'get').and.callThrough();
        diplomaStoreServiceMock.returnState({diploma: expected});


        diplomaService.getDiploma('spinnerText').subscribe(result => expect(result['mock']).toEqual('mockResult'));
        afwHttp.returnOnlyResponse();
        expect(spyHttp).not.toHaveBeenCalledWith();

    });

and it works

niceToExist
  • 55
  • 1
  • 10
  • I believe you need to call the same method multiple times to make sure each code path is taken (as the error describes). You'll have to seed different data to hit that logic. –  Jan 27 '19 at 15:14
  • But can you give an example. Don't understand clearly – niceToExist Jan 27 '19 at 15:19
  • You'll have to add more code related to how you are creating your diplomaStoreServiceMock. As far as I can tell, you will probably have to set the diploma in another "it" like you are in the first "it" and call `diplomaService.getDiploma('spinnerText')` again to trigger the code block that is executed when a diploma is present. –  Jan 27 '19 at 15:23
  • And what do you mean with multiple times? – niceToExist Jan 27 '19 at 15:24
  • I edit the post – niceToExist Jan 27 '19 at 15:28
  • Ah, I think I see more now. You first test doesn't actually call `diplomaService.getDiploma` like the second one does. –  Jan 27 '19 at 15:45
  • I edit the post – niceToExist Jan 27 '19 at 15:48
  • And this post https://stackoverflow.com/questions/50678582/property-jasminematches-is-missing-in-type-x describes the issue with that. Can you share all the code in the file (imports can be omitted)? –  Jan 27 '19 at 15:53
  • I added the whole test file – niceToExist Jan 27 '19 at 15:57
  • Does it make sense that `const diploma = this.diplomasState.diploma; if (diploma) {` might not ever evaluate to 'true' in the test? –  Jan 27 '19 at 16:03
  • yes, oke, but how to make it true then? – niceToExist Jan 27 '19 at 16:05
  • Follow the code in your service and see how to set it's this.diplomasState.diploma to a value, do that in the test where it's supposed to be a value. –  Jan 27 '19 at 16:06
  • I edit the post – niceToExist Jan 27 '19 at 16:23
  • Oke, I solved it see the post. Thank you – niceToExist Jan 27 '19 at 16:27
  • @OneLunchMan. But can you help me with a other unit test? I will make separate post of it ok – niceToExist Jan 27 '19 at 16:28
  • I'm not sure using stack overflow to debug unit tests is the proper use of stack overflow- this link will help go into depth on unit testing (what/why/tooling) https://codecraft.tv/courses/angular/unit-testing/jasmine-and-karma/ It's worth spending the time to understand what the tools are doing and how they are interacting, as well as how to get the code you are working with the be in the test state that you want. It seems like you have some fundamentals about it down pretty well, but others could use some attention. –  Jan 27 '19 at 16:34
  • @OneLunchMan. I can only post a post of one hour – niceToExist Jan 27 '19 at 16:54
  • Yes, so please take my last comment to heart- –  Jan 27 '19 at 16:55
  • Yes, I am reading now the article you have sent. But I posted a other 'big' unit test via this link: https://stackoverflow.com/questions/54391126/jasmine-unit-testing-with-istanbul-big-unit-test – niceToExist Jan 27 '19 at 17:53

0 Answers0