কীভাবে আমি প্রয়োজনীয় জেএসে ইউনিট পরীক্ষার জন্য নির্ভরশীলতাগুলিকে উপহাস করতে পারি?


127

আমার একটি এএমডি মডিউল রয়েছে যা আমি পরীক্ষা করতে চাই, তবে আমি প্রকৃত নির্ভরতা লোড না করে এর নির্ভরতাগুলি উপহাস করতে চাই। আমি প্রয়োজনীয় জেএস ব্যবহার করছি এবং আমার মডিউলটির কোডটি দেখতে এরকম কিছু দেখাচ্ছে:

define(['hurp', 'durp'], function(Hurp, Durp) {
  return {
    foo: function () {
      console.log(Hurp.beans)
    },
    bar: function () {
      console.log(Durp.beans)
    }
  }
}

আমি কীভাবে উপহাস করব hurpএবং durpতাই কার্যকরভাবে ইউনিট পরীক্ষা করতে পারি?


defineফাংশনটি উপহাস করার জন্য আমি নোড.জেজে কিছু ক্রেজি ইভাল স্টাফ করছি । যদিও কয়েকটি আলাদা বিকল্প রয়েছে। আমি আশা করি একটি উত্তর পোস্ট করব যে এটি সহায়ক হবে।
jergason

1
জেসমিনের সাথে ইউনিট পরীক্ষার জন্য আপনি জাস্কের দিকে তাকাতে পারেন । [অস্বীকৃতি: আমি the
ষধটি

1
আপনি নোড এনভিটিতে পরীক্ষা করে নিলে আপনি প্রয়োজনীয়-মক প্যাকেজটি ব্যবহার করতে পারেন । এটি আপনাকে সহজেই আপনার নির্ভরতাগুলি উপহাস করতে পারে, মডিউলগুলি প্রতিস্থাপন করতে পারে ইত্যাদি হিসাবে যদি আপনার অ্যাসিঙ্ক
Squire.js

উত্তর:


64

সুতরাং এই পোস্টটি পড়ার পরে আমি এমন একটি সমাধান নিয়ে এসেছি যা আপনার পরীক্ষার জন্য একটি নতুন প্রসঙ্গ তৈরি করতে প্রয়োজনীয় js কনফিগারেশন ফাংশন ব্যবহার করে যেখানে আপনি কেবল আপনার নির্ভরতাগুলি উপহাস করতে পারেন:

var cnt = 0;
function createContext(stubs) {
  cnt++;
  var map = {};

  var i18n = stubs.i18n;
  stubs.i18n = {
    load: sinon.spy(function(name, req, onLoad) {
      onLoad(i18n);
    })
  };

  _.each(stubs, function(value, key) {
    var stubName = 'stub' + key + cnt;

    map[key] = stubName;

    define(stubName, function() {
      return value;
    });
  });

  return require.config({
    context: "context_" + cnt,
    map: {
      "*": map
    },
    baseUrl: 'js/cfe/app/'
  });
}

সুতরাং এটি একটি নতুন প্রেক্ষাপট তৈরি করে যেখানে সংজ্ঞাগুলির জন্য সংজ্ঞা দেওয়া হয় Hurpএবং Durpআপনি সেই ফাংশনে যে বিষয়গুলি দিয়েছিলেন তা সেট করে set নামের ম্যাথ.র্যান্ডম কিছুটা ময়লা হতে পারে তবে এটি কাজ করে। কারণ যদি আপনার কাছে একগুচ্ছ পরীক্ষার মুখোমুখি হয় তবে আপনার মোকগুলিকে পুনরায় ব্যবহার করা রোধ করতে বা আপনি যখন আবশ্যক জেএসজ মডিউলটি চান তখন মোকে লোড করার জন্য প্রতিটি স্যুইটের জন্য আপনার নতুন প্রসঙ্গ তৈরি করতে হবে।

আপনার ক্ষেত্রে এটি দেখতে এই রকম হবে:

(function () {

  var stubs =  {
    hurp: 'hurp',
    durp: 'durp'
  };
  var context = createContext(stubs);

  context(['yourModuleName'], function (yourModule) {

    //your normal jasmine test starts here

    describe("yourModuleName", function () {
      it('should log', function(){
         spyOn(console, 'log');
         yourModule.foo();

         expect(console.log).toHasBeenCalledWith('hurp');
      })
    });
  });
})();

তাই আমি উত্পাদনে এই পদ্ধতির কিছুক্ষণ ব্যবহার করছি এবং এর সত্যই দৃust়।


1
আপনি এখানে যা করছেন তা আমি পছন্দ করি ... বিশেষত যেহেতু আপনি প্রতিটি পরীক্ষার জন্য আলাদা প্রসঙ্গ লোড করতে পারেন। কেবলমাত্র আমিই চাই যে আমি পরিবর্তন করতে পারি এটি মনে হচ্ছে এটি কেবলমাত্র কাজ করছে যদি আমি সমস্ত নির্ভরতাগুলি উপহাস করি। মক অবজেক্টগুলি যদি সেখানে থাকে তবে তাদের ফেরত দেওয়ার কোনও উপায় সম্পর্কে আপনি কি জানেন তবে মক সরবরাহ না করা থাকলে আসল .js ফাইলটি থেকে পুনরুদ্ধার করতে ফলব্যাক? আমি এটি বের করার জন্য প্রয়োজনীয় কোডটি খনন করার চেষ্টা করছি তবে আমি কিছুটা হারিয়ে যাচ্ছি।
গ্লেন হিউজ

5
এটি কেবলমাত্র আপনি createContextফাংশনে যাওয়ার উপর নির্ভরশীলতার উপহাস করে । সুতরাং আপনার ক্ষেত্রে যদি আপনি কেবলমাত্র ফাংশনটিতে পাস {hurp: 'hurp'}করেন তবে durpফাইলটি একটি সাধারণ নির্ভরতা হিসাবে লোড হবে।
Andreas Köberle

1
আমি এটি রেলগুলিতে ব্যবহার করছি (জেসমিনিয়ার্স / ফ্যান্টমজ সহ) এবং এটি প্রয়োজনীয় জেএসের সাথে উপহাস করার জন্য আমি খুঁজে পেয়েছি সেরা সমাধান।
বেন অ্যান্ডারসন

13
+1 সুন্দর নয়, তবে সমস্ত সম্ভাব্য সমাধানগুলির মধ্যে এটি সর্বনিম্ন কুৎসিত / অগোছালো বলে মনে হয়। এই সমস্যাটি আরও মনোযোগ দেওয়ার দাবি রাখে।
ক্রিস সালজবার্গ

1
আপডেট: যে কেউ এই সমাধানটি বিবেচনা করছেন, আমি নীচে উল্লিখিত স্কয়ার.জে ( github.com/iammerrick/Squire.js ) যাচাইয়ের পরামর্শ দেব । এটি এমন একটি সমাধানের একটি দুর্দান্ত বাস্তবায়ন যা এর মতো এটিই যেখানে স্টাবের প্রয়োজন সেখানে নতুন প্রসঙ্গ তৈরি করা creating
ক্রিস সালজবার্গ

44

আপনি নতুন Squire.js lib দেখতে পারেন

ডক্স থেকে:

স্কয়ার.জেএস হ'ল বিদ্রূপ নির্ভরতা সহজ করার জন্য প্রয়োজনীয়.জেএস ব্যবহারকারীদের জন্য নির্ভরতা ইনজেক্টর!


2
দৃr়ভাবে প্রস্তাবিত! আমি স্কয়ার.জেএস ব্যবহার করার জন্য আমার কোড আপডেট করছি এবং এখন পর্যন্ত আমি এটিকে অনেক পছন্দ করছি। খুব সাধারণ কোড, হুডের নিচে দুর্দান্ত যাদু নেই, তবে এমন পদ্ধতিতে করা হয়েছে যা বুঝতে (তুলনামূলকভাবে) সহজ।
ক্রিস সালজবার্গ

1
স্কয়ারের পার্শ্ব অন্যান্য পরীক্ষাগুলিতে প্রভাব ফেলতে আমার অনেক সমস্যা হয়েছিল এবং এটির প্রস্তাব দিতে পারি না। আমি npmjs.com/package/requirejs-mock
জেফ হোয়াইট

17

আমি এই সমস্যার তিনটি পৃথক সমাধান পেয়েছি, তাদের কোনওটিই মনোরম নয়।

নির্ভরতা ইনলাইন সংজ্ঞায়িত

define('hurp', [], function () {
  return {
    beans: 'Beans'
  };
});

define('durp', [], function () {
  return {
    beans: 'durp beans'
  };
});

require('hurpdhurp', function () {
  // test hurpdurp in here
});

Fugly। আপনাকে প্রচুর এএমডি বয়লারপ্লেট দিয়ে পরীক্ষা করতে হবে।

বিভিন্ন পথ থেকে মক নির্ভরতা লোড হচ্ছে

এটির মধ্যে নির্ভরশীলতার পরিবর্তে মককে নির্দেশ করে এমন প্রতিটি নির্ভরতার জন্য পাথ সংজ্ঞায়িত করার জন্য একটি পৃথক কনফিগারেশন ফাইল ব্যবহার করা জড়িত। এটি অনেক কুরুচিপূর্ণ, প্রচুর পরিমাণে পরীক্ষা ফাইল এবং কনফিগারেশন ফাইল তৈরির প্রয়োজন।

নোডে ফেক ইট

এটি আমার বর্তমান সমাধান, তবে এটি এখনও ভয়াবহ one

আপনি defineমডিউলে আপনার নিজস্ব উপহাস সরবরাহ করার জন্য আপনার নিজস্ব ফাংশন তৈরি করেন এবং কল টেস্টগুলিতে আপনার পরীক্ষাগুলি রাখেন। তারপরে আপনি evalনিজের পরীক্ষা চালানোর জন্য মডিউলটি পছন্দ করেন:

var fs = require('fs')
  , hurp = {
      beans: 'BEANS'
    }
  , durp = {
      beans: 'durp beans'
    }
  , hurpDurp = fs.readFileSync('path/to/hurpDurp', 'utf8');
  ;



function define(deps, cb) {
  var TestableHurpDurp = cb(hurp, durp);
  // now run tests below on TestableHurpDurp, which is using your
  // passed-in mocks as dependencies.
}

// evaluate the AMD module, running your mocked define function and your tests.
eval(hurpDurp);

এটি আমার পছন্দসই সমাধান। এটি কিছুটা যাদু দেখায় তবে এর কয়েকটি সুবিধা রয়েছে।

  1. নোডে আপনার পরীক্ষাগুলি চালান, তাই ব্রাউজারের অটোমেশনে কোনও গোলযোগ নেই।
  2. আপনার পরীক্ষাগুলিতে অগোছালো এএমডি বয়লারপ্লেটের কম প্রয়োজন।
  3. আপনি evalক্রোধে ব্যবহার করতে পারেন এবং ক্রোকফোর্ড ক্রোধের সাথে বিস্ফোরিত হওয়ার কথা ভাবুন।

স্পষ্টতই এর কিছু ত্রুটি রয়েছে।

  1. যেহেতু আপনি নোডে পরীক্ষা করছেন, আপনি ব্রাউজার ইভেন্ট বা ডোম হেরফের দিয়ে কিছুই করতে পারবেন না। যুক্তি পরীক্ষার জন্য কেবল ভাল।
  2. এখনও সেট আপ করতে একটি সামান্য clunky। আপনার defineপ্রতিটি পরীক্ষায় উপহাস করা দরকার , যেহেতু আপনার পরীক্ষাগুলি আসলে এটিই চালিত হয়।

আমি এই ধরণের স্টাফের জন্য একটি ভাল সিনট্যাক্স দেওয়ার জন্য একটি পরীক্ষক রানার উপর কাজ করছি, তবে আমার এখনও সমস্যা 1 এর কোনও ভাল সমাধান নেই।

উপসংহার

আবশ্যকগুলিতে বিদ্রূপ করা কঠিন চুষছে। আমি একটি উপায় খুঁজে পেয়েছি যা সাজানোর কাজ করে তবে আমি এতে খুব বেশি খুশি নই। আপনার যদি আরও ভাল ধারণা থাকে তবে দয়া করে আমাকে জানান।


15

একটা ব্যাপার config.mapবিকল্প http://requirejs.org/docs/api.html#config-map

কীভাবে এটি ব্যবহার করবেন:

  1. সাধারণ মডিউল সংজ্ঞায়িত করুন;
  2. স্টাব মডিউল সংজ্ঞায়িত করুন;
  3. স্পষ্টভাবে প্রয়োজনীয় জেএস কনফিগার করুন;

    requirejs.config({
      map: {
        'source/js': {
          'foo': 'normalModule'
        },
        'source/test': {
          'foo': 'stubModule'
        }
      }
    });

এই ক্ষেত্রে স্বাভাবিক এবং পরীক্ষার কোডের জন্য আপনি fooমডিউলটি ব্যবহার করতে পারেন যা আসল মডিউল রেফারেন্স এবং তদনুসারে স্টাব হবে।


এই পদ্ধতির আমার জন্য সত্যিই ভাল কাজ করে। আমার ক্ষেত্রে, আমি এটি পরীক্ষার রানার পৃষ্ঠা -> মানচিত্রের t এইচটিএমএল এ যুক্ত করেছি - map '*': Common 'সাধারণ / মডিউল / দরকারীমডিউল': '/ টেস্টস / স্পেস / কমন / ইউজফুলমডিউলমক.জেএস'}}
সারিবদ্ধ

