Search code examples
angularunit-testingmockingjasmine

I got and error: TypeError: this.todos$.pipe is not a function when create an jasmine.SpyObj as a fack service


Here is a simple todo list component: enter image description here Here is my test case:

fdescribe('TodoListComponent With FackService', () => {
  let fixture: ComponentFixture<TodolistComponent>;
  let component: TodolistComponent;
  let fackTodoService: jasmine.SpyObj<TodoService>;

  beforeEach(async () => {
    fackTodoService = jasmine.createSpyObj<TodoService>('TodoService', {
      todolist$: of(mocktodos),
      currentTodoList: [],
      getTodos: of(mocktodos),
      deleteTodo: undefined,
      addTodo: of(mocktodos[0]),
    });

    await TestBed.configureTestingModule({
      declarations: [TodolistComponent],
      providers: [
        {
          provide: TodoService,
          useValue: fackTodoService,
        },
      ],
    }).compileComponents();

    fixture = TestBed.createComponent(TodolistComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should do something', () => {});
});

after I trigger this test, it report an error as follow: enter image description here

It seems the mocked service cannot init these data when creating the fixture... Can I get some help for it, I cannot find effect solution for it... Thank you gays~!


Solution

  • The 2nd argument here in jasmine.createSpyObj is for methods, the third argument is for instance variables.

    So imagine this is what you want to mock:

    export class AnyThing {
      instanceVariable = 2;
      
      method() {
        return 3;
      }
    }
    

    It would be:

    let mockAnything: jasmine.SpyObj<Anything>;
    ...
    mockAnything = jasmine.createSpyObj<Anything>('Anything', { method: jasmine.createSpy() }, { instanceVariable: 2 });
    

    It can be shortened to:

    mockAnything = jasmine.createSpyObj<Anything>('Anything', ['method'], { instanceVariable: 2 });
    

    All that to say, change this line:

    fackTodoService = jasmine.createSpyObj<TodoService>('TodoService', {
          todolist$: of(mocktodos),
          currentTodoList: [],
          getTodos: of(mocktodos),
          deleteTodo: undefined,
          addTodo: of(mocktodos[0]),
        });
    

    To this:

    // Add an extra object here for the mock methods after 'TodoService'
    fackTodoService = jasmine.createSpyObj<TodoService>('TodoService', {}, {
          todolist$: of(mocktodos),
          currentTodoList: [],
          getTodos: of(mocktodos),
          deleteTodo: undefined,
          addTodo: of(mocktodos[0]),
        });
    

    todolist$ is an instance variable and not a method and that's why we need to use the 3rd argument.

    Here is some documentation on what you would like: jasmine.createSpyObj with properties