# Review No. 3 (Tests)

In 
web
Published 2024-11-16

This page contains questions to improve/test your Angular skills (Tests).

# TestBed, Jasmine, Karma

TestBed

  • Configures Angular's testing environment and provides tools for component/service testing.
  • Create and test components/services with TestBed.createComponent or TestBed.inject

Jasmine

  • Defines the test structure and provides assertions.
  • Defines describe and it blocks, and the usage of expect(value).toBe(expected).

Karma

  • Runs the tests in a browser and reports results.

# toBeTrue() vs toBeTruthy()

  • toBeTrue() : Requires the value to be true.
  • toBeTruthy() : Fails for: false, 0, '', null, undefined, NaN.

# What is NaN ?

NaN = Not-a-Number

console.log(0 / 0);       // NaN
console.log(Math.sqrt(-1)); // NaN
console.log(Number('abc'));     // NaN
console.log(parseInt('hello')); // NaN

# Testing a Guard

Guard example:

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { Observable, Subscription } from 'rxjs';

import { UserSessionService } from './100-services/user-session.service';

@Injectable({
  providedIn: 'root',
})
export class LoggedInGuard implements CanActivate {

  loggedIn: boolean = false;
  subscription1: Subscription;

  constructor(private next: Router,
              private userSessionService: UserSessionService) {

     this.subscription1 = this.userSessionService.loginCheck.subscribe( nextVal  => {

            if (nextVal == 'Y') {
               this.loggedIn = true;
            } else {
               this.loggedIn = false;
            }
     });
  }

  canActivate(): Observable<boolean> | Promise<boolean> | boolean {

      if (this.loggedIn) {
        return true; // logged in so return true
      }

      this.next.navigate(['/login']);
      return false;
  }

  ngOnDestroy() {
      this.subscription1.unsubscribe();
  }
}

Guard Test example:

import { TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';
import { LoggedInGuard } from './logged-in.guard';
import { UserSessionService } from './100-services/user-session.service';
import { BehaviorSubject} from 'rxjs';

describe('loggedInGuard', () => {
    let loggedInGuard: LoggedInGuard;
    let userSessionService: MockUserSessionService;
    let router: MockRouter;

  beforeEach(() => {
      TestBed.configureTestingModule({
        providers: [
          LoggedInGuard,
          { provide: UserSessionService, useClass: MockUserSessionService},
          { provide: Router, useClass: MockRouter },
        ],
      });

      loggedInGuard = TestBed.inject(LoggedInGuard);
      userSessionService = TestBed.inject(UserSessionService) as unknown as MockUserSessionService;
      router = TestBed.inject(Router) as unknown as MockRouter;

    });

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

  it('should allow navigation if user is authenticated', () => {
    const result = loggedInGuard.canActivate();
    expect(result).toBeTrue(); //navigation allowed
    expect(router.navigate).not.toHaveBeenCalled(); //no redirect
  });

  it('should not allow navigation if user is non authenticated', () => {
    userSessionService.loginCheck.next('N');
    const result = loggedInGuard.canActivate();
    expect(result).toBeFalse(); //navigation not allowed
    //we test the redirection to login
    expect(router.navigate).toHaveBeenCalledWith(['/login']);
  });
});

class MockUserSessionService {
  loginCheck = new BehaviorSubject<string>('Y');
}

class MockRouter {
  navigate = jasmine.createSpy('navigate');
}

# Testing a Component

Testing the component :

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { LogoutComponent } from './logout.component';
import { Router } from '@angular/router';

describe('LogoutComponent', () => {
  let component: LogoutComponent;
  let fixture: ComponentFixture<LogoutComponent>;
  let router: Router;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [LogoutComponent], // Declare the component being tested
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(LogoutComponent);
    component = fixture.componentInstance;
    fixture.detectChanges(); // Initialize component and run lifecycle hooks
    router = TestBed.inject(Router);

    // Spy on navigateByUrl
    spyOn(router, 'navigateByUrl');
    // Spy on sessionStorage.setItem (we will know what was done on it)
    spyOn(sessionStorage, 'setItem');
  });

  it('should create the component', () => {
    expect(component).toBeTruthy();
  });

  it('should navigate to "/contacte" page on button click', () => {
      component.route_contacte(); // Call the route_contacte() method
      expect(router.navigateByUrl).toHaveBeenCalledWith('/contacte');
      expect(router.navigateByUrl).toHaveBeenCalledTimes(1);
  });

  // test a method
  it('should add numbers correctly', () => {
    component.addNumbers(4, 3);
    expect(component.result).toBe(7); // Validate the method logic
  });

  // test a function
  it('should add numbers correctly', () => {
    const fctResult = component.addNumbersFct(1, 3);
    expect(fctResult).toBe(4); // Validate the method logic
  });

  it('should call sessionStorage.setItem with correct arguments', () => {
    component.logout();
    expect(sessionStorage.setItem).toHaveBeenCalledWith('userId', '0');
  });
});