DEV Community

Cover image for Mock TypeORM Package
Jazim Abbas
Jazim Abbas

Posted on • Edited on

Mock TypeORM Package

References

  1. Mock TypeORM Package
  2. Mock TypeORM Package Documentation

In the last couple of months, I have been working extensively with TypeORM and built many projects using this ORM. Setting up a test database for my unit tests was quite challenging. To overcome this, I mocked TypeORM methods to avoid interacting with a real database. However, I found myself copying the same mock code into every new project repeatedly.

To streamline this process and help others facing the same issue, I decided to create a package. Initially, I mocked all the TypeORM code using Vitest, but that only worked in the Vitest environment. Since many developers use different testing frameworks such as Jest, Mocha, etc., I needed a more versatile solution. Hence, I used Sinon for mocking TypeORM, which is compatible with various testing frameworks. Although Sinon is excellent, I still have a soft spot for Vitest.

Installation

This package is built using TypeScript, so you’ll get type safety out of the box. I have also added thorough unit tests, which you can refer to for better understanding and reference.

To install this package, use the following command:

npm install --save-dev mock-typeorm sinon @types/sinon
Enter fullscreen mode Exit fullscreen mode

That’s pretty much it! Now you can use this package to mock your TypeORM calls. Note that Sinon is added as a peer dependency, so you need to install it as well.

Key Concepts

Here’s how you can mock TypeORM calls. Use the following snippet:

import { MockTypeORM } from 'mock-typeorm'

test('abc', () => {
  new MockTypeORM()
})

Enter fullscreen mode Exit fullscreen mode

By just doing this, you prevent any interaction with your database. This is the magic of mocking.

Reset or Restore Mock State

After each test, you need to clear the mock state so that other tests cannot use the mock state of previous tests. Otherwise, you’ll get some strange results. To reset the state, you have different methods:

Method 1: Create an instance for each test

import Sinon from 'sinon'
import { MockTypeORM } from 'mock-typeorm'
import { afterEach, beforeEach, describe, expect, it } from 'vitest'

describe('tests suite', () => {
  let typeorm: MockTypeORM

  beforeEach(() => {
    typeorm = new MockTypeORM()
  })

  afterEach(() => {
    typeorm.restore()
    // you can also do this instead - Sinon.restore()
    // Sinon.restore();
  })

  it('first test', async () => {
    const mockUsers = ['user']
    typeorm.onMock('User').toReturn(mockUsers, 'find')

    const users = await dataSource.getRepository(User).find()

    expect(users).toEqual(mockUsers)
  })

  it('second test', async () => {
    const mockUsers = []
    typeorm.onMock('User').toReturn(mockUsers, 'find')

    const users = await dataSource.getRepository(User).find()

    expect(users).toEqual(mockUsers)
  })
})
Enter fullscreen mode Exit fullscreen mode

In this approach, using hooks provided by Vitest (or similar hooks from other testing libraries), we create a new MockTypeORM object in the beforeEach hook and restore TypeORM to its original state in the afterEach hook.

Method 2: Single Instance

import Sinon from 'sinon'
import { MockTypeORM } from 'mock-typeorm'
import { afterAll, afterEach, beforeAll, describe, expect, it } from 'vitest'

describe('tests suite', () => {
  let typeorm: MockTypeORM

  beforeAll(() => {
    typeorm = new MockTypeORM()
  })

  afterEach(() => {
    typeorm.resetAll()
  })

  afterAll(() => {
    typeorm.restore()
  })

  it('first test', async () => {
    const mockUsers = ['user']
    typeorm.onMock('User').toReturn(mockUsers, 'find')

    const users = await dataSource.getRepository(User).find()

    expect(users).toEqual(mockUsers)
  })

  it('second test', async () => {
    const mockUsers = []
    typeorm.onMock('User').toReturn(mockUsers, 'find')

    const users = await dataSource.getRepository(User).find()

    expect(users).toEqual(mockUsers)
  })
})
Enter fullscreen mode Exit fullscreen mode

In this approach, we create a MockTypeORM instance once before all tests start and reset the mock state after each test. After all tests, we restore TypeORM to its original behavior.

Mocking - Fun Stuff

Now that you understand how mocking works and how to restore it to its original behavior, let's see how to mock actual methods like find(), findOne(), save(), etc.

Example

describe('test suites', () => {
  it('test', async () => {
    const typeorm = new MockTypeORM()
    typeorm.onMock(User).toReturn(['user'], 'find')

    const userRepo = dataSource.getRepository(User)
    const users = await userRepo.find()

    expect(users).toEqual(['user'])
  })
})
Enter fullscreen mode Exit fullscreen mode

Helper Functions

  • onMock()
  • resetAll()
  • restore()

onMock()

onMock() accepts a repository class or string (repository name). This is useful when using EntitySchema in JavaScript.

const typeorm = new MockTypeORM()
typeorm.onMock(User) // repository class
typeorm.onMock('User') // repository name as string
Enter fullscreen mode Exit fullscreen mode

onMock() returns this to allow method chaining:

typeorm.onMock(User).toReturn([], 'find').toReturn({ id: '1' }, 'findOne')
Enter fullscreen mode Exit fullscreen mode

reset()

onMock() also returns a reset() method to reset mock data:

describe('test suites', () => {
  it('test', async () => {
    const typeorm = new MockTypeORM()
    typeorm.onMock(User).toReturn(['user'], 'find').reset('find')

    const userRepo = dataSource.getRepository(User)
    const users = await userRepo.find()

    expect(users).toEqual({})
  })
})
Enter fullscreen mode Exit fullscreen mode

As you can see, I reset the mock data for the find method that we set using the toReturn() function. So when the find() method is called and no mock data is found for that method, it will return {} by default. That’s what we are expecting in our test assertion, meaning we have successfully reset the find method.

To reset everything at the repository level:

describe('test suites', () => {
  it('test', async () => {
    const typeorm = new MockTypeORM()
    typeorm
      .onMock(User)
      .toReturn(['user'], 'find')
      .toReturn({ id: '1' }, 'findOne')
      .reset()

    const userRepo = dataSource.getRepository(User)
    const users = await userRepo.find()
    const user = await userRepo.findOne({})

    expect(users).toEqual({})
    expect(user).toEqual({})
  })
})
Enter fullscreen mode Exit fullscreen mode

If you find this package useful please stare this my github repository and share with other developers.

Buy Me A Coffee

So if you enjoy my work and found it useful, consider buying me a coffee! I would really appreciate it.

Top comments (0)