为带有关联的模型创建存根

问题描述 投票:0回答:1

我正在使用mochachai编写RESTful API的测试我读过一些文章,有人建议为查询创建存根,而您实际上不应该进行数据库查询。但是我如何确定它是否有效?参见下面的控制器。

const Op = require('sequelize').Op
//Models
const {
    Item,
    Location,
    Combo,
    Service,
    ComboItem,
    ItemLocation
} = require('../models')

const _ = require('lodash')
//Services
const paginate = require('../services/PaginationService')







const getAllItems = async function(req, res) {
    if(req.query.location_id){
        let items
        const item = await Location.findOne({
            where: {
                id: 1
            },
            include: {
                model: Item,
                through: {
                    model: ItemLocation,
                    attributes: []
                },
                as: 'itemsAtLocation',
                include: [
                    {
                        model: Service,
                        as: 'service',
                        attributes: ["id"]

                    }, 
                    {
                        model: Combo,
                        as: 'combo',
                        attributes: ["start_date", "expiry_date"]
                    }
                ]
            }
        })
        if(!item)
            return res.status(200).send({
                status: true,
                message: "No item found at location!",
                data: {}
            })

        items = item.itemsAtLocation
        let data = {}
        data.services = []
        data.combos   = []
        _.forEach(items, item => {
            let itemData = {
                id: item.id,
                name: item.name,
                price: item.price,
                discount_per: item.discount_per,
            }
            if(item.service) 
                data.services.push(itemData)
            if(item.combo) {
                itemData.start_date = item.combo.start_date
                itemData.expiry_date = item.combo.expiry_date
                data.combos.push(itemData)
            }     
        })
        return res.status(200).send({
            status: true,
            message: "Successfully fetch all items!",
            data: data
        })
    } else {
        const items = await Item.findAll({
            include: [
                {
                    model: Service,
                    as: 'service',
                    attributes: ["id"]

                }, 
                {
                    model: Combo,
                    as: 'combo',
                    attributes: ["start_date", "expiry_date"]
                }
            ],
            attributes: ["id", "name", "price", "discount_per", "description"],
            ...paginate(+req.query.page, +req.query.per_page)
        })
        let data = {}
        data.services = []
        data.combos   = []
        _.forEach(items, item => {
            let itemData = {
                id: item.id,
                name: item.name,
                price: item.price,
                discount_per: item.discount_per,
            }
            if(item.service) 
                data.services.push(itemData)
            if(item.combo) {
                itemData.start_date = item.combo.start_date
                itemData.expiry_date = item.combo.expiry_date
                data.combos.push(itemData)
            }     
        })
        return res.status(200).send({
            status: true,
            message: "Successfully fetch all items!",
            data: data
        })
    }

}

module.exports = {
    getAllItems
}

您可以从上面的代码中看到。我需要queries以特定形式返回数据。如果不采用这种形式,则将无法正常工作。有人可以建议如何为此类功能创建存根,以便也保留结构吗?下面是我编写的测试,但是它使用实际的数据库调用。

describe('GET /api/v1/items', function () {
    it('should fetch all items orgianized by their type', async () => {
        const result = await request(app)
            .get('/api/v1/items')
            .set('Accept', 'application/json')
            .expect('Content-Type', /json/)
            .expect(200)
        expect(result)
            .to.be.a('Object')
        expect(result.body.status)
            .to.be.a('Boolean').true
        expect(result.body.data, "data should be an Object and every key should an Array")
            .to.satisfy(data => {
                expect(data).to.be.a('Object')
                .to.not.be.null
                if(!_.isEmpty(data)) {
                    expect(data).to.have.any.keys('services', 'combos')  
                    _.forOwn(data, (value, key) => {
                        expect(data[key]).to.be.a('Array')
                     })
                    return true
                }
                return true
            })   
    })
})
node.js sequelize.js mocha chai sinon
1个回答
0
投票

您可以通过在模型中存入方法Location.findOneItem.findAll来做到这一点。所以您的测试可能看起来像下面的代码:

  const sinon = require('sinon');
  const Location = require('../models/location'); // Get your location model
  const Item = require('../models/item'); // Get your item model

  describe('myTest', () => {
    let findOneLocationStub;
    let findAllItemsStub;

    beforeEach(() => {
      findOneLocationStub = sinon.stub(Location, 'findOne');
      findAllItemsStub = sinon.stub(Item, 'findAll');
    });

    afterEach(() => {
      findOneLocationStub.verifyAndRestore();
      findAllItemsStub.verifyAndRestore();
    });

    it('returns 200 when location not found', () => {
      findOneLocationStub.resolves(null);

      expects...
    });
  });

我没有进行测试,但类似的方法应该可以工作。但是请注意,我必须将模型拆分到自己的文件中以进行存根。可能有一种方法可以使用您当前的实现来执行相同操作。

我建议的另一件事是在您的方法中使用某种用例来负责数据库的实现。类似于:

   const getAllItemsUseCase = (params, queryService) => {
    if(params.locationId){
        let items
        const item = await queryService.findOneLocation({
   };

因此,当您从控制器调用此方法时,可以调用:

const getAllItems = async function(req, res) {
  const params = {
    locationId: req.query.location_id,
    // and more parameters
  };

  const queryService = {
    findOneLocation: Location.findOne,
  };
  const results = await getAllItemsUseCase(params, queryService);
}

通过这种方式,您将业务逻辑从控制器中分离出来,并且可以轻松得多地模拟查询:您只需更改提供给queryService的方法。

您可以从此博客文章中找到一些有趣的读物:https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

© www.soinside.com 2019 - 2024. All rights reserved.