Home > Enterprise >  How to mock a dependency in Jasmine/Karma
How to mock a dependency in Jasmine/Karma

Time:03-17

I have a method, that I want to unit test, but in the method there is a dependency to a storageService. I don't understand, how I can mock the dependency the right way so the mock is called instead and returns a certain value. Please help me.

import { TestBed } from '@angular/core/testing';

import { ProductDataService } from './product-data.service';
import { StorageService } from './storage.service';

describe('ProductDataService', () => {
  let service: ProductDataService;
  let storageServiceSpy;
  
  let mockFileId, getMock;
  
  getMock = (key: string): Promise<any> => {
    return new Promise((resolve, reject) => {
      if (key == "PRODUCT_DATA_INFO_LOCAL") {
        return resolve();
      } else {
        return reject();
      }
    })
  }

  beforeEach(() => {    
    mockFileId = "test";
    
    storageServiceSpy = jasmine.createSpyObj('StorageService', ['get', 'set']);

    TestBed.configureTestingModule({
      imports: [],
      providers: [
        { provide: StorageService, useValue: storageServiceSpy }
      ]
    });
    service = TestBed.inject(ProductDataService);
  });

  it('should call getFileInfo and resolve', (done) => {
    let spy = spyOn(storageServiceSpy, 'get').and.callFake(getMock("PRODUCT_DATA_INFO_LOCAL"));
    service['getFileInfo'](mockFileId).then((data) => {
      expect(spy).toHaveBeenCalled();
      done();
    })

  })
});

Jasmine gives me the following error:

Error: : get has already been spied upon

CodePudding user response:

I mangaged to do it by myself. Here is the code:

describe('ProductDataService', () => {
  let fixture: ComponentFixture<ProductDataService>;
  let component: ProductDataService;
  let service: ProductDataService;
  
  let mockFileId;

  const mockStorageService = {    
    get: () => {return Promise.resolve();}
  }

  beforeEach(async(() => {    
    mockFileId = "test";    

    TestBed.configureTestingModule({
      providers: [
        { provide: StorageService, useValue: mockStorageService }
      ]
    })
    service = TestBed.inject(ProductDataService);
  }));

  it('should call getFileInfo and resolve', async (done) => {    
    let spy = spyOn<any>(mockStorageService, 'get').and.resolveTo(['foo', 'bar']);

    service['getFileInfo'](mockFileId).then((data) => {
      expect(spy).toHaveBeenCalled();
      done();
    })
  })
  
});

I think the key was mocking the service as a const on the one hand and on the other hand creating the spy later in the test case. I also declared the test-case as async, because of these timeout errors. Maybe someone knows more about it?

CodePudding user response:

OK, I tried it without the double spy:

beforeEach(() => {  
    storageServiceSpy = jasmine.createSpyObj('StorageService', {get: getMock, set: null});
    TestBed.configureTestingModule({
      providers: [
        { provide: StorageService, useValue: storageServiceSpy }
      ]
    });
    service = TestBed.inject(ProductDataService);
  });

it('should call getFileInfo and resolve', (done) => {
    service['getFileInfo'](id).then(() => {
      expect(storageServiceSpy).toHaveBeenCalled();
      done();
    })

  })

With the solution above, I get:

Error: Timeout - Async function did not complete within 5000ms (set by jasmine.DEFAULT_TIMEOUT_INTERVAL)

  • Related