How does one unit test routes with Express?

后端 未结 8 2157
一整个雨季
一整个雨季 2020-12-07 08:38

I\'m in the process of learning Node.js and have been playing around with Express. Really like the framework;however, I\'m having trouble figuring out how to write a unit/i

相关标签:
8条回答
  • 2020-12-07 09:26

    I was wondering this as well, but specifically for unit tests and not integration tests. This is what I'm doing right now,

    test('/api base path', function onTest(t) {
      t.plan(1);
    
      var path = routerObj.path;
    
      t.equals(path, '/api');
    });
    
    
    test('Subrouters loaded', function onTest(t) {
      t.plan(1);
    
      var router = routerObj.router;
    
      t.equals(router.stack.length, 5);
    });
    

    Where the routerObj is just {router: expressRouter, path: '/api'}. I then load in subrouters with var loginRouterInfo = require('./login')(express.Router({mergeParams: true})); and then the express app calls an init-function taking in the express router as a parameter. The initRouter then calls router.use(loginRouterInfo.path, loginRouterInfo.router); to mount the subrouter.

    The subrouter can be tested with:

    var test = require('tape');
    var routerInit = require('../login');
    var express = require('express');
    var routerObj = routerInit(express.Router());
    
    test('/login base path', function onTest(t) {
      t.plan(1);
    
      var path = routerObj.path;
    
      t.equals(path, '/login');
    });
    
    
    test('GET /', function onTest(t) {
      t.plan(2);
    
      var route = routerObj.router.stack[0].route;
    
      var routeGetMethod = route.methods.get;
      t.equals(routeGetMethod, true);
    
      var routePath = route.path;
      t.equals(routePath, '/');
    });
    
    0 讨论(0)
  • 2020-12-07 09:30

    Change your response object:

    var response = {
        viewName: ""
        , data : {}
        , render: function(view, viewData) {
            this.viewName = view;
            this.data = viewData;
        }
    };
    

    And it will work.

    0 讨论(0)
  • 2020-12-07 09:31

    To achieve unit testing instead of integration testing, I mocked the response object of the request handler.

    /* app.js */
    import endpointHandler from './endpointHandler';
    // ...
    app.post('/endpoint', endpointHandler);
    // ...
    
    /* endpointHandler.js */
    const endpointHandler = (req, res) => {
      try {
        const { username, location } = req.body;
    
        if (!(username && location)) {
          throw ({ status: 400, message: 'Missing parameters' });
        }
    
        res.status(200).json({
          location,
          user,
          message: 'Thanks for sharing your location with me.',
        });
      } catch (error) {
        console.error(error);
        res.status(error.status).send(error.message);
      }
    };
    
    export default endpointHandler;
    
    /* response.mock.js */
    import { EventEmitter } from 'events';
    
    class Response extends EventEmitter {
      private resStatus;
    
      json(response, status) {
        this.send(response, status);
      }
    
      send(response, status) {
        this.emit('response', {
          response,
          status: this.resStatus || status,
        });
      }
    
      status(status) {
        this.resStatus = status;
        return this;
      }
    }
    
    export default Response;
    
    /* endpointHandler.test.js */
    import Response from './response.mock';
    import endpointHandler from './endpointHander';
    
    describe('endpoint handler test suite', () => {
      it('should fail on empty body', (done) => {
        const res = new Response();
    
        res.on('response', (response) => {
          expect(response.status).toBe(400);
          done();
        });
    
        endpointHandler({ body: {} }, res);
      });
    });
    

    Then, to achieve integration testing, you can mock your endpointHandler and call the endpoint with supertest.

    0 讨论(0)
  • 2020-12-07 09:34

    In my case the only I wanted to test is if the right handler has been called. I wanted to use supertest to laverage the simplicity of making the requests to the routing middleware. I am using Typescript a and this is the solution that worked for me

    // ProductController.ts
    
    import { Request, Response } from "express";
    
    class ProductController {
      getAll(req: Request, res: Response): void {
        console.log("this has not been implemented yet");
      }
    }
    export default ProductController
    

    The routes

    // routes.ts
    import ProductController  from "./ProductController"
    
    const app = express();
    const productController = new ProductController();
    app.get("/product", productController.getAll);
    

    The tests

    // routes.test.ts
    
    import request from "supertest";
    import { Request, Response } from "express";
    
    const mockGetAll = jest
      .fn()
      .mockImplementation((req: Request, res: Response) => {
        res.send({ value: "Hello visitor from the future" });
      });
    
    jest.doMock("./ProductController", () => {
      return jest.fn().mockImplementation(() => {
        return {
          getAll: mockGetAll,
    
        };
      });
    });
    
    import app from "./routes";
    
    describe("Routes", () => {
      beforeEach(() => {
        mockGetAll.mockImplementation((req: Request, res: Response) => {
          res.send({ value: "You can also change the implementation" });
        });
      });
    
      it("GET /product integration test", async () => {
        const result = await request(app).get("/product");
    
        expect(mockGetAll).toHaveBeenCalledTimes(1);
    
      });
    
    
    
      it("GET an undefined route should return status 404", async () => {
        const response = await request(app).get("/random");
        expect(response.status).toBe(404);
      });
    });
    
    
    

    I had some issues to get the mocking to work. but using jest.doMock and the specific order you see in the example makes it work.

    0 讨论(0)
  • 2020-12-07 09:35

    As others have recommended in comments, it looks like the canonical way to test Express controllers is through supertest.

    An example test might look like this:

    describe('GET /users', function(){
      it('respond with json', function(done){
        request(app)
          .get('/users')
          .set('Accept', 'application/json')
          .expect(200)
          .end(function(err, res){
            if (err) return done(err);
            done()
          });
      })
    });
    

    Upside: you can test your entire stack in one go.

    Downside: it feels and acts a bit like integration testing.

    0 讨论(0)
  • 2020-12-07 09:36

    if unit testing with express 4 note this example from gjohnson :

    var express = require('express');
    var request = require('supertest');
    var app = express();
    var router = express.Router();
    router.get('/user', function(req, res){
      res.send(200, { name: 'tobi' });
    });
    app.use(router);
    request(app)
      .get('/user')
      .expect('Content-Type', /json/)
      .expect('Content-Length', '15')
      .expect(200)
      .end(function(err, res){
        if (err) throw err;
      });
    
    0 讨论(0)
提交回复
热议问题