You need to set up the spy before the method is called. Jasmine spys wrap function to determine when they are called and what they get called with. The method must be wrapped before the spied on method is called in order to capture the information. Try changing your test to match the following:

it('should foobar', () => {
    spyOn(component, 'bar');
    component.foo();
    expect(component.bar).toHaveBeenCalled();
})
Answer from Teddy Sterne on Stack Overflow
🌐
DigitalOcean
digitalocean.com › community › tutorials › angular-testing-with-spies
How To Use Spies in Angular Testing | DigitalOcean
June 21, 2021 - Jasmine spies are used to track or stub functions or methods. Spies are a way to check if a function was called or to provide a custom return value. We can use spies to test components that depend on service and avoid actually calling the service’s methods to get a value.
🌐
GitHub
github.com › jestjs › jest › issues › 9102
How To Use spyOn on a function inside of functional component · Issue #9102 · jestjs/jest
October 26, 2019 - here's the test I can't reach the updateDisplay directly so I get the Keypad props (as I am passing it to that component) and mock it from there but it seems to be the wrong way, anyone has a way to do it better or suggest a better way to mock that function. describe('mounted <Calculator />', () => { let wrapper; let keypad; beforeEach(() => { wrapper = mount(<Calculator/>); keypad = wrapper.find(Keypad); }); it('calls updateDisplay when a number key is clicked', () => { const spy = jest.spyOn(wrapper.find(Keypad).props(), 'updateDisplay'); wrapper.update(); expect(spy).toHaveBeenCalledTimes(0); wrapper.find('.number-key').first().simulate('click'); expect(spy).toHaveBeenCalledTimes(1); }); });
Author   MuhmdRaouf
Discussions

