Skip to content Skip to sidebar Skip to footer

Jasmine/karma Testing Angular 5 Http Responses

I'm totally new to TDD and I'm trying to debug a big Angular 5 application we're working in, at the company. The app is working well, but now I have to implement tests, and I'm le

Solution 1:

Yes, you"re doign something bad in your code. But don't worry, I did the same thing when I started.

First, you must understand the basics of unit testing : unit tests are made to prevent side effects in a unit.

Side effects are changes in a wanted behavior : for instance, you want to color a div in blue, and after some code editing, it colors red : this is a side effect.

A unit is the feature you're testing. In Angular, you can see which one it is with this :

describe('AppComponent'

Here, you're testing AppComponent.

Now that we settled this, let's go over what is wrong in your tests : you use real instances of your services. This means that you don't don't a single unit anymore : you're testing several units.

To correct that

You have to mock your services. You will check if your component is actually calling the service, and not if the service is calling your API (this will be checked by the unit test of the service itself).

In your test bed :

TestBed.configureTestingModule({declarations: [
    MainComponent,
    Menu,
    MessageViewer
  ],imports: [
    BrowserModule,
    BrowserAnimationsModule,
    routing,
    AngularFontAwesomeModule,
    FormsModule,
    HttpModule,
    ChartModule,
    Ng4LoadingSpinnerModule.forRoot(),
    NgbModule.forRoot()
  ],providers: [
    {
      provide:ReactiveService,
      useValue : {}
    },
    {
      provide:DataRequester,
      useValue: {}
    },
    { provide:APP_BASE_HREF, useValue :'/' }
  ]

Usually, components handle only the view : you don't really to mock them (although you should).

This allows you to remove the HttpModule, which isn't required in any test.

You can also remove your routing module, because Angular provides a mock of it already : RouterTestingModule.

Your test bed becomes this

TestBed.configureTestingModule({declarations: [
    MainComponent,
    Menu,
    MessageViewer
  ],imports: [
    BrowserModule,
    BrowserAnimationsModule,
    RouterTestingModule,
    AngularFontAwesomeModule,
    FormsModule,
    ChartModule,
    Ng4LoadingSpinnerModule.forRoot(),
    NgbModule.forRoot()
  ],providers: [
    {
      provide:ReactiveService,
      useValue : {}
    },
    {
      provide:DataRequester,
      useValue: {}
    },
    { provide:APP_BASE_HREF, useValue :'/' }
  ]

Now you have a proper test bed.

All you have to do left, is add in the useValue of your mocks, every service property used by your component with the correct signature.

For instance, imagine your app component has this :

ngOnInit() {
  this.dataRequester.requestWidth('URL').subscribe(res =>this.reactiveService.width = res);
}

Then your test bed becomes this :

TestBed.configureTestingModule({
  declarations: [
    MainComponent,
    Menu,
    MessageViewer
  ],
  imports: [
    BrowserModule,
    BrowserAnimationsModule,
    RouterTestingModule,
    AngularFontAwesomeModule,
    FormsModule,
    ChartModule,
    Ng4LoadingSpinnerModule.forRoot(),
    NgbModule.forRoot()
  ],
  providers: [
    {
      provide: ReactiveService,
      useValue : {
        width: 0
      }
    },
    {
      provide: DataRequester,
      useValue: {
        requestWidth: () => of(100)
      }
    },
    { provide: APP_BASE_HREF, useValue : '/' }
  ]

(Values of the mock aren't important, you'll change them on demand)

As you can see, because your requester service returns an Observable, you are forced to return one too. And because your reactive service stores the width, you have to declare a variable of type number.

Now, in your test, using the previous example, you will do this :

it(`should get width from requester and store it in reactive service`, fakeAsync(() => {
  spyOn(component['dataRequester'], 'requestWidth').and.callThrough();

  component.ngOnInit();
  tick();

  expect(component['dataRequester'].requestWidth).toHaveBeenCalledWith('URL');
  expect(component['reactiveService'].width).toEqual(100);
}));

You declare what you do (test driven), you spy on your service (to see if it has been called), then you call through (because our mock is already an Observable and returns 100).

Then, you call the method to test, and flush the async calls out (fakeAsync and tick are part of Angular, you can find them in the docs about testing).

Finally, you make your expectations.

And with that, you successfully tested your first method !

Post a Comment for "Jasmine/karma Testing Angular 5 Http Responses"