Создание заглушки для связывания моделей с ассоциацией - PullRequest
0 голосов
/ 09 марта 2020

Я использую mocha и chai для написания теста для API-интерфейсов RESTful
Я прочитал несколько статей, в которых люди предлагают создавать заглушки для запросов, и вам не следует создавать запрос к базе данных.
Но как мне убедиться, что он работает?
См. Ниже контроллер.

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, чтобы вернуть данные в указанной c форме. Если этого не произойдет, то все не будет работать.

Может кто-нибудь подсказать, как я могу создать заглушки для такого рода функций, чтобы структура также сохранялась?

Ниже приведен тест, который я написал, но он использует реальные вызовы БД.

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
            })   
    })
})

1 Ответ

0 голосов
/ 09 марта 2020

Один из способов сделать это - пометить методы из ваших моделей, то есть Location.findOne и Item.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);
}

Таким образом, вы отсоедините свою бизнес-логику c от контроллера и вам будет намного проще сменить запрос: вы просто измените предоставленные методы на queryService.

. Вы можете найти интересное прочтение этого поста в блоге: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html

...