2

I am incredibly new to Jest, and well unit tests in general and I am suck on writing up a test for something in particular.

I understand you can mock an external module with something like this..

jest.mock('@organisation/library', () => ({
 Database: jest.fn(() => ({})),
 ....,
 ...., 
}))

And if you wish to use an actual method from that you can do something like

const database = jest.requireActual('@organisation/library')

What I want to be able to do is not mock the entire library, but only one method from that library.

Is that possible?

I might explain the code a little further.

We have a file called StationRepository. This file includes a Database method from our JS framework. This file then calls the database and returns a value.

I am wanting to test that database query.

Here is the test

import { DatabaseModule, Pg } from '@organisation/framework'
import { StationRepository } from './StationRepository'
import { DatabaseConfig } from '../../spec'

const DatabaseInstance = new DatabaseModule()

beforeAll(async () => {
  DatabaseInstance.add('default', new Pg('default', DatabaseConfig))
  jest.clearAllMocks()
})

afterAll(async () => {
  const database = DatabaseInstance.get()
  await database.disconnect()
})

describe('find timezone', () => {
  it('can find a timezone', async () => {
    const ids = [1, 2]
    const expected = ['Pacific/Auckland', 'Australia/Adelaide']
    const results = []
    for (const id of ids) {
      const timezone: string | null = await StationRepository.findTimezone(id)
      results.push(timezone)
    }

    expect(results).toEqual(expected)
  })
})

And the method in the repository

import { DatabaseFacade as Database } from '@organisation/framework'


export class StationRepository {
  /**
   * Find Timezone
   *
   * Finds the station or system timezone and returns
   *  will return station timezone
   *  if no station timezone will return system timezone
   *  if no system timezone null will be returned
   *
   * @param {number} stationId - The station id to search on
   * @param {string} db - Optional - The Database connection
   *
   * @return string | null
   */
  public static findTimezone = async (
    stationId: number,
    db?: string,
  ): Promise<string | null> => {
    const query = `
      SELECT 
        (CASE
          WHEN stations.timezone IS NOT NULL 
            THEN stations.timezone
          WHEN systems.timezone IS NOT NULL 
            THEN systems.timezone
          END 
        ) AS timezone
      FROM 
        stations
      INNER JOIN systems ON systems.id = stations.system_id
      WHERE
        stations.id = $1
      `

    const result = await Database.get(db).queryOne<StationTimezone | null>.(
      query,
      [stationId],
    )
    if (!result) {
      return null
    }

    return result.timezone
  }
}

Everything seems to work, creating the connection from the test and all that. It is failing on the actual query Database.get(). This is loaded in via the framework. If I mock the entire framework everything falls over.

So essentially what I want to do is have the framework run as normal, and only replace the DatabaseFacade area to pull in the DatabaseInstance created on the test page.

Is that possible?

nzmattman
  • 465
  • 3
  • 15
  • 1
    Have a look here: https://jestjs.io/docs/en/jest-object#jestspyonobject-methodname – Josef Wittmann Jan 08 '21 at 01:07
  • thanks Josef, I am not sure that will work for what I need. I have updated my question with an example of what I am actually doing – nzmattman Jan 08 '21 at 01:38
  • How about `jest.mock('@organisation/framework', () => {DatabaseFacade: DatabaseInstance})`? Does that work? (Make sure to call it before the import) – Josef Wittmann Jan 08 '21 at 02:33
  • 1
    You may have to add this: https://stackoverflow.com/a/64401168/5312432 – Josef Wittmann Jan 08 '21 at 02:38
  • Does this answer your question? [Mock only one function from module but leave rest with original functionality](https://stackoverflow.com/questions/59312671/mock-only-one-function-from-module-but-leave-rest-with-original-functionality) – spencer.sm Aug 01 '21 at 19:29

1 Answers1

4
const module = '@organisation/framework'
jest.mock(module, () => ({
    ...jest.requireActual(module), // You may or may not need this line
    DatabaseFacade: DatabaseInstance
}))

This will replace exported DatabaseFacade with DatabaseInstance.

(answer from comments, to document this question as resolved)

Josef Wittmann
  • 1,259
  • 9
  • 16