"Expected a spy, but got Function" Error for Angular 9 Testing

5k Views Asked by At

I've working in an Angular 9 project.

I'm getting a "Expected a spy, but got Function" Error on a test, but only sometimes. Sometimes, all my test pass, other times this one test keeps giving this error.

I'm really confused because it's a pretty simple test, I have other tests in this project that are are essentially the same and never fail. I've also never seen this test fail if I'm focusing on it alone.

Here's my test (that gives an error sometimes):

describe("uploadTemplate", () => {
    beforeEach(() => {
      fixture = TestBed.createComponent(UploadTemplateComponent);
      const mockFile = new File(["mockFile"], "mockPath");
      mockFormData.append("document", mockFile);
      fixture.detectChanges();
    });

    it("should show showMessage on error", () => {
      let mockError;
      const uploadTemplateSpy = spyOn(
        mockTemplateService,
        "uploadTemplate"
      ).and.callThrough();
      uploadTemplateSpy.and.returnValue(throwError("mock error"));
      mockTemplateService.uploadTemplate(mockFormData).subscribe(
        () => {},
        err => {
          mockError = err;
        }
      );
      expect(mockError).not.toBeNull();
      spyOn(mockSnackBarService, "showMessage");
      component.uploadTemplate();
      expect(component.isLoading).toBeFalsy();
      expect(mockSnackBarService.showMessage).toHaveBeenCalled();
    });
  });

As you see, I have mocks for services. Here are my mocks:

export class MockTemplateService {
  uploadTemplate(formData: FormData): Observable<Template> {
    return of(MockTemplates.templates[0]);
  }
}
export class MockSnackBarService {
  showMessage() {}
}

and my testBeds:

beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [MatSnackBarModule, BrowserAnimationsModule],
      providers: [
        { provide: TemplateService, useClass: MockTemplateService },
        {
          provide: SnackBarService,
          useClass: MockSnackBarService
        },
        { provide: Router, useClass: MockRouter }
      ],
      schemas: [CUSTOM_ELEMENTS_SCHEMA],
      declarations: [UploadTemplateComponent]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(UploadTemplateComponent);
    component = fixture.componentInstance;
    mockSnackBarService = TestBed.inject(SnackBarService);
    mockTemplateService = TestBed.inject(TemplateService);
    fixture.detectChanges();
  });

I'm really struggling with finding the cause of this, especially since it never fails when I focus on it. Only sometimes fails when I run all tests, but also if I ignore only the above test, the rest of the tests always seem to pass.

1

There are 1 best solutions below

1
João Vitor Romero da Cruz On

The spyOn method returns a Spy object that toHaveBeenCalled is expecting, so your code should look something like this:

  const spyShowMessage = spyOn(mockSnackBarService, "showMessage");
  component.uploadTemplate();
  expect(component.isLoading).toBeFalsy();
  expect(spyShowMessage).toHaveBeenCalled();