Writing tests: type error not a function

Hi,

It is my first time writing tests, using jest. The error says that sortEvents is not a function even though it is:

import React from "react";
import ReactDOM from "react-dom";
import App from "./components/App";
import { sortEvents } from "./components/CalendarChallenge";

it("App renders successfully", () => {
  const div = document.createElement("div");
  ReactDOM.render(<App />, div);
  ReactDOM.unmountComponentAtNode(div);
});

// const { sortEvents } = require("./components/CalendarChallenge");
test("sortEvents", () => {
  let events = sortEvents(1, 5, 2);
  expect(events).toBe(1, 2, 5);
});
 sortEvents = () => {
    return [...this.props.events].sort((a, b) => (a.start > b.start ? 1 : -1));
  };

By any chance does sortEvents look like this:

class CalenderChallenge extends React Component {
  // Some stuff

 sortEvents = () => {
    return [...this.props.events].sort((a, b) => (a.start > b.start ? 1 : -1));
  };

  // Some stuff
}

Because sortEvents isn’t a function in that case, the error message is correct, it’s a property on an object (CalendarChallenge)

If you’re importing a function called sortEvents, you need to export one called sortEvents from the file.

And if it’s a function, it needs to be a standalone function so that you can actually test it, like

export const sortEvents = (events) => {
    return [...events].sort((a, b) => (a.start > b.start ? 1 : -1));
  };

export default class CalenderChallenge extends React Component {
  // Some stuff

  sortEvents() {
    // Use the function in the component
    return sortEvents(this.props.events);
  }

  // Some stuff
}

Otherwise it depends on the internal state and structure of the component, so cannot be unit tested.

Thanks! this all sounds right - I’ll try it out.

Sorry I understand the problem, but your solution, I’m not sure about.

Your line 1 ‘export const’ errors with my linter, and why have you got sortEvents() written separately below as well?

(My import statement in the test file has it destructured so I would have thought everything is included:
‘import { sortEvents } from “./components/CalendarChallenge”;’)

Okay, for the first thing, then do either

export function sortEvents (events) { // your logic

or

const sortEvents = (events) => {

then afterwards

export sortEvents;

The second thing – this is a misunderstanding of what classes are, in any language, not just JS. They aren’t just a way to collect some functions under a name on a file, they’re a way of defining a factory that creates objects:

class MyClass {
  myMethod() {
    return "hello"
  }
}

myMethod doesn’t exist as a separate thing, and doesn’t exist at all unless the class is instantiated:

const example = new MyClass()
example.myMethod() // returns "hello"

You can’t access myMethod separately.

And even if you could (which, just to emphasise, is not possible in any language), what would this.props.events be? If myMethod could be called as a separate function, then this is going to be called in a scope where this is the global scope (so as these tests are being run in Node, is global). There is no property called props on global, so the test cannot work: outside of that instantiated class, there is no data for the function to work on. Classes encapsulate some data and some functionality to work on that data.

So to unit test that function, you need to export it as a separate function, and you need to give it a parameter of events.

In the test, you pass it a piece of data that you write – an array of objects that represents events – and you check that it correctly sorts that. The test is checking that, given data that is the same shape as that which will be used in reality, that that unit of functionality works as expected.

In the class, you call that function, and pass it this.props.events.


Also

The import statement is not destructuring (although it looks similar), it is just the syntax for named imports:

export function someFunction() {}
const foo = 1;
export foo;
export class SomeClass {}

They are all named exports, so to import:

import { someFunction, foo, SomeClass } from "./someFile";

You only get to miss off the curly brackets if you are importing a default export (or under a namespace, but I’ll not confuse things further):

export default foo;
import foo from "./somefile";

thanks for that explanation. It’s just that my linter isn’t letting me do export sortEvents anywhere, with this warning: “Declaration or statement expected”.

I’ve tried it above the “export default CalendarChallenge”, below the function itself etc.