Before start testing method in components we initialize few things as below

  1. beforeEach method helps in initializing the values needed for testing
  2. describe are similar to JUnit class and it are similar to junit class methods which tests particular method in src code
  3. TestBed is needed while testing both component and html.You need to configure the TestBed before each test, adding any components, modules and services you need for the test. It’s just like configuring an regular @NgModule from scratch, but you just add what you need.
    import { async, TestBed } from '@angular/core/testing';
    
    beforeEach(async(() => {
      TestBed.configureTestingModule({
        declarations: [ AppComponent ],
        providers: [],
        imports: []
      })
      .compileComponents();
    }));
    
  4. Using TestBed.createComponent create component instance returns componentFixture Interface. componentFixture is a wrapper over component with additional methods needed for testing. fixture.componentInstance returns instance of component.
    beforeEach(() => {
    .
    //Returns Component Fixture
    let fixture = TestBed.createComponent(EmployeeComponent);
    
    //Get Instance of Component
    component = fixture.componentInstance;
    .
    .
    });
    
  5. fixture.detectChanges() would allow to check for change in DOM elements before expect or whenever it is referenced for value
  6. DebugElement, an interface which wraps native element instead of HTML element in idle scenario
  7. schemas:[NO_ERRORS_SCHEMAS] lets angular testing module to ignore unknown tags in html during testing. Suppose you have used router tags in html
    and angular could not recognize the router tag since routerTestingModule is not imported you can use this tag as below to avoid errors.

    beforeEach(() => {
         TestBed.configureTestingModule({
          declarations: [ EmployeeComponent]
          schemas:[NO_ERRORS_SCHEMAS]
         }).compileComponents();
    
        //Testbed to create fixture for component
        fixture = TestBed.createComponent(EmployeeComponent);
    
        //Using fixture to create component and service 
        component = fixture.componentInstance;
        empService = TestBed.inject(EmpserviceService);    
     });
    
    

Simple testing with matchers

  1. toBeTruthy tells the component is created successfully
  2. toBe checks for equality of value
  3. toEqual method does object comparison
  4. toContain method checks whether value is available in array
fit('Simple component testing', ()=>{
  //Checks whether component has been created
  expect(component).toBeTruthy();

  //Checks whether value returned by function are same
  expect(component.getGender(true)).toBe('Male');

  //Checks objects are equal and array contain that element
  var Names = ['Name1', 'Name2', 'Name3'];
  expect(Names).toEqual(['Name1', 'Name2', 'Name3']);
  expect(Names).toContain('Name2');
});

Checking DOM Elements

  1. nativeElement – provides the access to DOM element.This is same as Javascript DOM Element we use for DOM Manipulation.Using nativeElement you can
    access all the API methods provided vy Javascript for DOM manipulation like QuerySelector
  2. fixture.detectChanges() – updates the DOM Element after setting the value from testing component. This is similar to refresh once the component value is set the html should be refreshed.
  3. debugElement– debugElement is similar to nativeElement which has wrapper with some additional methods.It has query, queryAll, queryNode methods
    debugElement inturn calls nativeElement to get the values.fixture.debugElement returns the root debugElement from which we query for the nativeElement.

    .
    .
    fixture.debugElement.query(By.css('a')).nativeElement.textContent)
    fixture.debugElement.query(By.css('#ContactUsId')).nativeElement.textContent)
    fixture.debugElement.query(By.css('.ContactUsId')).nativeElement.textContent)
    .
    .
    .
    
  4. The below statement end up the same anchor tag
    .
    
    fixture.debugElement.query(By.css('a')).nativeElement.textContent)
    fixture.nativeElement.querySelector('a').textContent
    .
    

Using spyOn with different Options

  1. Using spyon with callThrough would make the call to actual service
  2. spyOn with returnValue returns the value specified without making call to real function
  3. spyOn withArgs does the same but takes arguments incase there is somelogic needs to change when using along callthrough
  4. spyOn callFake would run anonymous function withits own logic rather than calling original function to act on logic
spyOn(empService, 'authenticateEmp').withArgs('Admin').and.callThrough();

spyOn(empService, 'authenticateEmp').and.returnValue(true);

spyOn(empService, 'authenticateEmp').withArgs('Admin').and.returnValue(true);

spyOn(empService, 'authenticateEmp').and.callFake(function(name, args){
   return true;
});

What is difference between returnValue and callFake?
If we just want a return value when a service method is called then we can use any of and.callFake or and.returnValue. Lets take below example

component file:

@Component(...)
export class DependencyComponent {
  constructor(private service: RandomService){....}
  .....
  sampleMethod() {
   return this.service.randomMethod();
  }
  .....
}

unit test case for above component:

it('test callFake vs returnValue', () => {
  let randomService= new RandomService();
  let component = new DependencyComponent(randomService);
  spyOn(randomService, 'randomMethod').and.callFake(() => 4)
  expect(component.sampleMethod()).toBe(4)
  spyOn(randomService, 'randomMethod').and.returnValue(10);
  expect(component.sampleMethod()).toBe(10)
})

in above case both the ways are correct.

Suppose we are passing a parameter to service method to perform its logic then in that case we have to use and.callFake((param) => {…}). Here param parameter will be the argument passed to the spied method.

component file:

@Component(...)
export class DependencyComponent {
  constructor(private service: RandomService){....}
  .....
  sampleMethod(val) {
  return this.service.randomMethod(val);
  }
  .....
}

unit test case for above component:

it('test callFake vs returnValue', () => {
  let randomService= new RandomService();
  let component = new DependencyComponent(randomService);
  spyOn(randomService, 'randomMethod').and.callFake((param) => param+4)
  expect(component.sampleMethod(4)).toBe(8);
  expect(component.sampleMethod(12)).toBe(16)
})

when component.sampleMethod(4) is executed it will call this.service.randomMethod(4). As randomMethod() is being spied using and.callFake therefore 4 will be passed as argument of call back function of and.callFake.

One obvious question is can i use returnValue instead of callFake? You can still use but you need to define multiple returnValue methods based on total arguments needs to be tested.