Search code examples
angularunit-testingjasmineabstract-class

Angular 8 - Unit test for abstract service


I should write a unit test for an abstract service. We have couple of SSE channels in the application and we created base SSE service to be extended and it provides some basic functionality.

export abstract class BaseSseService {
  protected zone: NgZone;

  protected sseChannelUrl: string;

  private eventSource: EventSource;
  private reconnectFrequencySec: number = 1;
  private reconnectTimeout: any;

  constructor(channelUrl: string) {
    const injector = AppInjector.getInjector();
    this.zone = injector.get(NgZone);
    this.sseChannelUrl = channelUrl;
  }

  protected abstract openSseChannel(): void;
  //...
}

Now, before it was made abstract unit test were running. Once I changed it to the abstract class, I'm getting this error while trying to run should be created test: Error: Can't resolve all parameters for TestBaseSseService: (?).

My test looks like this:

class TestBaseSseService extends BaseSseService {
  openSseChannel() {
    console.log('Test');
  }
}

fdescribe('BaseSseService', () => {
  beforeEach(() => {
    TestBed.configureTestingModule({
      providers: [
        TestBaseSseService,
        Injector,
        { provide: String, useValue: "test" }
      ]
    });
    AppInjector.setInjector(TestBed.get(Injector));
  })

  describe(':', () => {
    function setup() {
      return TestBed.get(TestBaseSseService)
    }

    it('should be created', () => {
      const service = setup();
      expect(service).toBeTruthy();
    });
  });  
});

The question is, should I even bother with unit test for abstract service and if so how to solve this issue? As you can see, mocking the service does not solve this nor does adding BaseSseService to TestBed providers.


Solution

  • It's trying to provide the channelUrl from the abstract class, but this is obviously not a provider or an InjectionToken.

    You can change your service to handle this:

    class TestBaseSseService extends BaseSseService {
      constructor() {
        super('url');
      }
    
      openSseChannel() {
        console.log('Test');
      }
    }
    

    And yes, it's always good to test an abstract class. This way you can be sure-ish that whenever you implement it, it will act the same (when tested properly)