nestjs+postgreSql实现单元测试

1、使用npm安装数据库连接模块
npm install --save pg
2、Nest 与数据库无关,允许与任何 SQL 或 NoSQL 数据库集成,先安装依赖包。
npm install --save @nestjs/typeorm typeorm postgresql
3、测试时需要连接一个放置测试结果的数据库,遇到了一个问题,问题如下:Cannot create a new connection named “default”, because connection with such name already exist and it now has an active connection session.
在这里插入图片描述
解决方法:
在TypeOrmModule.forRoot模块添加一个keepConnectionAlive选项,设置其为true

举了例子:
我原来的代码:

// 错误代码
TypeOrmModule.forRoot({
          type: 'postgres',
          // host:'192.168.10.251',
          port: 5432,
          username: 'postgres',
          password: '123456',
          database: 'catsdb_test',
          name:'conn_test',
          entities: [Cat],
          synchronize: true,
           }),

更改后的代码:

// 正确代码
TypeOrmModule.forRoot({
          type: 'postgres',
          // host:'192.168.10.251',
          port: 5432,
          username: 'postgres',
          password: '123456',
          database: 'catsdb_test',
          name:'conn_test',
          entities: [Cat],
          synchronize: true,
          keepConnectionAlive:true,//**就是加这一句!**
        }),

4、测试时遇到另一个问题:
Nest can’t resolve dependencies of the DogRepository (?). Please make sure that the argument Connection at index [0] is available in the TypeOrmModule context.

Potential solutions:
- If Connection is a provider, is it part of the current TypeOrmModule?
- If Connection is exported from a separate @Module, is that module imported within TypeOrmModule?
  @Module({
    imports: [ /* the Module containing Connection */ ]
  })

解决方法:这个只要加入相关的模块就可以编译通过,但是!!!我测试的功能模块是向数据库中插入数据,第一次往数据库插入静态数据成功了,然后我就试图插入动态数据,结果崩了,崩了的后果就是即使我改回静态数据也只是编译通过,数据并没有插进数据库,我感觉我这块的原因是测试语句中请求http服务器失败,但他并没有报错,总之,我解决不了,这里吸取一个教训,那就是一定要进行版本控制!!!血与泪的教训啊!

言归正传,关于上述这个问题官网上有很多讨论,即mock出所有与被测模块相关的模块,具体可以看我下面的代码,如果不适用你的错误,建议看看官网上的讨论,这里附上官网讨论的链接:https://github.com/nestjs/nest/issues/363 .

我研究了很久,发现有两种方法:
(1)mock一个service,你测试的时候会调用模拟的service方法,但因为你这里模拟的是完美的service方法,所以测试编译肯定会通过。但是数据不会跟数据库产生联系。这块的写法可以对比
官网测试文档链接: https://docs.nestjs.cn/6/fundamentals?id=%e6%b5%8b%e8%af%95.和如下关于我写的cats.controller.spec.ts

测试文件的代码如下:

import { Test, TestingModule } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { INestApplication } from '@nestjs/common';
import { Cat } from '@libs/db/models/cat.model';
import { CatsService } from './cats.service ';
import { TypeOrmModule} from '@nestjs/typeorm';
import * as request from 'supertest';




describe('Cats Controller', () => {
  let controller:CatsController;
  let app: INestApplication;

  const catsService = {    //此处需要mock一个service,因为目前进行的是controller的单元测试,但是controller需要注入service提供程序,那么就假设service是一个完美的无错误的程序
    getAllCattest: () => ({}),
    create: () => ({}),
    getOneCat: () => ({}),
    getAllCat: () => ({}),
    postCat: () => ({}),
    deleteCat: () => ({}),
    
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [CatsController],
      imports:[
        TypeOrmModule.forRoot({
          type: 'postgres',
          port: 5432,
          username: 'postgres',
          password: '123456',
          database: 'catsdb',
          name:'conn_test',
          entities: [Cat],
          synchronize: true,
          keepConnectionAlive:true,
        }),
        TypeOrmModule.forFeature([Cat],'conn_test'),
      ],
      providers: [CatsService],
    })
      .overrideProvider(CatsService) //在这里负载CatsService
      .useValue(catsService)         //使用我们刚刚mock的完美service
      .compile();

    controller = module.get<CatsController>(CatsController);
    app = module.createNestApplication();
    await app.init();
  });

  it('should be defined', () => {
    expect(controller).toBeDefined();
  });

  it('getAllCat', (done) => {
    request(app.getHttpServer())
    .get('/cats/getCatList')
    .expect(200)
    .end(function (err, res) {
      if (err) {
        return done.fail(err);
      }
      expect(res.body).not.toBeNull();
      done();
    });
});

