Home > Back-end >  How to mock observable variable in unit test?
How to mock observable variable in unit test?

Time:11-01

I am learning how to write unit test. Here is part of the code from the course.

  #spec.ts
  ...
  beforeEach(waitForAsync(() => {
    const coursesServiceSpy = jasmine.createSpyObj("CoursesService", [
      "findAllCourses",
    ]);

    TestBed.configureTestingModule({
      imports: [CoursesModule, NoopAnimationsModule, HttpClientTestingModule],
      providers: [{ provide: CoursesService, useValue: coursesServiceSpy }],
    })
      .compileComponents()
      .then(() => {
        fixture = TestBed.createComponent(HomeComponent);
        component = fixture.componentInstance;
        el = fixture.debugElement;
        coursesService = TestBed.inject(CoursesService);
      });
  }));
  ...
  it("should display data from service", () => {
    coursesService.findAllCourses.and.returnValue(of(fake data));

    fixture.detectChanges();

    // the test code
  });
  #component.ts
  reloadCourses() {
    const courses$ = this.coursesService.findAllCourses();
  }

The problem is I am using component-store to manage the state which does not return anything after I call the effect.

  # my component
  
  constructor(
    private store: StoreService
  ) {}

  ...

  viewModel$ = this.store.viewModel$;

  // how I call effect to refresh data
  this.store.initCourses();

I just directly display data from component-store in my template.

  # my template
  <ng-container *ngIf="viewModel$ | async as viewModel">

I don't know how to fake component-store response and let my component know something has changed. What I am trying to do is to assign value to the viewModel$ which is an Observable. Does anyone know how to do that or have any other solution?

Sorry for my bad explanation, if you need more information plz let me know. Big thanks!

CodePudding user response:

Assuming StoreService is just a service, you need to mock it similar to how CoursesService is mocked in the class you took.

You should try this:

let storeServicesSpy: jasmine.SpyObj<StoreService>;
// Mock this behavior subject accordingly to how the data should be.
// Empty object is shown here.
const mockViewModel$ = new BehaviorSubject<any>({});

beforeEach(waitForAsync(() => {
  // Create a spy object
  // The first string argument is optional and used for debugging purposes.
  // The second array argument is an array of strings of public methods you would
  // like to be mocked.
  // The third object argument are instance variables you would like to mock
  storeServiceSpy = jasmine.createSpyObj<StoreService>('StoreService', ['initCourses'], { viewModel$: mockViewModel$ });
}));

  TestBed.configureTestingModule({
     ...
     // Assign our mock for when the component asks for the StoreService
     providers: [{ provide: StoreService, useValue: storeServiceSpy }],
     ...
  }).compileComponents();

Then you can do mockViewModel$.next({/* new stuff here */ }); to make viewModel$ emit with new data.

Learn more about testing here: https://testing-angular.com/testing-components-depending-on-services/#testing-components-depending-on-services.

  • Related