Заполнение базы данных | Кодементор

Когда вы пишете тесты для серверной части, вам нужно тестировать четыре разных типа операций:

  1. Создать (для добавления вещей в базу данных)
  2. Чтение (для получения данных из базы данных)
  3. Обновление (для изменения базы данных)
  4. Удалить (для удаления вещей из базы данных)

Самый простой тип для проверки — создание операций. Вы помещаете что-то в базу данных и проверяете, есть ли оно там.

Для остальных трех типов операций вам нужно что-то поместить в базу данных до вы пишете тест.

Внесение вещей в базу данных

Процесс, в котором вы добавляете начальный контент в базу данных, называется засев.

Допустим, вы хотите добавить в базу данных трех пользователей. Эти пользователи содержат имя и адрес электронной почты.

const users = [
  {
    name: "Zell",
    email: "testing1@gmail.com"
  },
  {
    name: "Vincy",
    email: "testing2@gmail.com"
  },
  {
    name: "Shion",
    email: "testing3@gmail.com"
  }
];

Вы можете использовать свои модели для заполнения базы данных в начале теста.

const User = require('../model/User') // Link to User model 

it('does something', async done => {
  // Add users to the database
  for (const u of users) {
    const user = new User(u)
    await user.save()
  }

  // Create the rest of your test here
})

Если вам нужны эти пользователи для каждого теста, лучше всего добавить их через beforeEach крюк. beforeEach хук запускается перед каждым it декларация.

// Seed the database with users
beforeEach(async () => {
  for (u of users) {
    const user = new User(u)
    await user.save()
  }
})

Вы также можете использовать Mongoose’s create функция, чтобы сделать то же самое. Он работает new Model() а также save()поэтому приведенный ниже и приведенный выше код делают одно и то же.

// Seed the database with users
beforeEach(async () => {
  await User.create(users)
})

создать против вставить много

У Mongoose есть второй метод, который поможет вам заполнить базу данных. Этот метод называется insertMany. insertMany быстрее, чем createпотому что:

  • insertMany отправляет одну операцию на сервер
  • create отправляет одну операцию для каждого документа

Однако, insertMany не запускает save промежуточное ПО.

Важен ли запуск промежуточного программного обеспечения для сохранения?

Это зависит от ваших исходных данных. Если ваши начальные данные должны пройти через save промежуточное ПО, вам нужно использовать create. Например, допустим, вы хотите сохранить пароль пользователя в базе данных. У вас есть эти данные:

const users = [
  {
    name: "Zell",
    email: "testing1@gmail.com",
    password: "12345678"
  },
  {
    name: "Vincy",
    email: "testing2@gmail.com",
    password: "12345678"
  },
  {
    name: "Shion",
    email: "testing3@gmail.com",
    password: "12345678"
  }
];

Когда мы сохраняем пароль пользователя в базе данных, мы хотим хэшировать пароль из соображений безопасности. Обычно мы хешируем пароль через save промежуточное ПО.

// Hashes password automatically
userSchema.pre('save', async function (next) {
  if (!this.isModified('password')) return next()
  const salt = bcrypt.genSaltSync(10)
  const hashedPassword = bcrypt.hashSync(password, salt)
  this.password = hashedPassword
})

Если вы используете createвы получите пользователей с хешированными паролями:

Create запускает ПО промежуточного слоя для сохранения.

Если вы используете insertManyвы получите пользователей без хешированных паролей:

InsertMany не запускает ПО промежуточного слоя для сохранения.

Когда использовать create, когда использовать insertMany

С insertMany быстрее, чем createвы хотите использовать insertMany когда вы сможете.

Вот как я это делаю:

  1. Если исходные данные не требуют save промежуточное ПО, использование insertMany.
  2. Если требуются исходные данные save промежуточное ПО, использование create. Затем перезапишите начальные данные, чтобы они больше не требовали save промежуточное ПО.

Для приведенного выше примера пароля я бы запустил create первый. Затем я копирую и вставляю хешированные исходные данные пароля. Тогда я побегу insertMany с этого момента и далее.

Если вы хотите перезаписать сложные исходные данные, вы можете получить JSON прямо из MongoDB. Для этого вы можете использовать mongoexport:

mongoexport --db <databaseName> --collection <collectionName> --jsonArray --pretty --out output.json

Это говорит:

  1. Экспорт <collection> из <databaseName>
  2. Создает вывод в виде предварительно обработанного массива JSON в файле с именем output.json. Этот файл будет помещен в папку, в которой вы запускаете команду.

Заполнение нескольких тестовых файлов и коллекций

Вам нужно место для хранения исходных данных, чтобы вы могли использовать их во всех своих тестах и ​​коллекциях. Вот система, которую я использую:

  1. Я называю свои исходные файлы в соответствии с их моделями. я сею User модель с user.seed.js файл.
  2. Я помещаю свои начальные файлы в seeds папка
  3. Я перебираю каждый начальный файл, чтобы заполнить базу данных.

Чтобы пройтись по каждому начальному файлу, вам нужно использовать fs модуль. fs обозначает файловую систему.

Самый простой способ просмотреть файлы — создать index.js файл в том же seeds папка. Как только у вас есть index.js файл, вы можете использовать следующий код для поиска всех файлов с *.seed.js

const fs = require('fs')
const util = require('util')

