ক্লাউড ফাংশন ইউনিট পরীক্ষা

এই পৃষ্ঠাটি আপনার ফাংশনগুলির জন্য ইউনিট পরীক্ষা লেখার জন্য সর্বোত্তম অনুশীলন এবং সরঞ্জামগুলি বর্ণনা করে, যেমন পরীক্ষাগুলি যা একটি অবিচ্ছিন্ন ইন্টিগ্রেশন (CI) সিস্টেমের একটি অংশ হবে। পরীক্ষা সহজতর করার জন্য, Firebase ক্লাউড ফাংশনের জন্য Firebase টেস্ট SDK প্রদান করে। এটি npm-এ firebase-functions-test হিসাবে বিতরণ করা হয় এবং এটি firebase-functions জন্য একটি সহচর পরীক্ষা SDK। ক্লাউড ফাংশনের জন্য ফায়ারবেস টেস্ট SDK:

  • আপনার পরীক্ষার জন্য উপযুক্ত সেটআপ এবং টিয়ারডাউনের যত্ন নেয়, যেমন firebase-functions জন্য প্রয়োজনীয় পরিবেশ ভেরিয়েবল সেট করা এবং আনসেট করা।
  • নমুনা ডেটা এবং ইভেন্ট প্রসঙ্গ জেনারেট করে, যাতে আপনাকে শুধুমাত্র আপনার পরীক্ষার সাথে প্রাসঙ্গিক ক্ষেত্রগুলি নির্দিষ্ট করতে হবে।

পরীক্ষা সেটআপ

আপনার ফাংশন ফোল্ডারে নিম্নলিখিত কমান্ডগুলি চালিয়ে firebase-functions-test এবং মোচা , একটি টেস্টিং ফ্রেমওয়ার্ক উভয়ই ইনস্টল করুন:

npm install --save-dev firebase-functions-test
npm install --save-dev mocha

এর পরে ফাংশন ফোল্ডারের ভিতরে একটি test ফোল্ডার তৈরি করুন, আপনার পরীক্ষার কোডের জন্য এটির ভিতরে একটি নতুন ফাইল তৈরি করুন এবং index.test.js মতো কিছু নাম দিন।

অবশেষে, নিম্নলিখিত যোগ করতে functions/package.json পরিবর্তন করুন:

"scripts": {
  "test": "mocha --reporter spec"
}

একবার আপনি পরীক্ষাগুলি লিখলে, আপনি আপনার ফাংশন ডিরেক্টরির মধ্যে npm test চালিয়ে সেগুলি চালাতে পারেন।

ক্লাউড ফাংশনের জন্য Firebase টেস্ট SDK শুরু করা হচ্ছে

firebase-functions-test ব্যবহার করার দুটি উপায় রয়েছে:

  1. অনলাইন মোড (প্রস্তাবিত): পরীক্ষাগুলি লিখুন যা পরীক্ষার জন্য নিবেদিত একটি ফায়ারবেস প্রকল্পের সাথে ইন্টারঅ্যাক্ট করে যাতে ডাটাবেস লিখতে, ব্যবহারকারী তৈরি করে, ইত্যাদি বাস্তবে ঘটতে পারে এবং আপনার পরীক্ষার কোড ফলাফলগুলি পরীক্ষা করতে পারে৷ এর মানে হল আপনার ফাংশনে ব্যবহৃত অন্যান্য Google SDK গুলিও কাজ করবে৷
  2. অফলাইন মোড: সাইলড এবং অফলাইন ইউনিট পরীক্ষা লিখুন কোন পার্শ্বপ্রতিক্রিয়া ছাড়াই। এর মানে হল যে কোনও পদ্ধতির কল যা ফায়ারবেস পণ্যের সাথে ইন্টারঅ্যাক্ট করে (যেমন ডাটাবেসে লেখা বা ব্যবহারকারী তৈরি করা) স্টাব করা দরকার। আপনার যদি ক্লাউড ফায়ারস্টোর বা রিয়েলটাইম ডেটাবেস ফাংশন থাকে তবে সাধারণত অফলাইন মোড ব্যবহার করা বাঞ্ছনীয় নয়, কারণ এটি আপনার পরীক্ষার কোডের জটিলতাকে অনেক বাড়িয়ে দেয়।

