एक्सप्रेस के साथ एक इकाई कैसे परीक्षण करती है?


99

मैं Node.js सीखने की प्रक्रिया में हूं और एक्सप्रेस के साथ खेल रहा हूं । वास्तव में रूपरेखा की तरह; हालांकि, मुझे यह पता लगाने में कठिनाई हो रही है कि किसी रूट के लिए यूनिट / इंटीग्रेशन टेस्ट कैसे लिखें।

इकाई परीक्षण सरल मॉड्यूल के लिए सक्षम होना आसान है और मोचा के साथ कर रहा है ; हालाँकि, एक्सप्रेस के साथ मेरा यूनिट परीक्षण विफल रहता है क्योंकि मैं जिस प्रतिक्रिया ऑब्जेक्ट से गुजर रहा हूं वह मूल्यों को बनाए नहीं रखता है।

रूट-फंक्शन अंडर टेस्ट (मार्गों / index.js):

exports.index = function(req, res){
  res.render('index', { title: 'Express' })
};

यूनिट टेस्ट मॉड्यूल:

var should = require("should")
    , routes = require("../routes");

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

describe("Routing", function(){
    describe("Default Route", function(){
        it("should provide the a title and the index view name", function(){
        routes.index(request, response);
        response.viewName.should.equal("index");
        });

    });
});

जब मैं इसे चलाता हूं, तो यह "त्रुटि: वैश्विक लीक का पता लगाया: दृश्यनाम, डेटा" के लिए विफल रहता है।

  1. मैं कहां गलत हो रहा हूं ताकि मुझे यह काम मिल सके?

  2. क्या मेरे लिए इस स्तर पर अपने कोड का परीक्षण करने का एक बेहतर तरीका है?

अद्यतन 1. सही कोड स्निपेट के बाद से मैं शुरू में इसे "भूल गया" ()।

जवाबों:


21

अपनी प्रतिक्रिया वस्तु बदलें:

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

और यह काम करेगा।


2
यह यूनिट एक अनुरोध हैंडलर का परीक्षण कर रही है, एक मार्ग नहीं।
जेसन सेब्रिंग

43

जैसा कि अन्य लोगों ने टिप्पणियों में सिफारिश की है, यह सुपर नियंत्रक के माध्यम से एक्सप्रेस नियंत्रकों का परीक्षण करने के लिए विहित तरीके से दिखता है

एक उदाहरण परीक्षण इस तरह लग सकता है:

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

अपसाइड: आप एक बार में अपने पूरे स्टैक का परीक्षण कर सकते हैं।

डाउनसाइड: यह एकीकरण परीक्षण की तरह महसूस करता है और कार्य करता है।


1
मुझे यह पसंद है, लेकिन क्या दृश्यनाम का पता लगाने का एक तरीका है (जैसा मूल प्रश्न है) - या क्या हमें प्रतिक्रिया की सामग्री पर जोर देना होगा?
एलेक्स

20
मैं आपके नकारात्मक पक्ष से सहमत हूँ, यह इकाई परीक्षण नहीं है। यह आपके एप्लिकेशन के urls का परीक्षण करने के लिए आपकी सभी इकाइयों के एकीकरण पर निर्भर करता है।
ल्यूक एच

10
मुझे लगता है कि यह कहना कानूनी है कि एक "मार्ग" वास्तव में एक है integration, और शायद परीक्षण मार्गों को एकीकरण परीक्षणों के लिए छोड़ दिया जाना चाहिए। मेरा मतलब है, उनके परिभाषित कॉलबैक से मेल खाते मार्गों की कार्यक्षमता संभवत: पहले से ही व्यक्त की गई है। js; किसी मार्ग का अंतिम परिणाम प्राप्त करने के लिए किसी भी आंतरिक तर्क को आदर्श रूप से इसके बाहर संशोधित किया जाना चाहिए, और उन मॉड्यूल को इकाई परीक्षण किया जाना चाहिए। उनके अंतःक्रिया, अर्थात, मार्ग का एकीकरण परीक्षण होना चाहिए। क्या आप सहमत हैं?
आदित्य एमपी

1
यह एंड-टू-एंड टेस्टिंग है। इसमें कोई शक नहीं।
किलोप्रोड्यूसर

23

मैं इस निष्कर्ष पर पहुंचा हूं कि वास्तव में इकाई परीक्षण एक्सप्रेस अनुप्रयोगों का एकमात्र तरीका अनुरोध संचालकों और आपके मुख्य तर्क के बीच बहुत अधिक अलगाव बनाए रखना है।

इस प्रकार, आपका आवेदन तर्क अलग मॉड्यूलों में होना चाहिए जो requireडी और यूनिट परीक्षण किया जा सकता है, और एक्सप्रेस अनुरोध और प्रतिक्रिया कक्षाओं पर न्यूनतम निर्भरता है।

फिर अनुरोध संचालकों में आपको अपने मुख्य तर्क वर्गों के उपयुक्त तरीकों को कॉल करने की आवश्यकता है।

एक बार जब मैंने अपना वर्तमान ऐप पुनर्गठन कर लिया, तो मैं एक उदाहरण रखूँगा!

मैं कुछ इस तरह लगता है ? (जिस्ट या टिप्पणी को कांटा करने के लिए स्वतंत्र महसूस करें, मैं अभी भी इसे खोज रहा हूं)।

संपादित करें

यहाँ एक छोटा सा उदाहरण है, इनलाइन। अधिक विस्तृत उदाहरण के लिए जिस्ट देखें ।

/// usercontroller.js
var UserController = {
   _database: null,
   setDatabase: function(db) { this._database = db; },

   findUserByEmail: function(email, callback) {
       this._database.collection('usercollection').findOne({ email: email }, callback);
   }
};

