简体   繁体   中英

How to mock TS class in unit tests

Consider the following code snippet:

import {Calendar} from '@fullcalendar/core';

...

ngAfterViewInit() {
  this.calendar = new Calendar(this.element.nativeElement, config);
  this.calendar.render();
}

I'm using fullcalendar plugin but it has nothing to do with my original question, I think, it might be any other dependency. So the plugin creates a calendar view. It is up to fullcalendar team to test the calendar behavior, while I'm responsible to test the calendar integration. So I need to test that the calendar has been initialized with correct config, therefore I want to omit real constructor and retrieve params. And also I don't want to create an instance of the calendar.

How to mock Calendar class in my tests?

It is good practice to wrap your library calls. First of all it's easier to test them and if the library interface will change you only have to change your code at a single location and keep your own interface in the rest of your code.

Therefore one solution for your problem would be to wrap the calendar creation in a factory service like:

@Injectable({providedIn:'root'})
export class FullcalendarFactoryService{

  public buildCalendar(element:HTMLElement,config:any){
    return new Calendar(element,config);
  }
}

In your component you have to inject your factory service and use it like:

constructor(public calenderFactory:FullcalendarFactoryService) {
}

ngAfterViewInit(): void {
    this.calendar = this.calenderFactory.buildCalendar(this.element.nativeElement,this.config);
    this.calendar.render();
}

And to test, you could simply mock your factory function like shown below:

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        YourComponent
      ],
      providers: [{
        provide: FullcalendarFactoryService,
        useClass: class {
          buildCalendar = jasmine.createSpy('buildCalendar').and.returnValue({
            render: () => true
          });
        }
      }
      ]
    }).compileComponents();

    calendarFactory = TestBed.get(FullcalendarFactoryService);

}));

it('should call factory method with element and config', () => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();

    expect(calendarFactory.buildCalendar).toHaveBeenCalledWith(fixture.componentInstance.element.nativeElement, fixture.componentInstance.config);
  });

UPDATE:

To test if the services buildCalendar function returns an instance of Calendar you would test your service like shown below:

import {FullcalendarFactoryService} from './fullcalendar-factory.service';
import {Calendar} from '@fullcalendar/core';
import dayGridPlugin from '@fullcalendar/daygrid'

describe('calendar factory service', () => {
  let factory:FullcalendarFactoryService;

  beforeEach(() => {
    factory = new FullcalendarFactoryService();
  })

  it('should return calender instance',() => {
    expect(factory.buildCalendar(document.createElement('test'),{plugins:[dayGridPlugin]})).toEqual(jasmine.any(Calendar))
  })
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM