TypeScript'te Jest, Supertest, Knex ve Objection ile Entegrasyon Testleri

Fatih Küçükkarakurt

Fatih Küçükkarakurt

6 min read

TypeScript ile Jest ve Supertest kullanarak, entegrasyon testleri için kullanabileceğiniz bir veritabanını, nasıl kuracağınızı ve parçalayacağınızı anlatmaya çalışacağım.. Testlerinizde gerçek bir veritabanına gerçek çağrılar yapacak ve HTTP yanıtlarını onaylayabileceksiniz.

Son günlede, TypeScript'te bir Node API'si ile çok fazla çalışıyorum, burada bazı birim ve entegrasyon testleri kurdum. Bununla ilgili çok fazla kaynak bulamadım. Bu yüzden deneyimlerimlerimi sizinle paylaşmak ve yol gösterici bir içerik hazırlamak istedim.

Gerekenler

Ne Öğreneceğiz?

Bir test veritabanını döndürebilmeyi, yanıtlar ve hatalarla gerçek API çağrıları yapabilmeyi ve testlerin sonunda veritabanını parçalayabilmeyi amaçlıyoruz.

Her ayrıntıyı adım adım anlatan, eksiksiz bir makale olmayacak. Ancak size, TypeScript API'sini Objection ile kurmanın ve bunun için bir test paketi oluşturmanın, nasıl olduğu konusunda büyük ölçüde fikir verecektir.

Kurulum

Bu uygulamada, objection, knex, pg, express, ve typescript kullanacağız. Ayrıca test için jestve supertest kullanacağız.

npm i objection knex pg express
npm i -D typescript jest jest-extended supertest ts-jest ts-node

GET/books/:id adında, "books" nesnesi döndüren, uç noktaya sahip bir API'niz olduğunu düşünelim. Veritabanında bir book tablosu olduğunu varsayarsak, Book için Objection modelimiz şöyle olacaktır:

//book.model.ts
import {Model} from 'objection'

export class Book extends Model {
  id!: string
  name!: string
  author!: string

  static tableName = 'book' // database tablo ismi
  static idColumn = 'id' // id kolon ismi
}

export type BookShape = ModelObject<Book>

Tek bir uç noktaya sahip Express uygulamasını meydana getirdik bile. Testlerin uygulamayı başlatmaması ve sorunlara neden olmaması için uygulamayı dışa aktarmak ve app.listen() dosyasını burada ÇALIŞTIRMAMAK önemlidir.

//app.ts
import express, {Application, Request, Response, NextFunction} from 'express'
import {Book} from './book.model'

// App'i dışa aktar
export const app: Application = express()

app.use(express.json())
app.use(express.urlencoded({extended: true}))

// Book için uç noktada >> GET işlevi
app.get(
  '/books/:id',
  async (request: Request, response: Response, next: NextFunction) => {
    try {
      const {id} = request.params

      const book: BookShape = await Book.query().findById(id)

      if (!book) {
        throw new Error('Book not found')
      }

      return response.status(200).send(book)
    } catch (error) {
      return response.status(404).send({message: error.message})
    }
  },
)

index.ts, veritabanı bağlantınızı kuracağınız ve uygulamayı başlatacağınız yerdir.

//index.ts
import Knex from 'knex'
import {Model} from 'objection'

// App'i içe aktar
import {app} from './app'

// Veritabanını kur (Postgres varsayılarak)
const port = 5000
const knex = Knex({
  client: 'pg',
  connection: {
    host: 'localhost',
    database: 'books_database',
    port: 5432,
    password: 'your_password',
    user: 'your_username',
  },
})

// Veritabanını Objection'a bağla
Model.knex(knex)

// App'i başlat
app.listen(port, () => console.log(`*:${port} - Listening on port ${port}`))

Artık /books/:id uç noktası için eksiksiz bir API'niz var.

tsc && npm start

komutu ile bu API'yi başlatabilirsiniz.

Diğer bir yöntem olarak bir geliştirme sunucusunu çalıştırmak için nodemon kullanabilirsiniz.

Knex İşlemleri

Knex'te şema veya veriler için yalnızca ham SQL kullanmak yerine farklı bir geçiş kullanabilirsiniz. Bir geçiş dosyası oluşturmak için Knex CLI'yi kullanmanız yeterlidir:

knex migrate:make initial-schema

Şimdi verileri ayarlayın. Örneğimizdeki gibi birkaç sütuna sahip basit bir kitap tablosu yapabilirsiniz:

//db/migrations/initial-chema.js
exports.up = async function (knex) {
  await knex.schema.createTable('book', function (table) {
    table.increments('id').primary().unique()
    table.string('name').notNullable()
    table.string('author').notNullable()
  })
}

exports.down = async function (knex) {
  await knex.schema.dropTable('book')
}

Test Yapılandırması

Temel jest-config.js dosyanız aşağıdaki gibi görünmelidir:

module.exports = {
  clearMocks: true,
  moduleFileExtensions: ['ts'],
  roots: ['<rootDir>'],
  testEnvironment: 'node',
  transform: {
    '^.+\\.ts?$': 'ts-jest',
  },
  setupFilesAfterEnv: ['jest-extended'],
  globals: {
    'ts-jest': {
      diagnostics: false,
    },
  },
  globalSetup: '<rootDir>/tests/global-setup.ts',
  globalTeardown: '<rootDir>/tests/global-teardown.ts',
}