অনলাইন মোডে SDK শুরু করুন (প্রস্তাবিত)

আপনি যদি একটি পরীক্ষামূলক প্রকল্পের সাথে ইন্টারঅ্যাক্ট করে এমন পরীক্ষা লিখতে চান, তাহলে আপনাকে firebase-admin মাধ্যমে অ্যাপটি শুরু করার জন্য প্রয়োজনীয় প্রজেক্ট কনফিগার মান এবং একটি পরিষেবা অ্যাকাউন্ট কী ফাইলের পথ সরবরাহ করতে হবে।

আপনার Firebase প্রকল্পের কনফিগার মান পেতে:

  1. Firebase কনসোলে আপনার প্রকল্প সেটিংস খুলুন।
  2. আপনার অ্যাপে, পছন্দসই অ্যাপটি নির্বাচন করুন।
  3. ডান ফলকে, অ্যাপল এবং অ্যান্ড্রয়েড অ্যাপের জন্য একটি কনফিগারেশন ফাইল ডাউনলোড করার বিকল্পটি নির্বাচন করুন।

    ওয়েব অ্যাপ্লিকেশানগুলির জন্য, কনফিগারেশন মানগুলি প্রদর্শন করতে Config নির্বাচন করুন৷

একটি কী ফাইল তৈরি করতে:

  1. Google ক্লাউড কনসোলের পরিষেবা অ্যাকাউন্ট প্যান খুলুন।
  2. অ্যাপ ইঞ্জিন ডিফল্ট পরিষেবা অ্যাকাউন্ট নির্বাচন করুন, এবং তৈরি কী নির্বাচন করতে ডানদিকে বিকল্প মেনু ব্যবহার করুন।
  3. প্রম্পট করা হলে, কী ধরণের জন্য JSON নির্বাচন করুন এবং তৈরি করুন ক্লিক করুন।

কী ফাইলটি সংরক্ষণ করার পরে, SDK শুরু করুন:

// At the top of test/index.test.js
const test = require('firebase-functions-test')({
  databaseURL: 'https://my-project.firebaseio.com',
  storageBucket: 'my-project.appspot.com',
  projectId: 'my-project',
}, 'path/to/serviceAccountKey.json');

অফলাইন মোডে SDK শুরু করুন

আপনি যদি সম্পূর্ণ অফলাইন পরীক্ষা লিখতে চান, আপনি কোনো পরামিতি ছাড়াই SDK শুরু করতে পারেন:

// At the top of test/index.test.js
const test = require('firebase-functions-test')();

কনফিগার মান উপহাস

আপনি যদি আপনার ফাংশন কোডে functions.config() ব্যবহার করেন, আপনি কনফিগার মান উপহাস করতে পারেন। উদাহরণস্বরূপ, যদি functions/index.js নিম্নলিখিত কোড থাকে:

const functions = require('firebase-functions');
const key = functions.config().stripe.key;

তারপরে আপনি আপনার পরীক্ষার ফাইলের ভিতরে মানটিকে উপহাস করতে পারেন:

// Mock functions config values
test.mockConfig({ stripe: { key: '23wr42ewr34' }});

আপনার ফাংশন আমদানি করা হচ্ছে

আপনার ফাংশন আমদানি করতে, একটি মডিউল হিসাবে আপনার প্রধান ফাংশন ফাইল আমদানি করতে require ব্যবহার করুন। firebase-functions-test শুরু করার পরে এবং কনফিগার মানগুলিকে উপহাস করার পরেই এটি করতে ভুলবেন না।

// after firebase-functions-test has been initialized
const myFunctions = require('../index.js'); // relative path to functions code

