Search code examples
typescriptunit-testingjestjstypeorm

How to mock Typeorm DataSource with Jest


Im new to using typeorm and Jest for that matter. I have a function that is creating and returning a DataSource here:

app/lib/connections/createDBConnection.ts

 const createDBConnection = async () => {
  const dbUrl = Environment.get('DATABASE_URL')
  const db = new DataSource({
    type: 'postgres',
    url: dbUrl,
    synchronize: false,
    entities: [],
    connectTimeoutMS: 10000,
  })
  await db.initialize()
  return db
}

export default createDBConnection

Here is the get function if it helps:

export class Environment {
  public static get(key: string): string {
    const result = process.env[key]

    if (result === undefined) {
      throw new Error(`Failed to find key ${key} in environment`)
    }

    return result
  }

Here is my test file:

const FakeDB = new DataSource({
  type: 'postgres',
  url: 'fakeURL',
  synchronize: false,
  entities: [],
  connectTimeoutMS: 10000,
})

jest.mock('app/lib/connections/createDBConnection', () => {
  return jest.fn().mockImplementation(() => Promise.resolve(FakeDB))
}
)

afterEach(() => {
  jest.clearAllMocks()
})

test('creates a fake connection', async () => {
  const DbConnection = await createDBConnection()
  expect(DbConnection).toBeTruthy()
  expect(DbConnection).toBeInstanceOf(DataSource)
  expect(DbConnection).toEqual(FakeDB)
  
  // Im trying to make something like these pass
  expect(FakeDB.initialize).toHaveBeenCalled
  expect(FakeDB.isInitialized).toBe(true)
})

Im not sure how to mock out the initialize function correctly. I want to verify its called in my function somehow. The first three assertions seem to pass, but im not sure if its actually correct, since those bottom 2 both fail. Any help or advice will be appreciated.

Ive tried mocking the initialize a couple of ways like:

 jest.mock('typeorm'), () => {
   return jest.fn().mockImplementation(() => {
     return {
       initialize: () => {true}
   }
 })
 }

Solution

  • Seems that you are mocking TypeORM which you shouldn't. You should trust the API and that TypeORM will return an instance of DataSource and that it sets isInitialized=true.

    What you might do is to mock createDBConnection in the components that are consuming it, or maybe design your code with dependency injection (DI) so you can inject your DataSource instance in your application, or its double when testing.

    And, if you still want to test that your createDBConnection is working as expected, you may assert that Environment.get is called with DATABASE_URL and that db.initialize() is called:

    const mockDS = {
      initialize: jest.fn(),
    };
    
    jest.mock("typeorm", () => {
      return {
        DataSource: jest.fn().mockImplementation(() => mockDS),
      };
    });
    
    // Env
    const mockEnv = {
      get: jest.fn(),
    };
    jest.mock("src/path/to/environment/file", () => {
      return {
        Environment: mockEnv,
      };
    });
    
    import createDBConnection from "src/app/lib/connections/createDBConnection.ts";
    
    afterEach(() => {
      jest.clearAllMocks();
    });
    
    test("calls .initialize() and env.get", async () => {
      await createDBConnection();
    
      expect(mockEnv.get).toHaveBeenCalledWith("DATABASE_URL");
      expect(mockDS.initialize).toHaveBeenCalled();
    });