// for (const item of res0) {
  it('/postCat', done => {
    request(app.getHttpServer())
      .post('/cats/postcat_test')
      .send({
      id:1,
      name:'小猫咪',
      })
      .expect(201)
      .end((error, response) => {
        if (error) {
          return done.fail(error);
        }
        expect(response.body).not.toBeNull();
        done();
      });
  });
// }
  afterAll(async () => {
    await app.close();
  });
pool.end();
});


(2)如果不仅要实现通过测试用例,还要实现测试代码与数据库的交互,建议这里不适用mock的service,而是直接在@Module里将提供程序导入。代码如下:

import { Test, TestingModule } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { INestApplication } from '@nestjs/common';
import { CatsModule } from './cats.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Cat } from '@libs/db/model/cat.model';
import { CatsService } from './cats.service';
import * as request from 'supertest';

describe('Cats Controller', () => {
  let controller: CatsController;
  let app:INestApplication;

  // const catsService = {
  //   getcat:() => ({}),
  //   create:() =>({}),
  // };

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      // controllers: [CatsController],
      imports:[
        CatsModule,
        TypeOrmModule.forRoot({
          type: 'postgres',
          port: 5432,
          username: 'postgres',
          password: '123456',
          database: 'catsdb',
          entities: [Cat],
          synchronize: true,
          keepConnectionAlive:true,
        }),
        TypeOrmModule.forFeature([Cat]),
      ],
      providers:[CatsService],
    })
    // .overrideProvider(CatsService)
    // .useValue(catsService)
    .compile();

    controller = module.get<CatsController>(CatsController);
    app = module.createNestApplication();
    await app.init();
  });

  it('should be defined', () => {
    expect(controller).toBeDefined();
  });
//下面这个测试会将这条数据添加到数据库中
  it('/POST /api/cats', done => {
  return  request(app.getHttpServer())
      .post('/cats/postacat')
      .send({
        id:6,
        name: '小花猫',
        age: 2,
        description: '折耳',
      })
      .expect(201)
      .end((error, response) => {
        if (error) {
          return done.fail(error);
        }
        expect(response.body).not.toBeNull();
        done();
      });
  });
  afterAll(async () => {
    await app.close();
  });

});

(3)考虑到以上代码只是实现了静态数据的测试,现实中每个单元测试都会有很多条测试用例,因此这块考虑获取动态数据,为了在测试时获取动态的测试用例,需要引入一个测试用例的数据库,这里使用pgsql,首先需要安装pg依赖包:
npm install @types/pg

在cats.controller.spec.ts文件中连接测试用例数据库,获取数据,然后进行测试,代码如下:

import { Test, TestingModule } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { INestApplication } from '@nestjs/common';
import { CatsModule } from './cats.module';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Cat } from '@libs/db/model/cat.model';
import { CatsService } from './cats.service';
import * as request from 'supertest';
import { Pool } from 'pg';

describe('Cats Controller', () => {
  let controller: CatsController;
  let app:INestApplication;

  //连接测试用例数据库
  const pool = new Pool({
  host:'localhost',
  port: 5432,
  user: 'postgres',
  password: '123456',
  database: 'catsdb',
  max:20,
  idleTimeoutMillis: 30000,
  connectionTimeoutMillis: 2000,
  });

  beforeAll(async () => {
    const module: TestingModule = await Test.createTestingModule({
      imports:[
        CatsModule,
        TypeOrmModule.forRoot({
          type: 'postgres',
          port: 5432,
          username: 'postgres',
          password: '123456',
          database: 'TestOutput',
          entities: [Cat],
          synchronize: true,
          keepConnectionAlive:true,
        }),
        TypeOrmModule.forFeature([Cat]),
      ],
      providers:[CatsService],
    })
    .compile();

    controller = module.get<CatsController>(CatsController);
    app = module.createNestApplication();
    await app.init();
  });

  it('should be defined', () => {
    expect(controller).toBeDefined();
  });

  it('/POST /api/cats', done => {
    pool.query('select *from cat',(err,result) =>{
      if (err) {
        return console.error('Error executing query', err.stack)
      }
      const  res0 = result.rows;
  for(const item of res0){
    request(app.getHttpServer())
      .post('/cats/postacat')
      .send(item)
      .expect(201)
      .end((error, response) => {
        if (error) {
          return done.fail(error);
        }
        expect(response.body).not.toBeNull();
        done();
      });
  }});
}
)
  afterAll(async () => {
    await app.close();
  });

});

我在这里只使用了pool.query的方法,还有许多其他方法,这里附上官网链接: https://node-postgres.com/api/pool/.

5、对于被测软件来说,一种思路就是穷尽性测试,即考虑其所有的输入组合或者考虑程序所有的执行路径,当这些全部执行无误后,可以认为这款软件是高质量软件,但是这是不现实的。所以测试用例的设计就比较重要, 但是不管是黑盒测试和白盒测试技术,人工设计测试用例工作量都比较大,所以想找一款覆盖面广的自动生成测试用例的工具。还在找。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值