# Testing the Domain

### Repository

Recall how we implemented the `userRepository` in one of the earlier chapters.

*File: src/domain/repositories/user.repository.js*

```javascript
const UserModel = require('../models/user.model')

/**
 *
 * @param {*} user {
 *  - name
 *  - email
 *  - password
 * }
 *
 * @returns user
 */
const createUser = async (userData) => {
  const user = new UserModel(userData)
  const userReturn = await user.save(userData)

  return userReturn
}

module.exports = {
  createUser
}
```

Now it's time to write out test cases.

For testing repositories, what we are mainly interested in is if the database operations are working. For that to work, we'll have to arrange to connect to the database on every test.

Here we have a couple of helpers that will help us test for database-based test cases. The `db` function let's us create a connection to the database, it's simply a wrapper around the mongoose database driver, that is why you see a `disconnect` function in the `afterAll` function. We then have a `dbTestUtils` object that has a `clearDatabase` function which allows us to reset the database and let's us start off from a clean slate. For a closer look at what those helper functions do, you can dive deeper by looking at the source code of the repository.

*File: src/domain/repositories/\_\_tests\_\_/user.repository.test.js*

```javascript
const db = require('../../../utils/db')
let dbConnection
const dbTestUtils = require('../../../../tests/testUtils/dbTestUtil')

const userRepository = require('../user.repository')

beforeAll(async () => {
  dbConnection = await db()
})

afterEach(async () => {
  await dbTestUtils.clearDatabase()
})

afterAll(async () => {
  await dbConnection.disconnect()
})

describe('Test Suite: User Repository', () => {
  // Our tests go here...
})
```

Now Moving on to the first successful test case, we have the following. We are simply calling the `createUser` method and passing in all of the necessary fields. This is a nice, clean, and simple test.

```javascript
test('User Repository - createUser - success', async () => {
  const testUser = {
    first_name: 'Yichen',
    last_name: 'Zhu',
    email: 'yichen@yichen.com',
    password: 'password123',
    phone_number: '1234567890'
  }

  const user = await userRepository.createUser(testUser)
  const expectedName = 'Yichen'
  const actual = user.first_name

  expect(actual).toEqual(expectedName)
})
```

For the failing test case, we can decide to not pass in certain required fields. Recall that our UserModel had certain required fields when we created it.

Here is what a possible failing test case would look like.

```javascript
test('User Repository - createUser - error', async () => {
  try {
    const testUser = {
      email: 'yichen@yichen.com'
    }

    await userRepository.createUser(testUser)
  } catch (error) {
    /* eslint-disable-next-line */
    expect(error.message).toBe(
      'user validation failed: phone_number: Path `phone_number` is required., password: Path `password` is required., first_name: Path `first_name` is required.'
    )
  }
})
```

Although this seems a little redundant due to the fact that we already have validation in another layer, this is simply to illustration how one might go about writing tests for a repository layer. When you start creating more complicated repositories that use multiple different models, you'll find more complicated tests are needed.

### Service

Recall how we implemented the `authService` in one of the earlier chapters.

*File: src/domain/services/auth.service.js*

```javascript
const ApiException = require('../../utils/ApiException')
const userRepository = require('../repositories/user.repository')

/**
 * @returns User
 */
const registerUser = async (user) => {
  try {
    const createdUser = await userRepository.createUser(user)
    return createdUser
  } catch (err) {
    if (err.code === 11000 && err?.keyPattern?.email === 1) {
      throw new ApiException({
        message: `This email is already taken.`,
        status: 'error',
        code: 409,
        data: null,
        errors: [`This email is already taken.`]
      })
    }
  }
}
```

The service layer is going to be all about us mocking out the repository layer. We do so in order to keep the tests as separate and isolated as possible.

For a successful test case, take a look at the following. We do a simple mock of both the `userRepository` and its method `createUser` functions and see if they've been called properly.

*File: src/domain/services/\_\_tests\_\_/auth.service.test.js*

```javascript
const userRepository = require('../../repositories/user.repository')
const authService = require('../auth.service')

beforeAll(async () => {})

beforeEach(() => {
  userRepository.createUser = jest.fn(() => {
    return {}
  })
})

afterEach(async () => {})

afterAll(async () => {})

describe('Test Suite: Auth Service', () => {
  test('Auth Service - registerUser', async () => {
    const testUser = {
      first_name: 'john',
      last_name: 'doe',
      email: 'john@john.com',
      password: 'password',
      phone_number: '4168561988'
    }
    await authService.registerUser(testUser)

    expect(userRepository.createUser).toHaveBeenCalledWith(testUser)
    expect(userRepository.createUser).toHaveBeenCalledTimes(1)
    expect(userRepository.createUser).toHaveReturnedWith({})
  })
})
```

For the failing test, we'll simply want to throw an `ApiException` artificially.

```javascript
test('Auth Service - registerUser - error', async () => {
  userRepository.createUser = jest.fn(() => {
    class CustomError extends Error {
      constructor() {
        super()
        this.code = 11000
        this.keyPattern = {
          email: 1
        }
      }
    }

    throw new CustomError()
  })

  try {
    await authService.registerUser({})
  } catch (err) {
    /* eslint-disable-next-line */
    expect(err.message).toBe('This email is already taken.')
  }
})
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://book.restfulnode.com/part-3/chapter-10/4-testing-the-domain.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
