MongooseError - 开玩笑地连接到 mongoosedb NestJS 测试用例时,操作 users.xxx() 缓冲在 10000 毫秒后超时

Posted

技术标签:

【中文标题】MongooseError - 开玩笑地连接到 mongoosedb NestJS 测试用例时,操作 users.xxx() 缓冲在 10000 毫秒后超时【英文标题】:MongooseError - Operation users.xxx() buffering timed out after 10000ms while connecting to mongoosedb NestJS test case in jest 【发布时间】:2021-08-11 18:44:54 【问题描述】:

我正在尝试为与 mongoosedb 交互的服务方法 create 编写测试用例。 Nest 无法连接到数据库,我注意到 MongooseError - Operation users.xxx() buffering timed out after 10000ms 。当我运行应用程序时,我没有看到错误。只有在执行测试用例时才会出现错误。

测试用例

我没有直接连接到mongoose,而是在编译一个模块。

//import statements

describe('user DB Service - Sample', () => 
    let service: DbService;
    let userConnection: Connection;
    const userModel = model('User', UserSchema);
    beforeEach(async () => 
        const module: TestingModule = await Test.createTestingModule(
            imports: [
                MongooseModule.forRoot('mongodb://127.0.0.1:27017/nest?readPreference=primary&appname=MongoDB%20Compass&ssl=false',  connectionName: 'users', useNewUrlParser: true ),
            ],
            providers: [
                DbService,
                
                    provide: getModelToken('User'),
                    useValue: userModel,
                ,
            ],
        ).compile();

        service = module.get<DbService>(DbService);
        userConnection = await module.get(getConnectionToken('users'))
        jest.setTimeout(30000)

    );

    /**
     * create user
     */
    it('Create user with null input should return Invalid Input exception', async () => 
        try 

            let createRequest = 
                // user input object
            
            idConnection.on('connect', () => 
                console.log('connected')
            )
            const result = await service.create(createRequest
            // validate result
         catch (error) 
            console.log('error creating', error)
            // error validation
        
    );




数据库服务

//import statements

@Injectable()
export class DbService 

    constructor(@InjectModel('id') private readonly idModel: Model<IdDocument>) 
    

    create(input: UserCreate) 
        return new Promise((resolve, reject) => 
            try 
                // create, save the doc and return doc to caller
                const userdoc = new this.idModel(input)
                const result = await userdoc.save( validateBeforeSave: true )
                resolve(userdoc)
             catch (error) 
                // send error to caller
                console.log('error while resolving promise' + JSON.stringify(error))
                reject(error)
            
        )
    

【问题讨论】:

不和谐链接discord.com/channels/520622812742811698/606125380817911828/… 【参考方案1】:

您似乎走在正确的轨道上,但是,您的代码存在一些问题,难以测试。

首先,在单元测试时无需担心数据库连接。如果您只需要确保您的服务方法调用模型方法,那么您可以执行以下操作:

服务


@Injectable()
export class DbService  // consider naming this UserService

  constructor(@InjectModel('id') private readonly idModel: Model) 

  async create(input: UserCreate) 
    try 
      const userdoc = new this.idModel(input);
      return userdoc.save()
     catch (error) 
      console.log('error while resolving promise' + JSON.stringify(error));
      throw error;
    
  

更好的是,使用异常过滤器将服务的关注点分开,并将错误处理从服务中推开:

异常过滤器

服务:


@Injectable()
export class DbService  // consider naming this UserService

  constructor(@InjectModel('id') private readonly idModel: Model) 

  async create(input: UserCreate) 
    return new this.idModel(input).save();
  

过滤器:


@Catch(MongooseValidationError)
export class ValidationExceptionFilter extends BaseExceptionFilter 
  catch (exception: MongooseValidationError, host: ArgumentsHost) 
    const customError = new YourCustomError('Invalid!', 'you gave me invalid details!');
    super.catch(customError, host)
  

然后,调整您的单元测试以使用模拟而不是真实的东西。您的集成 (e2e) 测试应确保使用真实的数据库。

测试


//import statements

const data = 
  // ... a mock object


class mockModel 

     constructor(public data?: any) 

     save() 
         return this.data;
     

     static findOne( _id ) 
         return data;
     



describe('user DB Service - Sample', () => 
    let service: DbService;
    let model: Model;
    beforeEach(async () => 
        const module: TestingModule = await Test.createTestingModule(
            providers: [
                DbService,
                
                    provide: getModelToken('User'),
                    useClass: mockModel // if it throws error use useValue
                ,
            ],
        ).compile();


        service = module.get(DbService);
        model = module.get(getModelToken('User')); // add your types
    );

    /**
     * create user
     */
    it('Create user with null input should return Invalid Input exception', async () => 
      // the failing request
      const createRequest = 
        // ...
      

      // provide the response you want (ValidationError for example)
      jest.spyOn(model, 'save').mockImplementationOnce(jest.fn(async () => 
        throw new Error(); // throw ValidationError or whatever error mongoose would throw
      ));

      expect(service.create(createRequest)).rejects.toThrow(); // validate error is thrown

      // ... validate method is called
      expect(model.save).toHaveBeenCalled();
      expect(model.save).toHaveBeenCalledWith(
        validateOnSave: true
      );
    );

    it('Create user with good input should return User', async () => 
      // the good request
      const createRequest = 
        // ...
      ;

      // provide the response you want (ValidationError for example)
      jest.spyOn(model, 'save').mockImplementationOnce(
        jest.fn(async () => data)
      );

      const result = await service.create(createRequest);
      expect(result).toStrictEqual(data); // or whatever mock object you provide

      // ... validate method is called
      expect(model.save).toHaveBeenCalled();
      expect(model.save).toHaveBeenCalledWith(
        validateOnSave: true,
      );
    );
);

了解详情

    https://docs.nestjs.com/exception-filters https://docs.nestjs.com/pipes#the-built-in-validationpipe(推荐这个,验证完全远离数据库,那么数据库验证更像是一种健全性检查) https://docs.nestjs.com/fundamentals/testing#unit-testing https://docs.nestjs.com/fundamentals/testing#end-to-end-testing

注意

重新添加任何被剥离的类型。

【讨论】:

感谢您的详细建议,我也在考虑使用集成测试进行真正的数据库连接。我将重新访问/检查其他可用的嵌套选项。

以上是关于MongooseError - 开玩笑地连接到 mongoosedb NestJS 测试用例时,操作 users.xxx() 缓冲在 10000 毫秒后超时的主要内容,如果未能解决你的问题,请参考以下文章