9

নির্ভরশীলতাগুলিকে উপহাস করার জন্য আপনি testr.js ব্যবহার করতে পারেন । আপনি মূলগুলির পরিবর্তে মক নির্ভরতা লোড করতে পরীক্ষক সেট করতে পারেন। এখানে একটি উদাহরণ ব্যবহার:

var fakeDep = function(){
    this.getText = function(){
        return 'Fake Dependancy';
    };
};

var Module1 = testr('module1', {
    'dependancies/dependancy1':fakeDep
});

ভাল হিসাবে এই পরীক্ষা করে দেখুন: http://cyberasylum.janithw.com/mocking-requirejs-dependencies-for-unit-testing/


2
আমি সত্যিই টেস্ট.আর.জেসকে কাজ করতে চেয়েছিলাম, তবে এটি এখনও টাস্কটি আপ করতে পারে না। শেষ পর্যন্ত আমি @ আন্ড্রেয়াস কেবারেলের সমাধান নিয়ে যাচ্ছি, যা আমার পরীক্ষাগুলিতে নেস্টেড প্রসঙ্গ যুক্ত করবে (সুন্দর নয়) তবে যা ধারাবাহিকভাবে কাজ করে। আমি আশা করি যে কেউ এই সমাধানটি আরও মার্জিত উপায়ে সমাধানের দিকে মনোনিবেশ করতে পারে। আমি testr.js দেখব এবং যদি / কখন এটি কাজ করে, স্যুইচটি করে দেবে।
ক্রিস সালজবার্গ

@ shioyama হাই, প্রতিক্রিয়া জন্য ধন্যবাদ! আপনার টেস্ট স্ট্যাকের মধ্যে আপনি কীভাবে টেস্ট.আর.জে কনফিগার করেছেন তা একবার দেখে নিতে চাই। আপনার যে সমস্যা হতে পারে তা ঠিক করতে আপনাকে সাহায্য করার জন্য খুশি! আপনি যদি কিছু লগইন করতে চান তবে সেখানে গিথব ইস্যুগুলির পৃষ্ঠাও রয়েছে। ধন্যবাদ,
ম্যাটিটি এফ

1
@ ম্যাটিএফ দুঃখিত, আমি এখনই মনে করতে পারি না কারণ সঠিক কারণটি ছিল যে টেস্ট.আরজেসগুলি আমার পক্ষে কাজ করে নি, তবে আমি এই সিদ্ধান্তে পৌঁছেছি যে অতিরিক্ত প্রসঙ্গে ব্যবহারের বিষয়টি আসলে বেশ সরল এবং সত্যই লাইনে আছে কীভাবে প্রয়োজনীয়.js বোঝানো হয়েছে উপহাস / স্টাবিংয়ের জন্য ব্যবহার করা হয়েছিল।
ক্রিস সালজবার্গ

2

এই উত্তরটি আন্দ্রেস কাবারেলের উত্তরের ভিত্তিতে তৈরি ।
তার সমাধানটি বাস্তবায়ন এবং বুঝতে আমার পক্ষে এত সহজ ছিল না, তাই আমি কীভাবে এটি কাজ করে তা আরও বিশদে এবং কিছু ক্ষতিগুলি এড়ানোর জন্য ব্যাখ্যা করব, আশা করি যে এটি ভবিষ্যতের দর্শকদের সহায়তা করবে।

সুতরাং, সমস্ত সেটআপের আগে:
আমি পরীক্ষার রানার হিসাবে কর্ম এবং পরীক্ষার কাঠামো হিসাবে MochaJs ব্যবহার করছি ।

স্কয়ারের মতো কিছু ব্যবহার করা আমার পক্ষে কাজ করে না, কোনও কারণে, যখন আমি এটি ব্যবহার করি, পরীক্ষার কাঠামোর ত্রুটি ছুঁড়ে ফেলে:

প্রকারের ত্রুটি: অনির্ধারিত সম্পত্তি 'কল' পড়তে পারে না

আবশ্যকজেগুলিতে অন্যান্য মডিউল আইডিতে মডিউল আইডির মানচিত্রের সম্ভাবনা রয়েছে । এটি এমন একটি requireফাংশন তৈরি করতে দেয় যা গ্লোবালের চেয়ে আলাদা কনফিগার ব্যবহার করে require
এই বৈশিষ্ট্যগুলি কাজের সমাধানের জন্য এটি গুরুত্বপূর্ণ।