globalSetup ve globalTeardownözelliklerini ve bunlara karşılık gelen dosyaları not alın. Bu dosyalar ile veritabanını taşıyabilir ve işiniz bittiğinde onu parçalayabilirsiniz.

globalSetup

Global kurulumda, ben iki adımlı bir işlem uyguladım. Önce veritabanı olmadan bağlanıp veritabanını oluşturun, ardından veritabanını taşıyın. Nasıl taşıyacağınızı Knex Belgerinden öğrenebilirsiniz.

//tests/global-setup.ts
import Knex from 'knex'

const database = 'test_book_database'

// Veritabı Oluştur
async function createTestDatabase() {
  const knex = Knex({
    client: 'pg',
    connection: {
      /* veritabanı olmadan bağlantı */
    },
  })

  try {
    await knex.raw(`DROP DATABASE IF EXISTS ${database}`)
    await knex.raw(`CREATE DATABASE ${database}`)
  } catch (error) {
    throw new Error(error)
  } finally {
    await knex.destroy()
  }
}

async function seedTestDatabase() {
  const knex = Knex({
    client: 'pg',
    connection: {
      /* veritabanı ile bağlantı */
    },
  })

  try {
    await knex.migrate.latest()
    await knex.seed.run()
  } catch (error) {
    throw new Error(error)
  } finally {
    await knex.destroy()
  }
}

Ardından, her ikisini de yapan işlevi dışa aktarın.

//tests/global-setup.ts
module.exports = async () => {
  try {
    await createTestDatabase()
    await seedTestDatabase()
    console.log('Test database created successfully')
  } catch (error) {
    console.log(error)
    process.exit(1)
  }
}

globalTeardown

Teardown için veritabanını silmeniz yeterlidir.

//tests/global-teardown.ts
module.exports = async () => {
  try {
    await knex.raw(`DROP DATABASE IF EXISTS ${database}`)
  } catch (error) {
    console.log(error)
    process.exit(1)
  }
}

Entegrasyon Testleri

Bir entegrasyon testi ile, bireysel testte bazı verileri tohumlamak (seed) ve tüm başarılı yanıtların yanı sıra hata yanıtlarını da test edebilmek istersiniz.

Test kurulumunda, yeni bir Knex örneği oluşturup, Objection modeline bağlayarak, istediğiniz veritabanına istediğiniz ekstra veriyi ekleyebilirsiniz.

Burada, HTTP istekleri için popüler bir kitaplık olan Supertest'i kullanacağız.

Supertest, knex, objection ve app'i içe aktarın, ihtiyacınız olan verileri tohumlayın ve testler yazmaya başlayın.

//books.test.ts
import request from 'supertest'
import Knex from 'knex'
import {Model} from 'objection'

import {app} from '../app'

describe('books', () => {
  let knex: any
  let seededBooks

  beforeAll(async () => {
    knex = Knex({
      /* test_book_database ile yapılandırma*/
    })
    Model.knex(knex)

    // herhangi bir seed
    seededBooks = await knex('book')
      .insert([{name: 'A Game of Thrones', author: 'George R. R. Martin'}])
      .returning('*')
  })

  afterAll(() => {
    knex.destroy()
  })

  decribe('GET /books/:id', () => {
    // Testler buraya gelecek
  })
})

Başarılı Yanıt Testi

Bu noktada, tüm kurulum hazırdır ve başarılı bir tohum test edebilir ve uç noktada GET uygulayabilirsiniz.

//tests/books.test.ts
it('should return a book', async () => {
  const id = seededBooks[0].id

  const {body: book} = await request(app).get(`/books/${id}`).expect(200)

  expect(book).toBeObject()
  expect(book.id).toBe(id)
  expect(book.name).toBe('A Game of Thrones')
})

Başarısız Yanıt Testi

Beklenen tüm hataların düzgün çalıştığından emin olmak önemlidir.

//tests/books.test.ts
it('should return 404 error ', async () => {
  const badId = 7500
  const {body: errorResult} = await request(app).get(`/books/${badId}`).expect(404)

  expect(errorResult).toStrictEqual({
    message: 'Book not found',
  })
})

Sonuç Olarak

Artık komut satırında npm run test veya jest komutunu çalıştırdığınızda, test_book_database veritabanını oluşturacak, sahip olduğunuz tüm geçişlerle (şemayı ve gerekli verileri ayarlamak için) tohumlayacak ve her birinde veritabanına erişebileceksiniz.

Böylece, veritabanı tohumlamadan, API denetleyicilerine kadar tüm sürecin düzgün çalışması sağlanmış olur. Bu tür bir kod, uygulama içindeki modeller, rotalar ve işleyiciler hakkında size tam kapsamlı bilgi sağlar.

Umarım yardımcı olabilmişimdir. Kendinize iyi bakın.

Burası AnatoliaCode.

Anatoliacode Makale Aboneliği

Bize abone olarak tüm makaleleri ilk siz okuyabilirsiniz. Ayrıca asla reklam veya spam yapmıyoruz.