আপনি যদি অফলাইন মোডে firebase-functions-test শুরু করেন, এবং আপনার ফাংশন কোডে admin.initializeApp() থাকে, তাহলে আপনার ফাংশনগুলি আমদানি করার আগে আপনাকে এটি স্টাব করতে হবে:

// If index.js calls admin.initializeApp at the top of the file,
// we need to stub it out before requiring index.js. This is because the
// functions will be executed as a part of the require process.
// Here we stub admin.initializeApp to be a dummy function that doesn't do anything.
adminInitStub = sinon.stub(admin, 'initializeApp');
// Now we can require index.js and save the exports inside a namespace called myFunctions.
myFunctions = require('../index');

টেস্টিং ব্যাকগ্রাউন্ড (নন-HTTP) ফাংশন

নন-HTTP ফাংশন পরীক্ষা করার প্রক্রিয়াতে নিম্নলিখিত পদক্ষেপগুলি জড়িত:

  1. test.wrap পদ্ধতি দিয়ে আপনি যে ফাংশনটি পরীক্ষা করতে চান তা মোড়ানো
  2. পরীক্ষার ডেটা তৈরি করুন
  3. আপনার তৈরি করা পরীক্ষার ডেটা এবং আপনি নির্দিষ্ট করতে চান এমন কোনো ইভেন্ট প্রসঙ্গ ক্ষেত্র দিয়ে মোড়ানো ফাংশনটি চালু করুন।
  4. আচরণ সম্পর্কে দাবী করুন।

প্রথমে আপনি যে ফাংশনটি পরীক্ষা করতে চান তা মোড়ানো। ধরুন আপনার functions/index.js এ একটি ফাংশন আছে যাকে বলা হয় makeUppercase , যা আপনি পরীক্ষা করতে চান। নিম্নলিখিতগুলি functions/test/index.test.js এ লিখুন

// "Wrap" the makeUpperCase function from index.js
const myFunctions = require('../index.js');
const wrapped = test.wrap(myFunctions.makeUppercase);

wrapped হল একটি ফাংশন যা makeUppercase কল করার সময় আহ্বান করে। wrapped 2 প্যারামিটার লাগে:

  1. ডেটা (প্রয়োজনীয়): makeUppercase পাঠানো ডেটা। এটি সরাসরি আপনার লেখা ফাংশন হ্যান্ডলারে পাঠানো প্রথম প্যারামিটারের সাথে মিলে যায়। firebase-functions-test কাস্টম ডেটা বা উদাহরণ ডেটা নির্মাণের জন্য পদ্ধতি প্রদান করে।
  2. eventContextOptions (ঐচ্ছিক): ইভেন্ট প্রসঙ্গের ক্ষেত্র যা আপনি নির্দিষ্ট করতে চান। ইভেন্ট প্রসঙ্গ হল আপনার লেখা ফাংশন হ্যান্ডলারে পাঠানো দ্বিতীয় প্যারামিটার। আপনি wrapped কল করার সময় একটি eventContextOptions প্যারামিটার অন্তর্ভুক্ত না করলে, একটি ইভেন্ট প্রসঙ্গ এখনও বোধগম্য ক্ষেত্রগুলির সাথে তৈরি করা হয়। আপনি এখানে নির্দিষ্ট করে উত্পন্ন কিছু ক্ষেত্র ওভাররাইড করতে পারেন। মনে রাখবেন যে আপনি যে ক্ষেত্রগুলিকে ওভাররাইড করতে চান তা অন্তর্ভুক্ত করতে হবে৷ আপনি ওভাররাইড করেননি এমন কোনো ক্ষেত্র তৈরি হয়।
const data = … // See next section for constructing test data

// Invoke the wrapped function without specifying the event context.
wrapped(data);

// Invoke the function, and specify params
wrapped(data, {
  params: {
    pushId: '234234'
  }
});

// Invoke the function, and specify auth and auth Type (for real time database functions only)
wrapped(data, {
  auth: {
    uid: 'jckS2Q0'
  },
  authType: 'USER'
});

