Насмешливый запрос и результат для проведения наименьшего возможного модульного теста

#node.js #express #unit-testing #chai #sinon

#node.js #экспресс #модульное тестирование #чай #sinon

Вопрос:

я создаю приложения для обратной связи с помощью Express. Он вызывается исключительно спереди по маршрутам, затем вызывает внешний API, чтобы вернуть результат. Вот пример кода логики :

панель мониторинга.маршрут.ts

 const router = Router(); const dashboardController = new DashboardController();  router.get("/distantCall", dashboardController.getDistantCall);  

панель управления.контроллер.ts

 import { Request, Response, NextFunction } from "express"; import DashboardService from "../services/dashboard.service";  export class DashboardController {  async getDistantCall(req: Request, res: Response, next: NextFunction) {  DashboardService.getDistantCalls()  .then((result: any) =gt; {  res.status(200).send(result);  }).catch((error: any) =gt; {  next(error);  });  } }  

dashboard.service.ts

 import { DashboardApi } from './dashboard.api';  class DashboardService {  public async getDistantCall() {  return new Promise((resolve, reject) =gt; {  new DashboardApi().getDistantCall()  .then((response: any) =gt; {  resolve({  distantResponse: response.body  });  })  .catch((error) =gt; {  reject(error);  });  });  }  

Класс DashboardAPI выполняет внешний http-вызов и возвращает обещание. Для этого примера он возвращает простой текст «distantSuccess».

Для своих тестов я довольно легко могу писать интеграционные тесты

панель мониторинга.маршруты.спецификации.ts

 import chai from "chai"; import chaiHttp from "chai-http"; import { expect } from "chai"; chai.use(chaiHttp);  import createServer from "../../src/server"; const app = createServer();  describe("dashboard routes", function() {   it('nominal distant call', async () =gt; {  const res = await chai.request(app).get("/dashboard/distantCall");  expect(res.status).to.eq(200);  expect(res.body).to.be.a('object');  expect(res.body).to.have.property('distantResponse');  expect(res.body.distantResponse).to.eq('distantSuccess');  }); });  

My problem is building unit tests. As I understand it, I should only test the controller or the service, and using mocks amp; stubs to simulate the elements outside of the scope. Here are the two tests I made :

dashboard.controller.spec.ts

 import { Request, Response, NextFunction } from "express"; import chai from "chai"; import chaiHttp from "chai-http"; import { expect } from "chai"; import sinon from "sinon"; chai.use(chaiHttp);  import createServer from "../../src/server"; const app = createServer(); import { DashboardController } from "../../src/controllers/dashboard.controller"; const dashboardController = new DashboardController(); import DashboardService from "../../src/services/dashboard.service";  describe("dashboard routes with fake objects", function () {  it("distant call by controller", async () =gt; {  const mockRequest: any = {  headers: {},  body: {},  };  const mockResponse: any = {  body: { distantResponse: "About..." },  text: "test",  status: 200,  };  const mockNext: NextFunction = () =gt; {};   await dashboardController.getDistantCallSucces(mockRequest, mockResponse, mockNext);   expect(mockResponse.status).to.eq(200);  expect(mockResponse.body).to.be.a("object");  expect(mockResponse.body).to.have.property("distantResponse");  expect(mockResponse.body.distantResponse).to.eq("About...");  }); });  describe("dashboard routes with stubs", function () {  before(() =gt; {  sinon  .stub(DashboardService, "getDistantCall")  .yields({ distantResponse: "distantSuccess" });  });   it("distant call by controller", async () =gt; {  const mockRequest: any = {};  const mockResponse: any = {};  const mockNext: NextFunction = () =gt; {};   const res = await dashboardController.getDistantCall(mockRequest, mockResponse, mockNext);  console.log(res);  }); });  

Для первого теста я явно не понимаю, как его использовать. я тестирую только что созданный объект, даже не зная, вызывается ли служба. Я чувствую, что должен сделать что-то более похожее на второй тест, но я получаю эту ошибку : Ошибка типа: ожидалось, что getDistantCall даст результат, но обратный вызов не был передан.

Ответ №1:

Я наконец нашел решение.

Я создал два отдельных файла : один для интеграционного тестирования, другой для модульного тестирования. Я немного изменил ответ с удаленного сервера, который теперь «с удаленного сервера» вместо «distantResponse», установленного в предыдущем сообщении. В контроллере и службе я также изменил getDistantCall на две разные функции getDistantCallSucces и getDistantCallError, чтобы принудительно разрешить и отклонить интеграционные тесты.

панель мониторинга-интеграция.спецификация.ts

 import chai, { expect } from "chai"; import chaiHttp from "chai-http"; chai.use(chaiHttp); import sinon from "sinon";  import app from "../src/app"; import { DashboardAPI } from '../src/dashboard/dashboard.api';  describe("Dashboard intagration tests", () =gt; {   describe("Integration with server online", () =gt; {  it('Dashboard routes up', async () =gt; {  const res = await chai.request(app).get("/dashboard");  expect(res.status).to.eq(200);  expect(res.text).to.eq('dashboard');  });  it('full process with expected success', async () =gt; {  const res = await chai.request(app).get("/dashboard/distantCallSuccess").set("code", "12345");  expect(res.status).to.eq(200);  expect(res.body).to.be.a('object');  expect(res.body).to.have.property('distantResponse');  expect(res.body.distantResponse).to.eq('from distant server');  });  it('full process with expected error', async () =gt; {  const res = await chai.request(app).get("/dashboard/distantCallError");  expect(res.status).to.eq(500);  });  });   describe("Integration with mocked server", () =gt; {  beforeEach(() =gt; {  sinon.restore();  });  it('full process with expected resolve', async () =gt; {  const mockedResponse = {body: 'mocked'};  sinon.stub(DashboardAPI.prototype, 'getDistantCallSuccess').resolves(mockedResponse);  const res = await chai.request(app).get("/dashboard/distantCallSuccess").set("code", "12345");  expect(res.status).to.eq(200);  expect(res.body).to.be.a('object');  expect(res.body).to.have.property('distantResponse');  expect(res.body.distantResponse).to.eq('mocked');  });  it('full process with expected reject', async () =gt; {  sinon.stub(DashboardAPI.prototype, 'getDistantCallSuccess').rejects({mockedError: true});  const res = await chai.request(app).get("/dashboard/distantCallSuccess").set("code", "12345");  expect(res.status).to.eq(500);  });  }); });  

приборная панель-unit.spec.ts

Мне пришлось использовать node-mocks-http для имитации объектов запроса и ответа

 import chai, { expect } from "chai"; import chaiHttp from "chai-http"; import sinon from "sinon"; import httpMocks from "node-mocks-http"; chai.use(chaiHttp);  import { DashboardController } from "../src/dashboard/dashboard.controller"; import DashboardService from "../src/dashboard/dashboard.service"; import { DashboardAPI } from '../src/dashboard/dashboard.api';  describe("Unit Testing the Dashboard process", () =gt; {  describe("Unit Testing the controller", () =gt; {  const dashboardController = new DashboardController();  beforeEach(() =gt; {  sinon.restore();  });  it("testing controller call without headers [catch]", async () =gt; {  var request = httpMocks.createRequest({});  var response = httpMocks.createResponse();  const next = () =gt; {};    await dashboardController.getDistantCallSuccess(request, response, next);   expect(response._getStatusCode()).to.eq(500);  expect(response._getData()).to.eq("Missing HEADER Parameter");  });  it("testing controller call with headers [resolve]", async () =gt; {  const mockedResponse = {  mockedResponse: true  };  sinon.stub(DashboardService, 'getDistantCallSuccess').resolves(mockedResponse);  var request = httpMocks.createRequest({  headers: { code: "123" }  });  var response = httpMocks.createResponse();  const next = () =gt; {};    await dashboardController.getDistantCallSuccess(request, response, next);  expect(response._getStatusCode()).to.eq(200);  expect(response._getData()).to.eql({mockedResponse: true});  });  it("testing controller call with headers [reject]", async () =gt; {  sinon.stub(DashboardService, 'getDistantCallSuccess').rejects({customError: true});    const request = httpMocks.createRequest({});  const response = httpMocks.createResponse();  const next = (res) =gt; {  expect(res).to.eql({customError: true});  };  await dashboardController.getDistantCallError(request, response, next);  });  });    describe("Unit Testing the service", () =gt; {  beforeEach(() =gt; {  sinon.restore();  });  it("testing service call with resolve", async() =gt; {  const mockedResponse = {  body: 'mocked'  };  sinon.stub(DashboardAPI.prototype, 'getDistantCallSuccess').resolves(mockedResponse);    let result;  await DashboardService.getDistantCallSuccess().then(res =gt; {  result = res;  });  expect(result).to.be.a('object');  expect(result).to.be.haveOwnProperty('distantResponse');  expect(result.distantResponse).to.eq('mocked');  });  it("testing service call with reject", async() =gt; {  sinon.stub(DashboardAPI.prototype, 'getDistantCallSuccess').rejects({mockedError: true});    let result;  await DashboardService.getDistantCallSuccess()  .then(res =gt; {  result = res;  })  .catch(err =gt; {  result = err;  });  expect(result).to.eql({mockedError: true});  });  }); });