阿里云主机折上折
  • 微信号
Current Site:Index > Setup of integrated testing environment

Setup of integrated testing environment

Author:Chuan Chen 阅读数:11201人阅读 分类: Node.js

Environment Preparation

To set up a Koa2 integration testing environment, first ensure the basic toolchain is in place. It is recommended to use an LTS version of Node.js (e.g., 16.x or 18.x), with npm automatically installed alongside Node.js. Install Yarn globally as the package management tool:

npm install -g yarn

Initialize the project using Yarn to create the basic structure:

mkdir koa2-integration-test
cd koa2-integration-test
yarn init -y

Key dependencies need to be installed step by step. First, install the Koa2 core and commonly used middleware:

yarn add koa @koa/router koa-bodyparser

Testing-related dependencies should be separated into devDependencies and production dependencies. Install the testing framework and assertion library using the following command:

yarn add -D mocha chai supertest nyc

Test Framework Configuration

Mocha, as the test runner, requires a configuration file. Create .mocharc.json in the project root directory:

{
  "require": ["esm"],
  "extension": ["js"],
  "timeout": 5000,
  "recursive": true,
  "exit": true
}

The test directory structure is recommended to be divided by functional modules:

test/
  ├── integration/
  │   ├── user.test.js
  │   └── product.test.js
  ├── fixtures/
  └── utils/

Add test scripts to package.json:

{
  "scripts": {
    "test": "NODE_ENV=test mocha",
    "test:cov": "nyc --reporter=text mocha"
  }
}

Application Instance Encapsulation

Testing requires an independent Koa application instance. Create test/bootstrap.js:

const Koa = require('koa')
const app = new Koa()

// Load middleware
const bodyParser = require('koa-bodyparser')
app.use(bodyParser())

// Dynamic route loading
const fs = require('fs')
const path = require('path')
const routerFiles = fs.readdirSync(path.join(__dirname, '../routes'))

routerFiles.forEach(file => {
  const router = require(`../routes/${file}`)
  app.use(router.routes())
})

module.exports = app

Database Isolation Handling

Integration testing requires an isolated test database. Use environment variables to switch configurations:

// config/database.js
const isTest = process.env.NODE_ENV === 'test'

module.exports = {
  host: isTest ? 'localhost' : 'prod.db.example.com',
  name: isTest ? 'test_db' : 'production_db'
}

Example helper function for initializing the database before testing:

// test/utils/db.js
const { MongoClient } = require('mongodb')

async function resetTestDB() {
  const client = new MongoClient('mongodb://localhost:27017')
  await client.connect()
  const db = client.db('test_db')
  
  // Clear collections
  const collections = await db.collections()
  await Promise.all(collections.map(col => col.deleteMany({})))
  
  // Insert base data
  await db.collection('users').insertMany([
    { name: 'test1', role: 'admin' },
    { name: 'test2', role: 'user' }
  ])
  
  await client.close()
}

HTTP Test Case Writing

Use supertest to write API test cases. Example user module test:

// test/integration/user.test.js
const request = require('supertest')
const app = require('../../test/bootstrap')
const { expect } = require('chai')

describe('User API', () => {
  before(async () => {
    await require('../utils/db').resetTestDB()
  })

  describe('GET /users', () => {
    it('should return all users', async () => {
      const res = await request(app.callback())
        .get('/users')
        .expect(200)
        
      expect(res.body).to.have.lengthOf(2)
      expect(res.body[0]).to.have.property('name', 'test1')
    })
  })

  describe('POST /users', () => {
    it('should create new user', async () => {
      const mockUser = { name: 'new_user', email: 'test@example.com' }
      
      const res = await request(app.callback())
        .post('/users')
        .send(mockUser)
        .expect(201)
        
      expect(res.body).to.have.property('_id')
      expect(res.body.name).to.equal(mockUser.name)
    })
  })
})

Test Coverage Integration

Istanbul's nyc tool requires a .nycrc configuration file:

{
  "all": true,
  "include": ["routes/**/*.js", "controllers/**/*.js"],
  "exclude": ["**/*.spec.js", "**/node_modules/**"],
  "reporter": ["lcov", "text"],
  "check-coverage": true,
  "branches": 80,
  "lines": 80,
  "functions": 80,
  "statements": 80
}

Generate HTML reports in CI environments:

nyc --reporter=html mocha

Continuous Integration Configuration

GitHub Actions configuration example (.github/workflows/test.yml):

name: Test

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    
    services:
      mongodb:
        image: mongo:4.4
        ports: ['27017:27017']
        options: --health-cmd "mongo --eval 'db.stats()'" --health-interval 10s
        
    steps:
      - uses: actions/checkout@v2
      - uses: actions/setup-node@v2
        with:
          node-version: '16'
          
      - run: yarn install
      - run: yarn test:cov
      
      - name: Upload coverage
        uses: codecov/codecov-action@v1

Test Data Factory

Use factory-girl to create test data:

// test/factories/user.js
const factory = require('factory-girl')
const { User } = require('../../models')

factory.define('user', User, {
  name: factory.sequence('User.name', n => `user_${n}`),
  email: factory.sequence('User.email', n => `user_${n}@test.com`),
  status: 'active'
})

// Usage in tests
const testUser = await factory.create('user', {
  role: 'admin'
})

Test Hook Optimization

Global test hooks can be placed in test/hooks.js:

const sinon = require('sinon')
const chai = require('chai')

before(() => {
  // Global sinon sandbox
  global.sandbox = sinon.createSandbox()
  
  // Chai plugins
  chai.use(require('sinon-chai'))
  chai.use(require('chai-as-promised'))
})

afterEach(() => {
  global.sandbox.restore()
})

Performance Testing Integration

Example of using autocannon for stress testing:

// test/performance/api.test.js
const autocannon = require('autocannon')
const app = require('../../app')

describe('Performance', () => {
  let server
  
  before(() => {
    server = app.listen(0)
  })
  
  after(() => {
    server.close()
  })
  
  it('should handle 1000 requests', async () => {
    const port = server.address().port
    const result = await autocannon({
      url: `http://localhost:${port}`,
      connections: 10,
      duration: 5
    })
    
    console.log(result)
    expect(result.errors).to.equal(0)
  })
})

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.