// Invoke the function, and specify all the fields that can be specified
wrapped(data, {
  eventId: 'abc',
  timestamp: '2018-03-23T17:27:17.099Z',
  params: {
    pushId: '234234'
  },
  auth: {
    uid: 'jckS2Q0' // only for real time database functions
  },
  authType: 'USER' // only for real time database functions
});

পরীক্ষার তথ্য নির্মাণ

একটি মোড়ানো ফাংশনের প্রথম প্যারামিটার হল অন্তর্নিহিত ফাংশনটি চালু করার জন্য পরীক্ষার ডেটা। পরীক্ষার ডেটা তৈরি করার বিভিন্ন উপায় রয়েছে।

কাস্টম ডেটা ব্যবহার করে

firebase-functions-test আপনার ফাংশন পরীক্ষা করার জন্য প্রয়োজনীয় ডেটা তৈরি করার জন্য অনেকগুলি ফাংশন রয়েছে। উদাহরণস্বরূপ, একটি Firestore DocumentSnapshot তৈরি করতে test.firestore.makeDocumentSnapshot ব্যবহার করুন। প্রথম আর্গুমেন্ট হল ডেটা, এবং দ্বিতীয় আর্গুমেন্ট হল সম্পূর্ণ রেফারেন্স পাথ, এবং স্ন্যাপশটের অন্যান্য বৈশিষ্ট্যগুলির জন্য একটি ঐচ্ছিক তৃতীয় আর্গুমেন্ট রয়েছে যা আপনি নির্দিষ্ট করতে পারেন৷

// Make snapshot
const snap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Call wrapped function with the snapshot
const wrapped = test.wrap(myFunctions.myFirestoreDeleteFunction);
wrapped(snap);

আপনি যদি একটি onUpdate বা onWrite ফাংশন পরীক্ষা করে থাকেন তবে আপনাকে দুটি স্ন্যাপশট তৈরি করতে হবে: একটি পূর্ববর্তী অবস্থার জন্য এবং একটি পরবর্তী অবস্থার জন্য। তারপর, আপনি এই স্ন্যাপশটগুলির সাথে একটি Change বস্তু তৈরি করতে makeChange পদ্ধতি ব্যবহার করতে পারেন।

// Make snapshot for state of database beforehand
const beforeSnap = test.firestore.makeDocumentSnapshot({foo: 'bar'}, 'document/path');
// Make snapshot for state of database after the change
const afterSnap = test.firestore.makeDocumentSnapshot({foo: 'faz'}, 'document/path');
const change = test.makeChange(beforeSnap, afterSnap);
// Call wrapped function with the Change object
const wrapped = test.wrap(myFunctions.myFirestoreUpdateFunction);
wrapped(change);

অন্যান্য সমস্ত ডেটা প্রকারের জন্য অনুরূপ ফাংশনের জন্য API রেফারেন্স দেখুন।

উদাহরণ ডেটা ব্যবহার করে

আপনার পরীক্ষায় ব্যবহৃত ডেটা কাস্টমাইজ করার প্রয়োজন না হলে, firebase-functions-test প্রতিটি ফাংশনের প্রকারের জন্য উদাহরণ ডেটা তৈরি করার পদ্ধতিগুলি অফার করে।

// For Firestore onCreate or onDelete functions
const snap = test.firestore.exampleDocumentSnapshot();
// For Firestore onUpdate or onWrite functions
const change = test.firestore.exampleDocumentSnapshotChange();

প্রতিটি ফাংশন ধরনের জন্য উদাহরণ ডেটা পাওয়ার পদ্ধতির জন্য API রেফারেন্স দেখুন।

স্টাবড ডেটা ব্যবহার করা (অফলাইন মোডের জন্য)

আপনি যদি অফলাইন মোডে SDK সূচনা করেন, এবং একটি ক্লাউড ফায়ারস্টোর বা রিয়েলটাইম ডেটাবেস ফাংশন পরীক্ষা করছেন, তাহলে একটি প্রকৃত DocumentSnapshot বা DataSnapshot তৈরি করার পরিবর্তে আপনার স্টাব সহ একটি প্লেইন অবজেক্ট ব্যবহার করা উচিত।

ধরা যাক আপনি নিম্নলিখিত ফাংশনের জন্য একটি ইউনিট পরীক্ষা লিখছেন:

// Listens for new messages added to /messages/:pushId/original and creates an
// uppercase version of the message to /messages/:pushId/uppercase
exports.makeUppercase = functions.database.ref('/messages/{pushId}/original')
    .onCreate((snapshot, context) => {
      // Grab the current value of what was written to the Realtime Database.
      const original = snapshot.val();
      functions.logger.log('Uppercasing', context.params.pushId, original);
      const uppercase = original.toUpperCase();
      // You must return a Promise when performing asynchronous tasks inside a Functions such as
      // writing to the Firebase Realtime Database.
      // Setting an "uppercase" sibling in the Realtime Database returns a Promise.
      return snapshot.ref.parent.child('uppercase').set(uppercase);
    });

ফাংশনের ভিতরে, snap দুইবার ব্যবহার করা হয়:

  • snap.val()
  • snap.ref.parent.child('uppercase').set(uppercase)

পরীক্ষার কোডে, একটি প্লেইন অবজেক্ট তৈরি করুন যেখানে এই কোড পাথ দুটিই কাজ করবে এবং পদ্ধতিগুলিকে আটকাতে Sinon ব্যবহার করুন।

// The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called,
// and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called.
const snap = {
  val: () => 'input',
  ref: {
    parent: {
      child: childStub,
    }
  }
};
childStub.withArgs(childParam).returns({ set: setStub });
setStub.withArgs(setParam).returns(true);

দাবী করা

SDK শুরু করার পরে, ফাংশনগুলি মোড়ানো এবং ডেটা তৈরি করার পরে, আপনি তৈরি করা ডেটা দিয়ে মোড়ানো ফাংশনগুলিকে আহ্বান করতে পারেন এবং আচরণ সম্পর্কে দাবি করতে পারেন। আপনি এই দাবিগুলি করার জন্য চাই এর মতো একটি লাইব্রেরি ব্যবহার করতে পারেন।

অনলাইন মোডে দাবী করা

আপনি যদি অনলাইন মোডে ক্লাউড ফাংশনগুলির জন্য Firebase টেস্ট SDK সূচনা করেন, তাহলে আপনি দাবি করতে পারেন যে firebase-admin SDK ব্যবহার করে কাঙ্ক্ষিত ক্রিয়াগুলি (যেমন একটি ডাটাবেস লেখা) হয়েছে৷

নীচের উদাহরণটি দাবি করে যে 'INPUT' পরীক্ষা প্রকল্পের ডাটাবেসে লেখা হয়েছে।

// Create a DataSnapshot with the value 'input' and the reference path 'messages/11111/original'.
const snap = test.database.makeDataSnapshot('input', 'messages/11111/original');

// Wrap the makeUppercase function
const wrapped = test.wrap(myFunctions.makeUppercase);
// Call the wrapped function with the snapshot you constructed.
return wrapped(snap).then(() => {
  // Read the value of the data at messages/11111/uppercase. Because `admin.initializeApp()` is
  // called in functions/index.js, there's already a Firebase app initialized. Otherwise, add
  // `admin.initializeApp()` before this line.
  return admin.database().ref('messages/11111/uppercase').once('value').then((createdSnap) => {
    // Assert that the value is the uppercased version of our input.
    assert.equal(createdSnap.val(), 'INPUT');
  });
});

অফলাইন মোডে দাবী করা

আপনি ফাংশনের প্রত্যাশিত রিটার্ন মান সম্পর্কে দাবি করতে পারেন:

const childParam = 'uppercase';
const setParam = 'INPUT';
// Stubs are objects that fake and/or record function calls.
// These are excellent for verifying that functions have been called and to validate the
// parameters passed to those functions.
const childStub = sinon.stub();
const setStub = sinon.stub();
// The following lines creates a fake snapshot, 'snap', which returns 'input' when snap.val() is called,
// and returns true when snap.ref.parent.child('uppercase').set('INPUT') is called.
const snap = {
  val: () => 'input',
  ref: {
    parent: {
      child: childStub,
    }
  }
};
childStub.withArgs(childParam).returns({ set: setStub });
setStub.withArgs(setParam).returns(true);
// Wrap the makeUppercase function.
const wrapped = test.wrap(myFunctions.makeUppercase);
// Since we've stubbed snap.ref.parent.child(childParam).set(setParam) to return true if it was
// called with the parameters we expect, we assert that it indeed returned true.
return assert.equal(wrapped(snap), true);

এছাড়াও আপনি সিনন গুপ্তচর ব্যবহার করতে পারেন যে নির্দিষ্ট পদ্ধতিগুলিকে ডাকা হয়েছে, এবং আপনি প্রত্যাশিত পরামিতিগুলির সাথে।

HTTP ফাংশন পরীক্ষা করা হচ্ছে

HTTP onCall ফাংশন পরীক্ষা করতে, ব্যাকগ্রাউন্ড ফাংশন পরীক্ষা করার মতো একই পদ্ধতি ব্যবহার করুন।

আপনি যদি HTTP অন রিকোয়েস্ট ফাংশন পরীক্ষা করছেন, তাহলে আপনার firebase-functions-test ব্যবহার করা উচিত যদি:

  • আপনি functions.config() ব্যবহার করেন
  • আপনার ফাংশন একটি Firebase প্রকল্প বা অন্যান্য Google API-এর সাথে ইন্টারঅ্যাক্ট করে এবং আপনি আপনার পরীক্ষার জন্য একটি বাস্তব Firebase প্রকল্প এবং এর প্রমাণপত্র ব্যবহার করতে চান।

একটি HTTP onRequest ফাংশন দুটি প্যারামিটার নেয়: একটি অনুরোধ বস্তু এবং একটি প্রতিক্রিয়া বস্তু। এখানে আপনি কিভাবে addMessage() উদাহরণ ফাংশন পরীক্ষা করতে পারেন:

  • রেসপন্স অবজেক্টে রিডাইরেক্ট ফাংশন ওভাররাইড করুন, যেহেতু sendMessage() এটিকে কল করে।
  • রিডাইরেক্ট ফাংশনের মধ্যে, রিডাইরেক্ট ফাংশনকে কোন প্যারামিটারগুলির সাথে কল করা উচিত সে সম্পর্কে দাবী করতে সাহায্য করার জন্য chai.assert ব্যবহার করুন:
// A fake request object, with req.query.text set to 'input'
const req = { query: {text: 'input'} };
// A fake response object, with a stubbed redirect function which asserts that it is called
// with parameters 303, 'new_ref'.
const res = {
  redirect: (code, url) => {
    assert.equal(code, 303);
    assert.equal(url, 'new_ref');
    done();
  }
};

// Invoke addMessage with our fake request and response objects. This will cause the
// assertions in the response object to be evaluated.
myFunctions.addMessage(req, res);

পরিচ্ছন্নতার পরীক্ষা

আপনার পরীক্ষার কোডের একেবারে শেষে, ক্লিনআপ ফাংশনটি কল করুন। এটি এনভায়রনমেন্ট ভেরিয়েবলগুলিকে আনসেট করে যা SDK শুরু করার সময় সেট করে এবং Firebase অ্যাপগুলিকে মুছে দেয় যেগুলি তৈরি করা হতে পারে যদি আপনি একটি রিয়েল টাইম ডাটাবেস DataSnapshot বা Firestore DocumentSnapshot তৈরি করতে SDK ব্যবহার করেন।

test.cleanup();

সম্পূর্ণ উদাহরণ পর্যালোচনা করুন এবং আরও জানুন

আপনি Firebase GitHub সংগ্রহস্থলে সম্পূর্ণ উদাহরণ পর্যালোচনা করতে পারেন।

আরও জানতে, firebase-functions-test এর জন্য API রেফারেন্স পড়ুন।