module.exports = UserController;

/// routes.js

/* GET user by email */
router.get('/:email', function(req, res) {
    var UserController = require('./usercontroller');
    UserController.setDB(databaseHandleFromSomewhere);
    UserController.findUserByEmail(req.params.email, function(err, result) {
        if (err) throw err;
        res.json(result);
    });
});

3
मेरी राय में, यह उपयोग करने के लिए सबसे अच्छा पैटर्न है। भाषाओं में कई वेब फ्रेमवर्क वास्तविक http प्रतिक्रिया गठन कार्यक्षमता से व्यापार तर्क को अलग करने के लिए नियंत्रक पैटर्न का उपयोग करते हैं। इस तरह, आप केवल तर्क का परीक्षण कर सकते हैं और संपूर्ण http प्रतिक्रिया प्रक्रिया का नहीं, जो कि कुछ ऐसी चीज है जिसे फ्रेमवर्क के डेवलपर्स को स्वयं परीक्षण करना चाहिए। इस पैटर्न में जिन अन्य चीजों का परीक्षण किया जा सकता है, वे हैं साधारण बिचौलिये, कुछ सत्यापन कार्य और अन्य व्यावसायिक सेवाएँ। DB कनेक्टिविटी परीक्षण एक अलग तरह का परीक्षण है, हालांकि
OzzyTheGiant 20:13 पर 20:13

1
दरअसल, यहां बहुत सारे उत्तर वास्तव में एकीकरण / कार्यात्मक परीक्षण के साथ हैं।
ल्यूक एच

19

एक्सप्रेस के साथ HTTP का परीक्षण करने का सबसे आसान तरीका टीजे के http सहायक को चोरी करना है

मैं व्यक्तिगत रूप से उनके सहायक का उपयोग करता हूं

it("should do something", function (done) {
    request(app())
    .get('/session/new')
    .expect('GET', done)
})

यदि आप विशेष रूप से अपने रूट ऑब्जेक्ट का परीक्षण करना चाहते हैं, तो सही मोक्स में पास करें

describe("Default Route", function(){
    it("should provide the a title and the index view name", function(done){
        routes.index({}, {
            render: function (viewName) {
                viewName.should.equal("index")
                done()
            }
        })
    })
})

5
क्या आप 'सहायक' लिंक को ठीक कर सकते हैं?
निकोलस मरे

16
ऐसा लगता है कि HTTP यूनिट परीक्षण के लिए अप-टू-डेट दृष्टिकोण विज़नमेडिया द्वारा सुपरटेस्ट का उपयोग करना है। यह भी लगता है कि टीजे का http सहायक सुपरस्टार के रूप में विकसित हुआ है।
अक्सेली पैलेन

2
गिएथुब पर सुपरर्टेस्ट यहां
ब्रैंडन

@ रेयानोस आपको समझा सकता है कि आपको अपने उदाहरण में अनुरोध और ऐप कैसे मिलेंगे?
jmcollin92

9
अफसोस की बात है कि यह इकाई परीक्षण के बजाय एकीकरण परीक्षण है।
ल्यूक एच

8

अगर यूनिट परीक्षण के साथ एक्सप्रेस 4 नोट इस उदाहरण को 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;
  });

1

मैं यह भी सोच रहा था, लेकिन विशेष रूप से इकाई परीक्षणों और एकीकरण परीक्षणों के लिए नहीं। यह मैं अभी कर रहा हूँ,

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

जहां रूटरओबज बस है {router: expressRouter, path: '/api'}। मैं तब सबरूटर्स में लोड करता हूं var loginRouterInfo = require('./login')(express.Router({mergeParams: true}));और फिर एक्सप्रेस ऐप एक राउटर को एक पैरामीटर के रूप में एक्सप्रेस राउटर में लेता है। InitRouter फिर सबरटर router.use(loginRouterInfo.path, loginRouterInfo.router);को माउंट करने के लिए कॉल करता है।

उपनगर को इसके साथ परीक्षण किया जा सकता है:

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, '/');
});

3
यह वास्तव में दिलचस्प लग रहा है। क्या आपके पास लापता टुकड़ों के अधिक उदाहरण हैं, यह दिखाने के लिए कि यह सब एक साथ कैसे फिट बैठता है?
cjbarth

1

एकीकरण परीक्षण के बजाय इकाई परीक्षण प्राप्त करने के लिए, मैंने अनुरोध हैंडलर की प्रतिक्रिया वस्तु का मज़ाक उड़ाया।

/* 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);
  });
});

फिर, एकीकरण परीक्षण प्राप्त करने के लिए, आप अपने एंडपॉइंटहैंडलर का मजाक उड़ा सकते हैं और सुपरपॉइंट के साथ समापन बिंदु को कॉल कर सकते हैं ।


0

मेरे मामले में केवल मैं परीक्षण करना चाहता था कि क्या सही हैंडलर को बुलाया गया है। मैं रूटिंग मिडलवेयर के लिए अनुरोध करने की सादगी का लाभ उठाने के लिए सुपरटेस्ट का उपयोग करना चाहता था। मैं एक टाइपस्क्रिप्ट का उपयोग कर रहा हूं और यह वह समाधान है जो मेरे लिए काम करता है

// 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

मार्ग

// routes.ts
import ProductController  from "./ProductController"

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

जाँच

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

मेरे पास काम करने के लिए मज़ाक करने के लिए कुछ मुद्दे थे। लेकिन jest.doMock और उदाहरण में आपके द्वारा देखे गए विशिष्ट आदेश का उपयोग करने से यह काम करता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.