angular - testing that a component method calls another method - Stack Overflow
describe('AppComponent', () => { let component: AppComponent; beforeEach(() => { component = new AppComponent(); } it('should foobar', () => { component.foo(); spyOn(component, 'bar'); expect(component.bar).toHaveBeenCalled(); }) } More on stackoverflow.com
🌐 stackoverflow.com
reactjs - Spying on React functional component method with jest and enzyme; Cannot spyOn on a primitive value - Stack Overflow
I am trying to test a React component and make sure that when its button gets clicked, the correct method gets invoked. However, when I try to run my test and try to spy on that method, I get the following message: Error: Cannot spyOn on a primitive value; undefined given More on stackoverflow.com
🌐 stackoverflow.com
How to spyOn method inside prop passed down to a component using Jest?
Background: My test Framework is Jest and Enzyme. I have a Component called Lazyload that is coupled to a LazyloadProvider using React.ContextAPI. I would like to write a test that guarantees that... More on stackoverflow.com
🌐 stackoverflow.com
spy on component method failed
// Sample.js export default class Sample extends Component { sampleMethod = () => { console.log("sample"); } render() { return (Sample); } } ... // Sample.test.js import { mount } from "enzyme"; import expect from "expect"; import Sample from "./Sample.js"; const wrapper = mount(); const spy = expect.spyOn... More on github.com
🌐 github.com
51
May 6, 2016
🌐
GitHub
github.com › cypress-io › cypress › discussions › 26081
[Documentation - Angular] What is the best practice for spying on component methods? · cypress-io/cypress · Discussion #26081
it('should call myMethod when the button is clicked', () => { spyOn(component, 'myMethod'); const button = fixture.debugElement.query(By.css('button')).nativeElement; button.click(); expect(component.myMethod).toHaveBeenCalled(); }); An @Input change is only one way to trigger 'side effects'.
Author   cypress-io
🌐
Gitbook
duncanhunter.gitbook.io › testing-angular › test-the-component-logic-using-spyon
9. Test the Component logic using SpyOn | Testing Angular
SpyOn is a Jasmine feature that allows dynamically intercepting the calls to a function and change its result. This example shows how spyOn works, even if we are still mocking up our service.
🌐
Meticulous
meticulous.ai › blog › how-to-use-jest-spyon
How to use Jest spyOn with React.js and Fetch
This is the main difference between SpyOn and Mock module/function. A spy may or may not mock the implementation or return value and just observe the method call and its parameters. On the other hand, a mock will always mock the implementation or return value in addition to listening to the calls and parameters passed for the mocked function. Next, render the App component and do a destructuring assignment to a variable called container.
Top answer
1 of 4
17

The error means, the function sampleMethod you defined inside the functional component SampleComponent is not defined in SampleComponent.prototype. So SampleComponent.prototype.sampleMethod is undefined, jest can't spy on a undefined value.

So the correct way to test sampleMethod event handler is like this:

index.spec.tsx:

import React from 'react';
import SampleComponent from './';
import { shallow } from 'enzyme';

describe('SampleComponent', () => {
  test('should handle click correctly', () => {
    const logSpy = jest.spyOn(console, 'log');
    const wrapper = shallow(<SampleComponent></SampleComponent>);
    const button = wrapper.find('button');
    expect(button.text()).toBe('Click Me');
    button.simulate('click');
    expect(logSpy).toBeCalledWith('hello world');
  });
});

We can spy on console.log, to assert it is to be called or not.

Unit test result with 100% coverage:

 PASS  src/react-enzyme-examples/02-react-hooks/index.spec.tsx
  SampleComponent
    ✓ should handle click correctly (19ms)

  console.log node_modules/jest-mock/build/index.js:860
    hello world

-----------|----------|----------|----------|----------|-------------------|
File       |  % Stmts | % Branch |  % Funcs |  % Lines | Uncovered Line #s |
-----------|----------|----------|----------|----------|-------------------|
All files  |      100 |      100 |      100 |      100 |                   |
 index.tsx |      100 |      100 |      100 |      100 |                   |
-----------|----------|----------|----------|----------|-------------------|
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        5.036s

Dependencies version:

"react": "^16.11.0",
"enzyme": "^3.10.0",
"enzyme-adapter-react-16": "^1.15.1",
"jest": "^24.9.0",
"jest-environment-enzyme": "^7.1.1",
"jest-enzyme": "^7.1.1",
2 of 4
9

sample.js

import * as React from 'react';

export let util = {sampleMethod: null };

const SampleComponent = () => {
  util.sampleMethod = () => {
    console.log('hello world');
  };

  return <button onClick={sampleMethod} type="button">Click Me</button>;
};

export default SampleComponent;

sample.test.js

import { shallow } from 'enzyme';
import SampleComponent, {util} from './sample';

test('testing spy', () => {
  const spy = jest.spyOn( util, 'sampleMethod' );
  const wrapper = shallow(<SampleComponent />);
  wrapper.find('button').simulate('click');
  expect(spy).toHaveBeenCalled(1);
});

I know I'm late to answer but I think this would help some other developers also

Find elsewhere
🌐
CodeCraft
codecraft.tv › courses › angular › unit-testing › mocks-and-spies
Testing with Mocks & Spies • Angular - CodeCraft
import {LoginComponent} from './login.component'; import {AuthService} from "./auth.service"; describe('Component: Login', () => { let component: LoginComponent; let service: AuthService; let spy: any; beforeEach(() => { (1) service = new AuthService(); component = new LoginComponent(service); }); afterEach(() => { (2) service = null; component = null; }); it('needsLogin returns true when the user has not been authenticated', () => { spy = spyOn(service, 'isAuthenticated').and.returnValue(false); (3) expect(component.needsLogin()).toBeTruthy(); expect(service.isAuthenticated).toHaveBeenCalled(); (4) }); it('needsLogin returns false when the user has been authenticated', () => { spy = spyOn(service, 'isAuthenticated').and.returnValue(true); expect(component.needsLogin()).toBeFalsy(); expect(service.isAuthenticated).toHaveBeenCalled(); }); });
🌐
Medium
medium.com › @patryk.nather › testing-local-functions-in-react-components-with-jest-55fe50a9032b
Testing Local Functions in React Components with Jest
August 10, 2023 - In Jest, jest.spyOn() is a powerful tool that allows us to monitor the calls to a specific function, checking how many times it was called, what arguments it was called with, and more.
🌐
GitHub
github.com › enzymejs › enzyme › issues › 365
spy on component method failed · Issue #365 · enzymejs/enzyme
May 6, 2016 - // Sample.js export default class Sample extends Component { sampleMethod = () => { console.log("sample"); } render() { return (<button onClick={this.sampleMethod}>Sample</button>); } } ... // Sample.test.js import { mount } from "enzyme"; import expect from "expect"; import Sample from "./Sample.js"; const wrapper = mount(<Sample />); const spy = expect.spyOn(wrapper.instance(), "sampleMethod"); expect(spy).toNotHaveBeenCalled(); wrapper.find("#sample").simulate("click"); expect(spy).toHaveBeenCalled();
Author   tpai
🌐
Codehandbook
codehandbook.org › home › mock service using spy on angular unit testing karma jasmine
Mock Service Using SpyOn | Angular Unit Testing | Karma | Jasmine -
June 21, 2021 - How to use spyOn in Angular Unit Testing? How to mock a service in Angular Unit Testing ? Here is your component code that you want to unit test. import { Component } from '@angular/core'; import { DataServiceService } from '../app/data-service.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { title = 'angu-unit-test'; constructor(private dataService : DataServiceService){} count = 0; config = {}; data; calculate(a,b){ this.count = (a * b) + 100 return this.
Top answer
1 of 4
102

You were almost done without any changes besides how you spyOn. When you use the spy, you have two options: spyOn the App.prototype, or component component.instance().


const spy = jest.spyOn(Class.prototype, "method")

The order of attaching the spy on the class prototype and rendering (shallow rendering) your instance is important.

const spy = jest.spyOn(App.prototype, "myClickFn");
const instance = shallow(<App />);

The App.prototype bit on the first line there are what you needed to make things work. A JavaScript class doesn't have any of its methods until you instantiate it with new MyClass(), or you dip into the MyClass.prototype. For your particular question, you just needed to spy on the App.prototype method myClickFn.


jest.spyOn(component.instance(), "method")

const component = shallow(<App />);
const spy = jest.spyOn(component.instance(), "myClickFn");

This method requires a shallow/render/mount instance of a React.Component to be available. Essentially spyOn is just looking for something to hijack and shove into a jest.fn(). It could be:

A plain object:

const obj = {a: x => (true)};
const spy = jest.spyOn(obj, "a");

A class:

class Foo {
    bar() {}
}

const nope = jest.spyOn(Foo, "bar");
// THROWS ERROR. Foo has no "bar" method.
// Only an instance of Foo has "bar".
const fooSpy = jest.spyOn(Foo.prototype, "bar");
// Any call to "bar" will trigger this spy; prototype or instance

const fooInstance = new Foo();
const fooInstanceSpy = jest.spyOn(fooInstance, "bar");
// Any call fooInstance makes to "bar" will trigger this spy.

Or a React.Component instance:

const component = shallow(<App />);
/*
component.instance()
-> {myClickFn: f(), render: f(), ...etc}
*/
const spy = jest.spyOn(component.instance(), "myClickFn");

Or a React.Component.prototype:

/*
App.prototype
-> {myClickFn: f(), render: f(), ...etc}
*/
const spy = jest.spyOn(App.prototype, "myClickFn");
// Any call to "myClickFn" from any instance of App will trigger this spy.

I've used and seen both methods. When I have a beforeEach() or beforeAll() block, I might go with the first approach. If I just need a quick spy, I'll use the second. Just mind the order of attaching the spy.


EDIT: If you want to check the side effects of your myClickFn you can just invoke it in a separate test.

const app = shallow(<App />);
app.instance().myClickFn()
/*
Now assert your function does what it is supposed to do...
eg.
expect(app.state("foo")).toEqual("bar");
*/

EDIT: Here is an example of using a functional component. Keep in mind that any methods scoped within your functional component are not available for spying. You would be spying on function props passed into your functional component and testing the invocation of those. This example explores the use of jest.fn() as opposed to jest.spyOn, both of which share the mock function API. While it does not answer the original question, it still provides insight on other techniques that could suit cases indirectly related to the question.

function Component({ myClickFn, items }) {
   const handleClick = (id) => {
       return () => myClickFn(id);
   };
   return (<>
       {items.map(({id, name}) => (
           <div key={id} onClick={handleClick(id)}>{name}</div>
       ))}
   </>);
}

const props = { myClickFn: jest.fn(), items: [/*...{id, name}*/] };
const component = render(<Component {...props} />);
// Do stuff to fire a click event
expect(props.myClickFn).toHaveBeenCalledWith(/*whatever*/);

If a functional component is niladic (no props or arguments) then you can use Jest to spy on any effects you expect from the click method:

import { myAction } from 'src/myActions'
function MyComponent() {
    const dispatch = useDispatch()
    const handleClick = (e) => dispatch(myAction('foobar'))
    return <button onClick={handleClick}>do it</button>
}

// Testing:
const { myAction } = require('src/myActions') // Grab effect actions or whatever file handles the effects.
jest.mock('src/myActions') // Mock the import

// Do the click
expect(myAction).toHaveBeenCalledWith('foobar')
2 of 4
31

You're almost there. Although I agree with @Alex Young answer about using props for that, you simply need a reference to the instance before trying to spy on the method.

describe('my sweet test', () => {
 it('clicks it', () => {
    const app = shallow(<App />)
    const instance = app.instance()
    const spy = jest.spyOn(instance, 'myClickFunc')

    instance.forceUpdate();    

    const p = app.find('.App-intro')
    p.simulate('click')
    expect(spy).toHaveBeenCalled()
 })
})

Docs: http://airbnb.io/enzyme/docs/api/ShallowWrapper/instance.html

Top answer
1 of 3
5

Your validateBeforeSave function is declared within SomeComponent making it a closed/private scope function not accessible outside. You can pass that function as a prop and you can then create spy and pass it as a prop value in your test and test for if the prop function passed (spy) was called or not

So you would modify your function somewhat like this:

// some validator function
function validateBeforeSave(){
  ...
}

// Some function Component

const SomeComponent = (props: IMyComponentProps) => {
  const { classes, validateBeforeSave } = props;

  // Component has state
  const [count, setCount] = useState(0);


  function handleClick() {
  validateBeforeSave();
  .
  .
  .
  }

  return (
   <div>
      <Button>
      className="saveBtn"
      onClick={handleClick}
      </Button>
    </div>
  );

};

And In your Unit test, something like this:

  // Unit test
  describe('SomeComponent' () => {
  it('validates model on button click', () => {
      const validateSpy = jest.fn();
      const wrapper = mount(
        <MuiThemeProvider theme={theme}>
          <SomeComponent validateSpy={validateSpy}/>
        </MuiThemeProvider>,
      );
      const instance = wrapper.instance();
      wrapper
        .find('.saveBtn')
        .at(0)
        .simulate('click');
      expect(validateSpy).toHaveBeenCalledTimes(1);
    });
  }
2 of 3
-1

I had similar problem mocking callback prop method with React 16.x.x, enzyme instance method returns null, what you can do is pass directly jest.fn() as a prop.

EXAMPLE:

  it('should invoke callback with proper data upon checkbox click', () => {
    const spyCheckboxClick = jest.fn((id, item) => ({
      id,
      item,
    }))
    const component: any = enzyme.mount(
      <SectionColumn {...{
        ...mockProps,
        onCheckboxClick: spyCheckboxClick,
      }} />
    );
    expect(spyCheckboxClick).toHaveBeenCalledTimes(0);
    // perform click to checkbox
    const checkboxComponent = component.find('StyledCheckbox');
    const input = checkboxComponent.first().children();
    input.simulate('change');
    expect(spyCheckboxClick).toHaveBeenCalledTimes(1);
    expect(spyCheckboxClick()).toEqual(null)
 });
Top answer
1 of 1
2

To specifically address the problem in your code above you need to set the service instance before calling spyOn. Something like this:

 schedulerReportService = fixture.debugElement.injector.get(SchedulerReportService);

  ...

 let scheduleServiceSpy = spyOn(schedulerReportService, submitScheduledReport');

However I recommend another way of doing this which is using a service spy stub. Since you don't need to test your service, only that a service method is called you don't need to inject the original SchedulerReportService instead you could provide a service object spy to Angular like this:

describe('SchedulerComponent', () => {
  let component : SchedulerComponent;
  let fixture : ComponentFixture<SchedulerComponent>;
  let schedulerReportService = jasmine.createSpyObj('SchedulerReportService', ['submitScheduledReport']);

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ SchedulerComponent ],
      imports: [BrowserAnimationsModule,
                MatSlideToggleModule,
                MatRadioModule,
                MatFormFieldModule,
                MatInputModule,
                MatSelectModule,
                MatNativeDateModule,
                MatDatepickerModule,
                NativeDateModule,
                NgxMaterialTimepickerModule,
                ReactiveFormsModule,
                FormsModule,
                HttpClientTestingModule],
      schemas:[CUSTOM_ELEMENTS_SCHEMA],
      providers: [{provide : SchedulerReportService, useValue: schedulerReportService}]
    })
    .compileComponents();

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

  it('onSubmit should call scheduleReportService', () => {
    component.onSubmit();
    expect(schedulerReportService.submitScheduledReport).toHaveBeenCalled();
  }));

This way you can test that schedulerReportService.submitScheduledReport was called without the overhead of the original service http calls.

🌐
Stack Overflow
stackoverflow.com › questions › 53477888 › angular-unit-testing-spyon-service-from-component
Angular unit-testing spyOn service from component - Stack Overflow
November 26, 2018 - I've got a login component and I want to test it whether it calls the service's method with proper values, however I don't know how to spy on that service from the component. ... it('should pass proper values', () => { const submitSpy = spyOn(authService, 'signInByLogin'); const loginSpy = spyOn(component, 'signIn').and.callThrough(); const username = fixture.debugElement.query(By.css('#username')); const password = fixture.debugElement.query(By.css('#password')); const submitBtn = fixture.debugElement.query(By.css('[type="submit"]')); username.nativeElement.value = 'test'; password.nativeElem
🌐
DEV Community
dev.to › devin-rosario › complete-guide-to-jestspyon-for-unit-testing-4io6
Complete Guide to Jest.spyOn for Unit Testing - DEV Community
October 16, 2025 - Perfect for testing components that depend on utility modules. Dangerous if module used across test files without proper cleanup. VISUAL CONTENT SUGGESTION: Create decision tree flowchart: "Which Jest.spyOn Strategy?" → branches for "Testing class methods?"