0
votes

I am writing Unit Test cases for a function in Angular7 template . It is a login component and the login function has router.navigate inside the http request, to route to the dashboard on correct login. But I am getting error -

Error: Expected spy navigate to have been called with [ [ '/ProjectData/MasterSequence' ] ] but it was never called. at stack (http://localhost:9876/absolute/home/hp/Downloads/ICICI/ICICI_UI/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:2455:17) at buildExpectationResult (http://localhost:9876/absolute/home/hp/Downloads/ICICI/ICICI_UI/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:2425:14) at Spec.expectationResultFactory (http://localhost:9876/absolute/home/hp/Downloads/ICICI/ICICI_UI/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:901:18) at Spec.addExpectationResult (http://localhost:9876/absolute/home/hp/Downloads/ICICI/ICICI_UI/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:524:34) at Expectation.addExpectationResult (http://localhost:9876/absolute/home/hp/Downloads/ICICI/ICICI_UI/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:845:21) at Expectation.toHaveBeenCalledWith (http://localhost:9876/absolute/home/hp/Downloads/ICICI/ICICI_UI/node_modules/jasmine-core/lib/jasmine-core/jasmine.js?0b1eaf7a13cae32191eadea482cfc96ae41fc22b:2369:12) at UserContext. (http://localhost:9876/src/app/authentication/login2/login2.component.spec.ts?:208:38)

app.component.html

loginData(value) {
    this.username = value.username;
    this.password = value.password;
    this.dataObj = {
      'username': this.username,
      'password': this.password
    }
    this.loginService.login(this.dataObj).then((data) => {
      console.log("data", data);
      this.response = data;
      console.log("message", this.response.message);
      if (this.response.message == "Login Successful.") {
        this.router.navigate(['/ProjectData/MasterSequence'])
      }
      else {
        this.toastr.error("UserName or Password is incorrect");
      }
    })

app.component.spec.ts

describe('Login2Component', () => {
    let comp: Login2Component;
    let fixture: ComponentFixture<Login2Component>;
    let de: DebugElement;
    let el: HTMLElement;
    beforeEach(async(() => {
        mockRouter = { navigate: jasmine.createSpy('navigate') };
        TestBed.configureTestingModule({
            declarations: [Login2Component, MasterSequenceComponent],

            imports: [
                BrowserModule,
                FormsModule,
                RouterModule,
                ReactiveFormsModule,
                RouterTestingModule,
                HttpClientModule,

                [
                    RouterTestingModule.withRoutes([
                        { path: '/ProjectData/MasterSequence', 
                          component: MasterSequenceComponent }
                    ])
                ]
            ],
            schemas: [CUSTOM_ELEMENTS_SCHEMA],

            providers: [{ provide: ToastrService, useValue: ToastrService }, Login2Service]
        })
            .compileComponents()
            .then(() => {
                //Login2Component
                fixture = TestBed.createComponent(Login2Component);

                comp = fixture.componentInstance;

                de = fixture.debugElement.query(By.css('form'));
                el = de.nativeElement;
            });
    }));

    it('should redirect the user to "login form" component when button is clicked', () => {
        const router = TestBed.get(Router);
        const spy = spyOn(router, 'navigate');
        fixture.detectChanges();

        const button = fixture.debugElement.query(By.css('form[id=loginform]'));

        button.triggerEventHandler('click', null);

        let userN = comp.addLoginData.controls['username'];
        userN.setValue('pallavi');
        let pass = comp.addLoginData.controls['password'];
        pass.setValue(1234);

        let value = {
            username: userN,
            password: pass
        };

        comp.loginData(value);

        expect(spy).toHaveBeenCalledWith(['/ProjectData/MasterSequence']);
    });
});

Reference -

Angular 2/4/6/7 - Unit Testing with Router

Further Error On using this code -

it('should redirect the user to "login form" component when button is clicked', () => {
    let value = {
         username: 'user123',
         password: 'pwd'
     };
     comp.username = '';
     comp.password = '';

     spyOn(comp.LoginService,'login').and.callThrough();

    comp.loginData(value);
     expect(comp.username).toBe('user123');
     expect(comp.password).toBe('pwd');
     expect(comp.LoginService.login).toHaveBeenCalledWith(value)

//expect(RouterMock.navigate).toHaveBeenCalledWith(['/ProjectData/MasterSe//quence']);
    });

ERROR: 'Unhandled Promise rejection:', HttpErrorResponse{headers: HttpHeaders{normalizedNames: Map{}, lazyUpdate: null, he aders: Map{}}, status: 0, statusText: 'Unknown Error', url: 'http://192.168.5.128:3002/api/user/login', ok: false, name: 'HttpErrorResponse', message: 'Http failure response for http://192.168.5.128:3002/api/user/login: 0 Unknown Error', error: ProgressEvent{isTrusted: true}}, '; Zone:', 'ProxyZone', '; Task:', 'Promise.then', '; Value:', HttpErrorResponse{headers: HttpHeaders{normalizedNames: Map{}, lazyUpdate: null, headers: Map{}}, status: 0, statusText: 'Unknown Error', url: 'http://192.168.5.128:3002/api/user/login', ok: false, name: 'HttpErrorResponse', message: 'Http failure response for http://192.168.5.128:3002/api/user/login: 0 Unknown Error', error: ProgressEvent{isTrusted: true}}, undefined

1
Could you please provide the component and template associated with that test otherwise it's a bit hard trying to figure out why your test doesn't work as expected. - Erbsenkoenig
OK. Thanks. I will do that - Techdive
Well without seeing the html I can't really understand why you are triggering a button click. You might need to be waiting for an async job to finish, but that's just guessing. But one problem is definitely that you need to wait until the promise returned from the loginService actually resolves. You can use fakeAsync in your test block it and a tick() before you expect that your spy has been called. But whether this alone will fix your test, I cannot tell having not seen the template. Besides what is the reason you are using the real Login2Service? - Erbsenkoenig
@Techdive Any update on my suggestion as provided in the chat ? - Shashank Vivek

1 Answers

1
votes

I cant find code of loginService.login() but I am sure that it must be making some API call, so its a good practice to create a stub. something like:

export class LoginServiceStub{
  login(obj){
     return new Promise((resolve, reject) => {
         resolve("whatever_data_is_expected");
     });
   }
}

In spec file:

describe('Login2Component', () => {
  let RouterMock = {
    navigate: jasmine.createSpy('navigate')
  };

 beforeEach(async(() => {
        mockRouter  = { navigate: jasmine.createSpy('navigate') };
        TestBed.configureTestingModule({
        decleration: [ .... ],
        providers: [
             {provide: ToastrService, useValue: ToastrService }, // I am not sure why you have done "useValue" with same Service
             {provide: Login2Service, useClass: Login2ServiceStub },
             {provide: Router, useValue: RouterMock  }
             ], 
      // ... and other deceleration of Test Bed
  )};

})

and then in it block:

it('should redirect the user to "login form" component when button is clicked', () => {
       let value = {
            username: 'user123',
            password: 'pwd';
        };
        comp.username = '';
        comp.password = '';
        spyOn(comp.loginService,'login').and.callThrough();
        comp.loginData(value);
        expect(comp.username).toBe('user123');
        expect(comp.password).toBe('pwd');
        expect(comp.loginService.login).toHaveBeenCalledWith(value)
        expect(RouterMock.navigate).toHaveBeenCalledWith(['/ProjectData/MasterSequence']);
    });
});

I would also suggest you to read this collection of articles specifically written for Unit testing in Angular. You can find several links which will cover almost all basic testing scenarios.