// fs.readdir is written with callbacks. 
// This line converts fs.readdir into a promise
const readDir = util.promisify(fs.readdir)

async function seedDatabase () {
  // Gets list of files in the directory
  // `__dirname` points to the `seeds/` folder
  const dir = await readDir(__dirname)

  // Gets a list of files that matches *.seed.js
  const seedFiles = dir.filter(f => f.endsWith('.seed.js'))
}

Когда у вас есть список исходных файлов, вы можете просмотреть каждый исходный файл, чтобы заполнить базу данных. Здесь я использую for...of цикл, чтобы все было просто.

async function seedDatabase () {
  for (const file of seedFiles) {
    // Seed the database
  } 
}

Чтобы заполнить базу данных, нам нужно найти правильную модель Mongoose по имени начального файла. Файл с именем user.seed.js следует посеять User модель. Это означает:

  1. Мы должны найти user из user.seed.js
  2. Мы должны капитализировать user в User

Вот грубая версия, которая делает то, что требуется. (Если вы хотите, вы можете сделать код более надежным с помощью регулярных выражений вместо split).

for (const file of seedFiles) {
  const fileName = file.split('.seed.js')[0]
  const modelName = toTitleCase(fileName)
  const model = mongoose.models[modelName]
}

Затем мы хотим убедиться, что у каждого файла есть соответствующая модель. Если модель не может быть найдена, мы хотим выдать ошибку.

for (const file of seedFiles) {
  //...
  if (!model) throw new Error(`Cannot find Model '${modelName}'`)
}

Если есть соответствующая модель, мы хотим заполнить базу данных содержимым начального файла. Для этого нам нужно сначала прочитать начальный файл. Здесь, поскольку я использовал .js расширение, я могу просто потребовать файл.

for (const file of seedFiles) {
  //...
  const fileContents = require(path.join(__dirname, file))
}

Чтобы это работало, мои исходные файлы должны экспортировать массив данных.

module.exports = [
  {
    name: "Zell",
    email: "testing1@gmail.com",
    password: "12345678"
  },
  {
    name: "Vincy",
    email: "testing2@gmail.com",
    password: "12345678"
  },
  {
    name: "Shion",
    email: "testing3@gmail.com",
    password: "12345678"
  }
];

Получив содержимое начального файла, я могу запустить create или же insertMany.

async function seedDatabase (runSaveMiddleware = false) {
  // ...
  for (const file of seedFiles) {
    // ... 

    runSaveMiddleware
      ? model.create(fileContents)
      : model.insertMany(fileContents)
  } 
}

вот и все seedDatabase код:

const fs = require('fs')
const util = require('util')
const readDir = util.promisify(fs.readdir).bind(fs)
const path = require('path')
const mongoose = require('mongoose')

function toTitleCase (str) {
  return str.replace(/\w\S*/g, (txt) => {
    return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase()
  })
}

async function seedDatabase (runSaveMiddleware = false) {
  const dir = await readDir(__dirname)
  const seedFiles = dir.filter(f => f.endsWith('.seed.js'))

  for (const file of seedFiles) {
    const fileName = file.split('.seed.js')[0]
    const modelName = toTitleCase(fileName)
    const model = mongoose.models[modelName]

    if (!model) throw new Error(`Cannot find Model '${modelName}'`)
    const fileContents = require(path.join(__dirname, file))

    runSaveMiddleware
      ? await model.create(fileContents)
      : await model.insertMany(fileContents)
  }
}

Почему JS, а не JSON?

Использование JSON для хранения данных является отраслевой нормой. В этом случае мне проще использовать объекты JavaScript, потому что:

  1. Мне не нужно писать открывающие и закрывающие двойные кавычки для каждого свойства.
  2. Мне вообще не нужно использовать двойные кавычки! (Легче писать в одинарных кавычках, потому что нет необходимости нажимать клавишу Shift).
// Which is easier to write. JavaScript objects or JSON?

// JavaScript objects
module.exports = [
  {
    objectName: "property"
  }
][
  // JSON
  {
    objectName: "property"
  }
];

Если вы хотите использовать JSON, убедитесь, что вы изменили seedDatabase для работы с JSON. (Я позволю вам работать с кодом самостоятельно).

Настройка функции setupDB

в предыдущая статьяя создал setupDB функция, помогающая настроить базы данных для моих тестов. seedDatabase идет в setupDB функция, так как заполнение является частью процесса настройки.

async function seedDatabase (runSaveMiddleware = false) {
  // ...
}

module.exports = {
  setupDB (databaseName, runSaveMiddleware = false) {
    // Connect to Mongoose
    beforeAll(/*...*/)

    // Seed Data 
    beforeEach(async () => {
      await seedDatabase(runSaveMiddleware)
    })

    // Cleans up database between each test
    afterEach(/*...*/)

    // Disconnect Mongoose
    afterAll(/*...*/)
  }
}

Репозиторий на GitHub

я создал Репозиторий на гитхабе чтобы пойти с этой серией испытаний из трех частей. Я надеюсь, что этот демонстрационный код поможет вам приступить к тестированию ваших приложений.

Спасибо за чтение. Эта статья изначально была размещена на мой блог. Подписаться на моя рассылка если вы хотите больше статей, которые помогут вам стать лучшим разработчиком внешнего интерфейса.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *