Тестовый пример js express js API узла с поддельными запросами и данными ответа - PullRequest
0 голосов
/ 04 ноября 2019

Я работаю над узлом записи js express js API, тестовый пример с использованием jasmine, но у меня есть одна проблема с моим тестовым примером, он работает только с подключением к действующей базе данных и возвращает фактическое значение из базы данных, поэтому в будущем данные могут измениться так,в это время контрольный пример не пройдёт.

Итак, я хочу создать макет моего контроллера и модель запроса к базе данных

Вот мой код контроллера

module.exports.updateProductStatus = async (req, res, next) => {
    let result = await apiModel.updateProductStatus(req.body);
    if (result.affectedRows > 0) {
        res.send({
            status: "success",
            message: "product status updated."
        })
    }
    else {
        res.send({
            status: "error",
            message: "failed to update product status."
        })
    }
};

и здесьмой модуль запросов к базе данных MySQL

module.exports.updateProductStatus = async (data) => {
    let updateData = {
        STATUS : data.status,
        UPDATED_TIMESTAMP : moment().unix()
    }
    let result = await db.query('UPDATE products SET ? WHERE PRODUCT_ID = ?',[updateData,data.product_id]).catch(handleError);
    return result[0];
};

Вот мой тестовый пример для вызова API и отправки данных запроса

it("should update product status", () => {
            var reqData = {
                "account_name" : "demo",
                "last_update": 1571298222,
                "product_detail": {
                    "QUENTITY":"36","PRICE":"5.69527","TITLE":"AAAAAAAAA","SKU":"A10512","UPC":""
                },
                "sku": "A10512AAA",
                "status": "inactive",
                "product_id": 1
            };
            request.post(
                {
                    url: 'localhost:9090/api/update-product-status',
                    headers: {
                        'authorization': 'Bearer eyJhbGciOiJIUzI1NiddIsInR5cCIdd'
                    },
                    form: reqData
                }, function (err, httpResponse, body) {
                    console.log("TCL: err", err);
                    console.log("TCL: body", body);
                    console.log("TCL: httpResponse", httpResponse);
                });
        });

, пожалуйста, укажите, как получить ответ без подключения базы данных MySQL при запускеконтрольный пример, если есть библиотека NPM для макета, дайте мне знать событие лучше, если вы предоставите пример кода

спасибо

1 Ответ

1 голос
/ 06 ноября 2019

Похоже, вы проводите интеграционные тесты, а это значит, что ваши тесты должны зависеть от реальной базы данных и других внешних сервисов и ресурсов.

Прежде чем мы проведем интеграционные тесты, мы можем написать модульные тесты. Вот модульные тесты, основанные на вашем коде:

структура папок:

.
├── api.js
├── api.spec.js
├── controller.js
├── controller.spec.js
└── db.js

api.js:

const moment = require('moment');
const db = require('./db');

module.exports.updateProductStatus = async data => {
  let updateData = {
    STATUS: data.status,
    UPDATED_TIMESTAMP: moment().unix()
  };
  let result = await db
    .query('UPDATE products SET ? WHERE PRODUCT_ID = ?', [updateData, data.product_id])
    .catch(handleError);
  return result[0];
};

function handleError(error) {
  console.error(error);
}

db.js:

module.exports = {
  async query() {
    return 'real query';
  }
};

controller.js:

const apiModel = require('./api');

module.exports.updateProductStatus = async (req, res, next) => {
  let result = await apiModel.updateProductStatus(req.body);
  if (result.affectedRows > 0) {
    res.send({
      status: 'success',
      message: 'product status updated.'
    });
  } else {
    res.send({
      status: 'error',
      message: 'failed to update product status.'
    });
  }
};

Юнит-тесты:

controller.spec.js:

const controller = require('./controller');
const apiModel = require('./api');

describe('updateProductStatus controller', () => {
  const mReq = { body: {} };
  const mRes = { send: jest.fn() };
  beforeEach(() => {
    jest.restoreAllMocks();
  });
  test('should send success response', async () => {
    const mResult = { affectedRows: 2 };
    const updateProductStatusSpy = jest.spyOn(apiModel, 'updateProductStatus').mockResolvedValueOnce(mResult);
    await controller.updateProductStatus(mReq, mRes);
    expect(mRes.send).toBeCalledWith({ status: 'success', message: 'product status updated.' });
    expect(updateProductStatusSpy).toBeCalledWith(mReq.body);
  });

  test('should send error response', async () => {
    const mResult = { affectedRows: 0 };
    const updateProductStatusSpy = jest.spyOn(apiModel, 'updateProductStatus').mockResolvedValueOnce(mResult);
    await controller.updateProductStatus(mReq, mRes);
    expect(mRes.send).toBeCalledWith({ status: 'error', message: 'failed to update product status.' });
    expect(updateProductStatusSpy).toBeCalledWith(mReq.body);
  });
});

api.spec.js:

const apiModel = require('./api');
const db = require('./db');

describe('api', () => {
  beforeEach(() => {
    jest.restoreAllMocks();
  });
  test('should update product status', async () => {
    const mData = { product_id: 1, status: 'no store' };
    const mResult = [{ affectedRows: 1 }];
    const querySpy = jest.spyOn(db, 'query').mockResolvedValueOnce(mResult);
    const actualValue = await apiModel.updateProductStatus(mData);
    expect(actualValue).toEqual(mResult[0]);
    expect(querySpy).toBeCalledWith('UPDATE products SET ? WHERE PRODUCT_ID = ?', [
      { STATUS: 'no store', UPDATED_TIMESTAMP: expect.any(Number) },
      1
    ]);
  });

  test('should handle error', async () => {
    const mData = { product_id: 1, status: 'no store' };
    const mError = new Error('connection error');
    const querySpy = jest.spyOn(db, 'query').mockRejectedValueOnce(mError);
    const errorSpy = jest.spyOn(console, 'error');
    await expect(apiModel.updateProductStatus(mData)).rejects.toThrowError(
      new TypeError("Cannot read property '0' of undefined")
    );
    expect(querySpy).toBeCalledWith('UPDATE products SET ? WHERE PRODUCT_ID = ?', [
      { STATUS: 'no store', UPDATED_TIMESTAMP: expect.any(Number) },
      1
    ]);
    expect(errorSpy).toBeCalledWith(mError);
  });
});

Результат модульного теста для controller.spec.js:

 PASS  src/stackoverflow/58692161/controller.spec.js
  updateProductStatus controller
    ✓ should send success response (10ms)
    ✓ should send error response (2ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.792s, estimated 3s

Результат модульного теста для api.spec.js:

 PASS  src/stackoverflow/58692161/api.spec.js
  api
    ✓ should update product status (14ms)
    ✓ should handle error (13ms)

  console.error node_modules/jest-mock/build/index.js:860
    Error: connection error
        at /Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:22:20
        at step (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:33:23)
        at Object.next (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:14:53)
        at /Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:8:71
        at new Promise (<anonymous>)
        at Object.<anonymous>.__awaiter (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:4:12)
        at Object.<anonymous> (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/src/stackoverflow/58692161/api.spec.js:20:31)
        at Object.asyncJestTest (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/jasmineAsyncInstall.js:102:37)
        at resolve (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:43:12)
        at new Promise (<anonymous>)
        at mapper (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:26:19)
        at promise.then (/Users/elsa/workspace/github.com/mrdulin/jest-codelab/node_modules/jest-jasmine2/build/queueRunner.js:73:41)
        at <anonymous>
        at process._tickCallback (internal/process/next_tick.js:188:7)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.506s

Исходный код: https://github.com/mrdulin/jest-codelab/tree/master/src/stackoverflow/58692161

...