এখানে মক কোডটির আমার সংস্করণটি রয়েছে (অনেকগুলি) মন্তব্য সহ (আমি এটি বুঝতে সক্ষম হব আশা করি)। আমি এটিকে একটি মডিউলের অভ্যন্তরে আবৃত করেছি, যাতে পরীক্ষাগুলি সহজেই এটির প্রয়োজন হয়।

define([], function () {
    var count = 0;
    var requireJsMock= Object.create(null);
    requireJsMock.createMockRequire = function (mocks) {
        //mocks is an object with the module ids/paths as keys, and the module as value
        count++;
        var map = {};

        //register the mocks with unique names, and create a mapping from the mocked module id to the mock module id
        //this will cause RequireJs to load the mock module instead of the real one
        for (property in mocks) {
            if (mocks.hasOwnProperty(property)) {
                var moduleId = property;  //the object property is the module id
                var module = mocks[property];   //the value is the mock
                var stubId = 'stub' + moduleId + count;   //create a unique name to register the module

                map[moduleId] = stubId;   //add to the mapping

                //register the mock with the unique id, so that RequireJs can actually call it
                define(stubId, function () {
                    return module;
                });
            }
        }

        var defaultContext = requirejs.s.contexts._.config;
        var requireMockContext = { baseUrl: defaultContext.baseUrl };   //use the baseUrl of the global RequireJs config, so that it doesn't have to be repeated here
        requireMockContext.context = "context_" + count;    //use a unique context name, so that the configs dont overlap
        //use the mapping for all modules
        requireMockContext.map = {
            "*": map
        };
        return require.config(requireMockContext);  //create a require function that uses the new config
    };

    return requireJsMock;
});

সবচেয়ে বড় ফাঁদ আমি সম্মুখীন, যা আক্ষরিক আমাকে ঘণ্টা খরচ RequireJs কনফিগ তৈরি করা হয়। আমি এটি (গভীর) অনুলিপি করার চেষ্টা করেছি, এবং কেবল প্রয়োজনীয় বৈশিষ্ট্যগুলি (যেমন প্রসঙ্গ বা মানচিত্র) ওভাররাইড করব। এটা কাজ করে না! শুধুমাত্র অনুলিপি করুন baseUrl, এটি সূক্ষ্ম কাজ করে।

ব্যবহার

এটি ব্যবহার করতে, এটি আপনার পরীক্ষায় প্রয়োজন, মক তৈরি করুন এবং তারপরে এটি পাস করুন createMockRequire। উদাহরণ স্বরূপ:

var ModuleMock = function () {
    this.method = function () {
        methodCalled += 1;
    };
};
var mocks = {
    "ModuleIdOrPath": ModuleMock
}
var requireMocks = mocker.createMockRequire(mocks);

এবং এখানে একটি সম্পূর্ণ পরীক্ষা ফাইলের একটি উদাহরণ :

define(["chai", "requireJsMock"], function (chai, requireJsMock) {
    var expect = chai.expect;

    describe("Module", function () {
        describe("Method", function () {
            it("should work", function () {
                return new Promise(function (resolve, reject) {
                    var handler = { handle: function () { } };

                    var called = 0;
                    var moduleBMock = function () {
                        this.method = function () {
                            methodCalled += 1;
                        };
                    };
                    var mocks = {
                        "ModuleBIdOrPath": moduleBMock
                    }
                    var requireMocks = requireJsMock.createMockRequire(mocks);

                    requireMocks(["js/ModuleA"], function (moduleA) {
                        try {
                            moduleA.method();   //moduleA should call method of moduleBMock
                            expect(called).to.equal(1);
                            resolve();
                        } catch (e) {
                            reject(e);
                        }
                    });
                });
            });
        });
    });
});

0

আপনি যদি কিছু প্লেইন জেএস পরীক্ষা করতে চান যা একটি ইউনিটকে আলাদা করে দেয় তবে আপনি কেবল এই স্নিপেট ব্যবহার করতে পারেন:

function define(args, func){
    if(!args.length){
        throw new Error("please stick to the require.js api which wants a: define(['mydependency'], function(){})");
    }

    var fileName = document.scripts[document.scripts.length-1].src;

    // get rid of the url and path elements
    fileName = fileName.split("/");
    fileName = fileName[fileName.length-1];

    // get rid of the file ending
    fileName = fileName.split(".");
    fileName = fileName[0];

    window[fileName] = func;
    return func;
}
window.define = define;
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.