रूबी/रेल्स डेवलपर्स के रूप में, हम टेस्ट लिखना पसंद करते हैं। टेस्टिंग सॉफ्टवेयर डेवलपमेंट का अभिन्न अंग है। अच्छे टेस्ट हमें उच्च गुणवत्ता वाला कोड लिखने में मदद करते हैं। वे समग्र डेवलपमेंट प्रक्रिया में मूल्य जोड़ते हैं, लेकिन यदि हम टेस्ट को अच्छी तरह से प्रबंधित नहीं करते हैं, तो वे हमें धीमा कर सकते हैं। यहां अनुचित तरीके से प्रबंधित टेस्ट्स के कुछ लक्षण हैं:
- टेस्ट चलाने में लंबा समय लगता है।
- टेस्ट अविश्वसनीय हैं और बेतरतीब ढंग से विफल होते हैं।
- टेस्ट अलग-अलग मशीनों पर अलग व्यवहार करते हैं।
- सभी टेस्ट सुइट्स चलाने से आपका CI धीमा हो जाता है।
इस ब्लॉग पोस्ट में, हम कवर करेंगे कि कंटेनर्स का उपयोग टेस्टिंग को विश्वसनीय बनाने के लिए कैसे किया जा सकता है, जो उच्च गुणवत्ता वाले सॉफ्टवेयर डिलीवर करने में सहायक है।
इंटीग्रेशन टेस्ट्स और यूनिट टेस्ट्स
इससे पहले कि हम कंटेनर्स के विषय पर जाएं, आइए पहले यूनिट और इंटीग्रेशन टेस्ट्स के बीच अंतर को समझें और एक ही पेज पर हों।
यूनिट टेस्ट्स कोड के एक ब्लॉक को आइसोलेशन में टेस्ट करने का एक तरीका है। वे कोड ब्लॉक की कार्यक्षमता को टेस्ट करने में मदद करते हैं लेकिन निर्भरताओं को नहीं। उदाहरण के लिए, यहां एक सरल मेथड है:
def sum(a, b) a + b end यह एक फंक्शन है जो दो संख्याओं को जोड़ता है, जो बहुत सरल है। इस फंक्शन को टेस्ट करना सुपर आसान होगा। लेकिन क्या होगा यदि इस फंक्शन की बाहरी सेवा पर निर्भरता है। इस मेथड को योग पूरा करने के बाद डेटाबेस में लिखना होगा।
def sum!(a, b) c = a + b Sum.create(value: c) end कोड के इस ब्लॉक में, मान जोड़े जाते हैं और डेटाबेस में सहेजे जाते हैं। कोड के इस ब्लॉक को टेस्ट करने के लिए, हमें एक डेटाबेस सर्वर चलाने की आवश्यकता है। यदि हम इसे डेटाबेस सर्वर चलाकर टेस्ट करते हैं, तो यह अब यूनिट टेस्ट नहीं है। इसे बाहरी निर्भरता की आवश्यकता है, और हम डेटाबेस कनेक्शन और रिकॉर्ड के डेटाबेस में सहेजे जाने को भी टेस्ट करेंगे। इस मामले में, यह टेस्ट एक इंटीग्रेशन टेस्ट है।
यूनिट टेस्ट्स एकाकी यूनिट टेस्ट्स हो सकते हैं (सभी निर्भरताओं को मॉक/स्टब करें) या सामाजिक यूनिट टेस्ट्स (अन्य निर्भरताओं से बात करने की अनुमति दें)। लोगों ने यूनिट टेस्ट्स की विभिन्न परिभाषाएं प्रस्तावित की हैं। इस ब्लॉग संदर्भ में, हम जिस प्रकार के यूनिट टेस्ट की बात कर रहे हैं वह एकाकी यूनिट टेस्ट है।
मॉक्स के साथ बाहरी निर्भरताओं को हटाने से टेस्ट तेजी से चलते हैं। यूनिट टेस्ट्स का उपयोग करते समय, हम इन निर्भरताओं को छोड़ देते हैं, लेकिन यह सुनिश्चित करने के लिए कि एप्लिकेशन अपेक्षा के अनुरूप काम करता है, निर्भरताओं का टेस्ट करना भी उतना ही महत्वपूर्ण है। जिन टेस्ट्स को हम इन बाहरी निर्भरताओं, जैसे डेटाबेस, नेटवर्क कॉल, और फाइलों को टेस्ट करने के लिए लिखते हैं, वे इंटीग्रेशन टेस्ट्स हैं।
यूनिट टेस्ट्स की तरह, लोगों ने इंटीग्रेशन टेस्ट्स की विभिन्न परिभाषाएं प्रस्तावित की हैं। हालांकि, मूल अवधारणा समान है। अंतर केवल स्कोप में है। डेटाबेस में लिखने वाला एक छोटा फंक्शन इंटीग्रेशन टेस्ट हो सकता है। एक व्यापक कार्यक्षमता जो सिस्टम के कई हिस्सों को जोड़ती है, वह भी इंटीग्रेशन टेस्ट है। हम यहां जिस टेस्ट की बात कर रहे हैं वह संकीर्ण भाग पर अधिक केंद्रित है। टेस्ट जितना संकीर्ण होगा, उतने अधिक टेस्ट हमें लिखने होंगे। व्यापक टेस्ट्स के साथ, हालांकि, आवश्यक टेस्ट्स की संख्या घट जाती है।
इंटीग्रेशन टेस्ट चलाते समय, हमें एप्लिकेशन और डेटाबेस और बाहरी सेवाओं जैसी निर्भरताओं को चलाने की आवश्यकता होती है। इन निर्भरताओं को प्रबंधित करना चुनौतीपूर्ण हो सकता है।
Docker के आधार
Docker एक तकनीक है जो एप्लिकेशन को उसके एनवायरनमेंट (ऑपरेटिंग सिस्टम) से अलग करने और इसे होस्ट से आइसोलेशन में चलाने में मदद करती है। यह वर्चुअल मशीन के समान है लेकिन तेज़ और अधिक कुशल है। Docker हमें हमारे एप्लिकेशन कोड और इसकी निर्भरताओं को एक ही कंटेनर में पैकेज करने की अनुमति देता है। यह कोड को पोर्टेबल बनाने में मदद करता है और इसका मतलब है कि हमारे पास डेवलपमेंट, टेस्टिंग, और प्रोडक्शन के दौरान चलने वाली एक ही Docker इमेज हो सकती है। यह उस एनवायरनमेंट की स्थिरता सुनिश्चित करेगा जिसमें एप्लिकेशन चलता है। चूंकि यह होस्ट सर्वर पर निर्भर नहीं है, यह एप्लिकेशन के व्यवहार को सभी एनवायरनमेंट्स में अनुमानित बनाता है। Docker डेवलपर्स को सरल कमांड्स का उपयोग करके एप्लिकेशन और उसके एनवायरनमेंट को चलाने और प्रबंधित करने में मदद करता है, जिसमें बिल्डिंग, रनिंग, डिप्लॉयिंग, और अपडेटिंग शामिल हैं।
आइए कुछ महत्वपूर्ण शब्दावली समझते हैं:
Dockerfile - एक Dockerfile बस एक फाइल है जहां हम एप्लिकेशन के लिए आवश्यक निर्भरताओं को परिभाषित करते हैं। हम ऑपरेटिंग सिस्टम, डेटाबेस क्लाइंट लाइब्रेरीज़, या एप्लिकेशन द्वारा आवश्यक किसी भी पैकेज को निर्दिष्ट कर सकते हैं। यहां, हम एप्लिकेशन को चलाने के लिए आवश्यक कमांड को भी परिभाषित करते हैं। एक Dockerfile बेस इमेज से शुरू होता है। ये बेस इमेज एक ऑपरेटिंग सिस्टम या प्रोग्रामिंग भाषा हो सकती हैं।
इमेजेज - इमेजेज एक ब्लूप्रिंट हैं जो कंटेनर को चलाने के लिए आवश्यक चरणों को बताते हैं। इमेजेज अलग-अलग लेयर्स के साथ बनाई जाती हैं। Dockerfile में परिभाषित हर चरण इमेज के भीतर एक लेयर है। यह पुन: उपयोगिता बढ़ाता है और इमेज को तेजी से बनाने के लिए इंटरमीडिएट लेयर को कैश करने में मदद करता है।
कंटेनर्स - कंटेनर्स वास्तविक इमेजेज हैं। ये रुकी हुई स्थिति में भी हो सकते हैं। कंटेनर्स को docker इमेज से शुरू किया जा सकता है। एक ही docker इमेज से कई docker कंटेनर्स शुरू किए जा सकते हैं। कंटेनर्स इमेज द्वारा निर्दिष्ट जानकारी रखते हैं। एक पतली कंटेनर लेयर होती है जहां एप्लिकेशन चलने के दौरान इमेज में किए गए सभी परिवर्तन स्टोर होते हैं।
Docker Compose - Docker Compose एक अलग टूल है जो कई कंटेनर्स को प्रबंधित करने में मदद करता है। हम एक ही docker इमेज से कई कंटेनर्स रख सकते हैं। हमें अलग-अलग docker इमेज से कई कंटेनर्स की भी आवश्यकता हो सकती है, और इन कंटेनर्स को एक-दूसरे के साथ इंटरैक्ट करने की आवश्यकता होती है। Docker Compose कंटेनर्स को प्रबंधित करता है। अन्य टूल्स उपलब्ध हैं, लेकिन इस संदर्भ में, हम केवल इसकी सरलता के कारण Docker Compose का उपयोग करेंगे।
वर्चुअल मशीन्स होस्ट हाइपरवाइज़र के ऊपर होती हैं और एक अलग ऑपरेटिंग सिस्टम इंस्टॉल करने की आवश्यकता होती है, इसलिए अधिक होस्ट संसाधनों का उपयोग होता है। हालांकि, Docker के साथ, हमें होस्ट ऑपरेटिंग सिस्टम में केवल Docker इंजन इंस्टॉल करने की आवश्यकता होती है, और होस्ट संसाधन प्रबंधन Docker द्वारा किया जाता है। Docker डेवलपर्स और सिसऑप्स दोनों के लिए जीवन आसान बनाता है, इसलिए यह DevOps संस्कृति में बहुत लोकप्रिय है।
टेस्टिंग के लिए Docker क्यों उपयोग करें
Docker अपने होस्ट एनवायरनमेंट से चल रहे एप्लिकेशन या टेस्ट को न्यूनतम संसाधन खपत के साथ आइसोलेट करता है। टेस्टिंग के लिए docker का उपयोग करने के कुछ फायदे यहां दिए गए हैं:
हल्का: Docker कंटेनर्स हल्के होते हैं। यह एक ही समय में कई कंटेनर्स चलाने में मदद करता है और एक टेस्ट प्रोसेस को दूसरे टेस्ट प्रोसेस में समस्या पैदा किए बिना।
पोर्टेबल: Docker इमेजेज पोर्टेबल हैं और किसी भी एनवायरनमेंट में चल सकती हैं। यह CI में मदद करता है, क्योंकि हम लोकल और CI एनवायरनमेंट्स में टेस्ट चलाने के लिए उसी इमेज का पुन: उपयोग कर सकते हैं।
वर्जनिंग: कई एप्लिकेशन्स का उपयोग करना या अपनी निर्भरताओं को अपग्रेड करना हमारी लोकल मशीन में हमारी निर्भरताओं के विभिन्न संस्करणों की आवश्यकता होती है। Docker के बिना इसे प्रबंधित करना बहुत मुश्किल होगा। Docker के साथ, हम अलग-अलग संस्करणों के साथ अलग-अलग कंटेनर्स चला सकते हैं। हम अलग-अलग संस्करणों पर टेस्ट भी चला सकते हैं और चीजों को सत्यापित कर सकते हैं।
माइक्रोसर्विसेज का आसान प्रबंधन: माइक्रोसर्विसेज के साथ, टेस्टिंग भी वितरित और जटिल हो जाती है। इसका मतलब है कि हमें निर्भर माइक्रोसर्विस को प्रबंधित करने की आवश्यकता है। Docker और Docker Compose इन सर्विस निर्भरताओं को प्रबंधित करने में मदद करते हैं।
पूर्व-आवश्यकताएँ
इसे आज़माने के लिए, आइए सुनिश्चित करें कि निम्नलिखित सेट अप हैं:
- Docker
- Docker-compose
- Ruby 2.7 (वैकल्पिक, क्योंकि हम Docker कंटेनर में Ruby जोड़ेंगे, लेकिन टेस्टिंग के लिए उपयोगी होगा)
- Rails (5.1+ अनुशंसित)
ऐप को Dockerize करें
आइए एक साधारण Rails ऐप बनाकर और इसे कंटेनराइज़ करके शुरू करें। मैं एक नए Rails ऐप से शुरू कर रहा हूं। आप मौजूदा Rails ऐप को भी कंटेनराइज़ कर सकते हैं।
rails new calculator Dockerfile नाम की एक फाइल बनाएं जिसमें निम्न सामग्री हो:
FROM ruby:2.7 RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list RUN apt-get update -qq && apt-get install -y nodejs yarn RUN mkdir /calculator WORKDIR /calculator COPY Gemfile /calculator/Gemfile COPY Gemfile.lock /calculator/Gemfile.lock RUN bundle install COPY package.json /calculator/package.json COPY yarn.lock /calculator/yarn.lock RUN yarn install --check-files COPY . /calculator EXPOSE 3000 CMD ["rails", "server", "-b", "0.0.0.0"] हमारे द्वारा परिभाषित Dockerfile में Ruby 2.7 बेस इमेज है। हमें NodeJS और Yarn की आवश्यकता है, इसलिए अगला कदम उन्हें इंस्टॉल करना है। एक बार इंस्टॉल हो जाने पर, हम bundle install और yarn install चलाते हैं। अंत में, हम सभी कोड कॉपी करते हैं और 3000 पोर्ट एक्सपोर्ट करके Rails सर्वर चलाते हैं। यह एक बहुत ही सरल Dockerfile है। इसके साथ, हमारा Rails ऐप dockerized है।
अब हम docker इमेज बना और चला सकते हैं ताकि यह सुनिश्चित हो सके कि यह चल रहा है।
docker इमेज बनाएं:
docker build . -t calculator कंटेनर चलाएं:
docker run -p 3000:3000 calculator कैलकुलेशन लॉजिक के साथ मॉडल बनाएं
आइए calculation नामक एक मॉडल बनाएं जहां हम अपना कैलकुलेशन लॉजिक जोड़ सकें। हम इस मॉडल का उपयोग कुछ लॉजिक लिखने और उनके लिए टेस्ट लिखने के लिए करेंगे। हम देख सकते हैं कि हम Docker Compose का उपयोग करके इन टेस्ट्स की निर्भरताओं को कैसे प्रबंधित कर सकते हैं।
हम वर्तमान में SQLite के साथ Rails चला रहे हैं। आइए इसे SQLite के बजाय Postgres में बदलें। यह निम्न कमांड से आसानी से किया जा सकता है:
rails db:system:change --to=postgresql कैलकुलेशन मॉडल जनरेट करें:
bundle exec rails generate model calculation कैलकुलेशन स्कीमा में कुछ फ़ील्ड्स जोड़ें:
# db/migrate/create_calculation.rb class CreateCalculations < ActiveRecord::Migration[6.0] def change create_table :calculations do |t| t.integer :x t.integer :y t.integer :result t.timestamps end end end अब, कंटेनर के अंदर डेटाबेस माइग्रेशन चलाने का प्रयास करें:
docker-compose exec web rails db:migrate चूंकि डेटाबेस कंटेनर के अंदर नहीं चल रहा है, यह कमांड चलाने से एरर आएगा।
rake aborted! PG::ConnectionBad: could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"? हम Postgres इमेज को दूसरी इमेज के रूप में चला सकते हैं और उनके बीच एक नेटवर्क जोड़ सकते हैं। इस कंटेनर संचार को प्रबंधित करने के लिए, हम Docker Compose का उपयोग कर सकते हैं।
Docker Compose का उपयोग करके कई कंटेनर्स प्रबंधित करें
Rails ऐप के कंटेनराइज़ होने के साथ, हम Docker Compose का उपयोग करके सर्विस में निर्भरताएं जोड़ सकते हैं। एप्लिकेशन पर निर्भरता Postgres है।
Postgres Docker इमेज सार्वजनिक रूप से उपलब्ध है, इसलिए हमें इसे बनाने की आवश्यकता नहीं है। हालांकि, हमें डेटाबेस कंटेनर और हमारे Rails ऐप कंटेनर को प्रबंधित करने की आवश्यकता है।
version: "3.0" services: db: image: postgres environment: POSTGRES_USER: calculator POSTGRES_PASSWORD: password POSTGRES_DB: calculator_test web: build: . ports: - "3000:3000" depends_on: - db environment: CALCULATOR_DATABASE_PASSWORD: password अब, database.yaml फाइल पर जाएं और डेटाबेस को अलग होस्ट पर बदलें। होस्ट Docker Compose फाइल में सर्विस में निर्दिष्ट नाम होगा। हमारे मामले में, होस्ट db है। सुनिश्चित करें कि DB पासवर्ड postgres सर्विस और web सर्विस में समान है। साथ ही, उपयोगकर्ता नाम, पासवर्ड, और डेटाबेस नाम को Docker Compose फाइल में निर्दिष्ट अनुसार बदलें।
# database.yaml development: <<: *default database: calculator_development username: calculator password: <%= ENV['CALCULATOR_DATABASE_PASSWORD'] %> अब, हम एक ही कमांड का उपयोग करके कंटेनर्स शुरू कर सकते हैं:
docker-compose up यह डेटाबेस और हमारे Rails सर्वर दोनों को शुरू करेगा। हम अब माइग्रेशन कमांड चला सकते हैं, और यह Postgres से कनेक्ट करने और माइग्रेशन चलाने में सक्षम होना चाहिए।
टेस्ट्स जोड़ना
अब जब हमारे पास सभी सेटअप तैयार है, तो आइए मॉडल के अंदर एक सरल मेथड लिखें और मेथड को टेस्ट करें:
# calculation.rb class Calculation < ApplicationRecord def self.sum(x, y) result = x + y Calculation.create(x: x, y: y, result: result) result end end यह sum मेथड न केवल संख्याओं को जोड़ता है बल्कि संख्या को डेटाबेस में भी सहेजता है। इसलिए, इस मेथड को टेस्ट करने के लिए डेटाबेस इंस्टेंस का चलना आवश्यक है।
यह टेस्ट एक इंटीग्रेशन टेस्ट होगा, क्योंकि हमें डेटाबेस से कनेक्ट करना होगा।
हम टेस्ट लिखने के लिए Mini-test का उपयोग करेंगे, जो Rails का डिफ़ॉल्ट है।
# calculation_test.rb require 'test_helper' class CalculationTest < ActiveSupport::TestCase test "should sum and save the data" do result = Calculation.sum(1, 2) c = Calculation.last assert result, 3 assert c.result, result end end टेस्ट चलाएं:
docker-compose exec web rails test उपरोक्त टेस्ट में, हम टेस्ट कर रहे हैं कि क्या sum मेथड मानों को जोड़ रहा है और मानों को डेटाबेस में सहेज रहा है। Docker Compose के साथ, हमारे पास डेटाबेस से कनेक्ट करने का एक बहुत आसान तरीका है।
यहां, मेथड डेटाबेस पर निर्भर है। निर्भरता न केवल डेटाबेस बल्कि एक थर्ड-पार्टी सर्विस भी हो सकती है जो REST API प्रदान करती है। तो, आइए एक थर्ड-पार्टी सर्विस का उपयोग करने का प्रयास करें जो sum मेथड प्रदान करती है, जिसका उपयोग हम अपने खुद के लिखने के बजाय कर सकते हैं।
# calculation.rb class Calculation < ApplicationRecord def self.sum(x, y) # होस्टनाम वही होना चाहिए जैसा docker-compose फाइल में निर्दिष्ट है url = 'https://sumservice:4010/sum' uri = URI(url) params = { :x => x, :y => y } uri.query = URI.encode_www_form(params) res = Net::HTTP.get_response(uri) throw "Error" unless res.is_a?(Net::HTTPSuccess) result = res.body.to_i Calculation.create(x: x, y: y, result: result) result end end हम डेटाबेस के लिए जो किया था, उसके लिए REST API के साथ कुछ समान कर सकते हैं। हमें यह सुनिश्चित करने की आवश्यकता है कि ये वैध एंडपॉइंट्स हैं, लेकिन हम टेस्ट के दौरान वास्तविक सर्विस को कॉल नहीं करना चाहते। इस मामले के लिए, हम मॉक/स्टब टेस्ट बना सकते हैं, लेकिन यदि हम ऐसा करते हैं तो ये टेस्ट इंटीग्रेशन टेस्ट नहीं होंगे। हम यह सुनिश्चित करना चाहते हैं कि टेस्ट यथासंभव यथार्थवादी हो। इस उद्देश्य के लिए, हम एक मॉक API एंडपॉइंट बना सकते हैं, जिसे हम कंटेनर में चलाएंगे, और मॉक कंटेनर API सर्विस को कॉल करेंगे, जो हमें परिणाम के साथ प्रतिक्रिया देगा।
आइए पहले टेस्ट को संशोधित करें:
# calculation_test.rb require 'test_helper' class CalculationTest < ActiveSupport::TestCase test "should sum and save the data" do result = Calculation.sum(1, 2) c = Calculation.last // हमें इस एसर्शन की आवश्यकता नहीं है क्योंकि हम इस जिम्मेदारी को बाहरी सर्विस को सौंप रहे हैं: // assert result, 3 // आवश्यक नहीं assert c.result, result end end यहां, हमने टेस्ट केस को केवल यह टेस्ट करने के लिए संशोधित किया है कि sum API द्वारा प्रदान किया गया परिणाम डेटाबेस में ठीक से सहेजा गया है या नहीं। हमें यह टेस्ट करने की आवश्यकता नहीं है कि sum का वास्तविक मान सही है या नहीं, क्योंकि इसे बाहरी सर्विस द्वारा संभाला जाता है, और हम उन पर भरोसा करते हैं।
मॉक सर्वर जनरेट करने के लिए, हम openAPI का उपयोग करेंगे। आइए एक साधारण स्वैगर परिभाषा बनाएं:
रूट डायरेक्टरी में एक नया फोल्डर बनाएं और इसे mock नाम दें। फोल्डर के अंदर, api.yaml नाम की एक फाइल बनाएं
# api.yaml swagger: "2.0" info: description: "This is a sum api provider" version: "1.0.0" title: "API Provider" host: "https://mock-service.com/" basePath: "/api" schemes: - "https" - "http" paths: /sum: get: produces: - "application/json" parameters: - name: "x" in: "query" description: "x value" required: true type: "integer" - name: "y" in: "query" description: "y value" required: true type: "integer" responses: "200": description: "successful operation" schema: type: "integer" example: 3 इस सर्वर को स्पेसिफिकेशन के आधार पर चलाने के लिए, हम किसी भी मॉक सर्विस प्रोवाइडर का उपयोग कर सकते हैं। मैं prism नामक टूल का उपयोग करूंगा। इसका एक docker इमेज उपलब्ध है, जिसे हम अपने docker-compose फाइल में जोड़ सकते हैं:
version: "3.0" services: db: image: postgres environment: POSTGRES_USER: calculator POSTGRES_PASSWORD: password POSTGRES_DB: calculator_test web: build: . ports: - "3000:3000" depends_on: - db - sumservice environment: CALCULATOR_DATABASE_PASSWORD: password sumservice: image: stoplight/prism:4 command: mock -h 0.0.0.0 "/tmp/api.yaml" volumes: - ./mock:/tmp/ ports: - 4010:4010 init: true अब, टेस्ट चलाएं और देखें कि क्या यह अपेक्षा के अनुरूप काम कर रहा है:
> docker-compose exec web rails test Finished in 0.337710s, 2.9611 runs/s, 5.9222 assertions/s. 1 runs, 2 assertions, 0 failures, 0 errors, 0 skips ये! अब हमारे पास टेस्ट चल रहा है। मेथड योग प्राप्त करने के लिए sum सर्विस Docker इमेज से कनेक्ट होता है और डेटा सहेजने के लिए Postgres से कनेक्ट होता है। सभी निर्भरताओं को Docker Compose द्वारा प्रबंधित किया जाता है, इसलिए हमें पहले से इंस्टॉल की गई निर्भरताओं के बारे में चिंता करने की आवश्यकता नहीं है। इस टेस्ट को किसी भी मशीन पर ले जाने से बिना किसी निर्भरता इंस्टॉलेशन के काम करेगा। मशीन को केवल docker की आवश्यकता है।
यदि आपका एप्लिकेशन Redis या Memcached जैसी किसी अन्य निर्भरता पर निर्भर करता है, तो ये dockerhub पर पाए जा सकते हैं। हम आवश्यकतानुसार इन इमेजेज को अपने Docker Compose फाइल में जोड़ सकते हैं।
पैरेलल टेस्ट्स
Rails 5.1.5+ टेस्ट्स को समानांतर में चलाने का समर्थन करता है। डिफ़ॉल्ट रूप से, Rails 6 से प्रोसेसर की संख्या के आधार पर टेस्ट्स समानांतर में चलते हैं। टेस्ट चलाते समय, यह समानांतर टेस्ट्स की संख्या के आधार पर डेटाबेस इंस्टेंस बनाता है। यदि हमारे पास चार प्रोसेसर हैं, तो यह डेटाबेस "calculator_test-0", calculator_test-1, calculator_test-2, और calculator_test-3 बनाता है। यदि आप postgres cli पर जाते हैं और डेटाबेस चेक करते हैं, तो आप निम्न देखेंगे:
> \l List of databases Name | Owner | Encoding | Collate | Ctype | Access privileges -------------------+------------+----------+------------+------------+--------------------------- calculator_test | calculator | UTF8 | en_US.utf8 | en_US.utf8 | calculator_test-0 | calculator | UTF8 | en_US.utf8 | en_US.utf8 | calculator_test-1 | calculator | UTF8 | en_US.utf8 | en_US.utf8 | calculator_test-2 | calculator | UTF8 | en_US.utf8 | en_US.utf8 | calculator_test-3 | calculator | UTF8 | en_US.utf8 | en_US.utf8 | अब, भले ही आप Rails या mini-tests का उपयोग नहीं कर रहे हों या Rails का पुराना संस्करण हो, आप अभी भी Docker के साथ टेस्ट्स को समानांतर में चला सकते हैं यदि आप उन्हें Docker के साथ लिखते हैं। Rails स्वचालित रूप से पैरेलल टेस्ट्स के लिए डेटाबेस बनाता है, लेकिन यदि कोई अन्य निर्भरताएं हैं, जैसे Redis या memcached, तो उन्हें मैन्युअल रूप से प्रबंधित करना पड़ सकता है।
चूंकि हमारे पास सभी निर्भरताएं प्रबंधित हैं, हम Docker Compose को अलग-अलग आर्गुमेंट्स के साथ चला सकते हैं ताकि टेस्ट्स को समानांतर में चलाया जा सके। इस उद्देश्य के लिए, हम निर्दिष्ट कर सकते हैं कि हम किन फोल्डर्स पर टेस्ट्स को समानांतर में चलाना चाहते हैं, कई Docker Compose फाइलें बनाएं, और उन्हें समानांतर में चलाएं।
कमांड्स को सरल बनाने के लिए Makefile जोड़ना
चूंकि हम अपने टेस्ट्स चलाने के लिए Docker और Docker Compose कमांड्स का उपयोग कर रहे हैं, कमांड्स लंबी हैं। उन्हें याद रखना और दिन-प्रतिदिन के डेवलपमेंट के दौरान चलाना मुश्किल हो सकता है। इसे सरल कमांड में सरल बनाने के लिए, आइए Makefile का उपयोग करें। Makefile एक साधारण टेक्स्ट फाइल है जिसका उपयोग टार्गेट कमांड निष्पादन को संदर्भित करने के लिए किया जाता है।
.PHONY: test up: docker-compose up down: docker-compose down test: docker-compose exec web rails test हमने makefile में कुछ कमांड्स जोड़ी हैं। इसे एप्लिकेशन द्वारा आवश्यकतानुसार बढ़ाया जा सकता है। Makefile को Rails एप्लिकेशन के रूट डायरेक्टरी में जोड़ें। एक बार हमारे पास यह makefile होने पर, हम निम्न कमांड का उपयोग करके टेस्ट को सरलता से चला सकते हैं:
कंटेनर्स बूट करें:
> make up टेस्ट चलाएं:
> make test लोकल डेवलपमेंट के लिए Docker का उपयोग करना
हम एप्लिकेशन विकसित करते समय Docker का उपयोग भी कर सकते हैं। यह टेस्टिंग, डेवलपमेंट, और प्रोडक्शन में एप्लिकेशन के व्यवहार में स्थिरता सुनिश्चित करने में मदद करता है। आइए समीक्षा करें कि उपरोक्त कंटेनर को लोकल डेवलपमेंट के लिए उपयोग करने के लिए हमें क्या बदलने की आवश्यकता है।
वॉल्यूम माउंट
एप्लिकेशन को स्थानीय रूप से चलाते समय, हम चाहते हैं कि जब हम कोड में परिवर्तन करें तो एप्लिकेशन लोकल सर्वर में परिवर्तनों को प्रतिबिंबित करे। वर्तमान परिदृश्य में, कोड हमारी मशीन से Docker पर कॉपी किया जाता है, और सर्वर शुरू किया जाता है। इसलिए, यदि हम लोकल मशीन में फाइल में परिवर्तन करते हैं, तो वे Docker में प्रतिबिंबित नहीं होते हैं। यह सुनिश्चित करने के लिए कि कोड हमेशा सिंक में है, हम वॉल्यूम माउंट का उपयोग कर सकते हैं।
Docker Compose फाइल में, आइए वॉल्यूम जोड़ें:
version: "3.0" services: db: image: postgres environment: POSTGRES_USER: calculator POSTGRES_PASSWORD: password POSTGRES_DB: calculator_test web: build: . volumes: - .:/calculator # वॉल्यूम माउंटेड ports: - "3000:3000" depends_on: - db - sumservice environment: CALCULATOR_DATABASE_PASSWORD: password sumservice: image: stoplight/prism:4 command: mock -h 0.0.0.0 "/tmp/api.yaml" volumes: - ./mock:/tmp/ ports: - 4010:4010 init: true अब, यदि हम सर्वर में कुछ भी परिवर्तन करते हैं, तो परिवर्तन प्रतिबिंबित होंगे। आइए एक कंट्रोलर बनाकर और हमारे sum मेथड को कॉल करके इसे आज़माएं:
# controllers/calculation_controller.rb class CalculationController < ApplicationController def index result = Calculation.sum(params[:x], params[:y]) render json: {result: result} end end रूट रूट जोड़ें:
# routes.rb Rails.application.routes.draw do # इस फाइल में उपलब्ध DSL के विवरण के लिए, देखें https://guides.rubyonrails.org/routing.html root "calculation#index" end अब, यदि हम वापस जाकर ब्राउज़र में ऐप चेक करते हैं, तो हमें अपेक्षित प्रतिक्रिया मिलती है:

ध्यान दें कि हम मॉक sumservice का उपयोग करके सर्वर शुरू कर रहे हैं, इसलिए चाहे हम URL में क्वेरी पैरामीटर के रूप में कुछ भी प्रदान करें, यह हमेशा 2 लौटाएगा। यह डेवलपमेंट करने का अच्छा तरीका नहीं लगता है लेकिन मॉक सर्विस प्रोवाइडर का उपयोग करने के अपने फायदे हैं। माइक्रोसर्विसेज विकसित करते समय, बहुत सारी निर्भर सेवाएं हो सकती हैं, जिन्हें सर्विस शुरू करने से पहले बूट अप करने की आवश्यकता होगी। यह डेवलपमेंट के दौरान उत्पादकता बढ़ाने में मदद करता है। हम अन्य सर्विस के बारे में चिंतित नहीं हैं और केवल उस सर्विस के साथ हैं जिसे हम विकसित कर रहे हैं।
यदि हम वास्तविक सर्विस के साथ यह कैसे काम करता है इसका अंतिम सत्यापन करना चाहते हैं, तो हम एक नई Docker Compose फाइल बनाकर और इसे सर्विस की वास्तविक इमेज से बदलकर ऐसा कर सकते हैं। यदि यह आंतरिक सर्विस है, तो हम इसे केवल नवीनतम स्थिर Docker इमेज से बदल सकते हैं। मैं डेवलपमेंट एनवायरनमेंट में भी मॉक सर्विस रखने की सिफारिश करूंगा।
.dockerignore
जैसे-जैसे एप्लिकेशन बड़ा होता जाता है, निर्भरताएं Docker बिल्ड कॉन्टेक्स्ट को बड़ा कर देंगी। बड़े बिल्ड कॉन्टेक्स्ट के साथ, docker इमेज बनाने में अधिक समय लगता है। वॉल्यूम माउंट भी एप्लिकेशन के बढ़े हुए आकार के साथ देरी का कारण बन सकता है। इस मामले में, हम अवांछित फाइल को .dockerignore फाइल में जोड़कर बिल्ड कॉन्टेक्स्ट को कम कर सकते हैं।
.git* log/* README.md node_modules एंट्रीपॉइंट
जब एक Rails सर्वर कंटेनर में चल रहा होता है, तो हमें कभी-कभी एक एरर मैसेज मिलता है जिसमें कहा जाता है कि सर्वर पहले से ही चल रहा है क्योंकि PID फाइल कंटेनर के बाहर निकलने पर डिलीट नहीं हुई थी। इस एरर को हटाने के लिए, हम एक ENTRYPOINT जोड़ सकते हैं, जो server.pid फाइल को हटा देगा।
FROM ruby:2.7 RUN curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - RUN echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list RUN apt-get update -qq && apt-get install -y nodejs yarn RUN mkdir /calculator WORKDIR /calculator COPY Gemfile /calculator/Gemfile COPY Gemfile.lock /calculator/Gemfile.lock RUN bundle install COPY package.json /calculator/package.json COPY yarn.lock /calculator/yarn.lock RUN yarn install --check-files COPY . /calculator EXPOSE 3000 ENTRYPOINT ./entrypoint.sh CMD ["rails", "server", "-b", "0.0.0.0"] entrypoint.sh फाइल बनाएं:
#!/bin/bash set -e # Rails के लिए संभावित पूर्व-मौजूदा server.pid को हटाएं। rm -f /calculator/tmp/pids/server.pid # कंटेनर की मुख्य प्रक्रिया निष्पादित करें (rails फाइल में CMD कमांड) exec "$@" इसे एक्जीक्यूटेबल बनाएं
chmod +x entrypoint.sh अब, जब हम Docker इमेज चलाएंगे, तो हमें यह समस्या नहीं होगी।
निष्कर्ष
इंटीग्रेशन टेस्ट्स और यूनिट टेस्ट्स के बीच अंतर करना बहुत महत्वपूर्ण है। यूनिट टेस्ट्स लिखते समय, हम बाहरी निर्भरताओं, जैसे डेटाबेस को मॉक कर सकते हैं। यह कोड को आइसोलेशन में टेस्ट करने में मदद करेगा। कोर बिजनेस लॉजिक और इसकी निर्भरताओं के बीच इंटीग्रेशन टेस्ट चलाना भी उतना ही महत्वपूर्ण है। डेटाबेस और बाहरी सेवाओं जैसी निर्भरताओं का इंटीग्रेशन द्वारा Docker और Docker Compose का उपयोग करके टेस्ट किया जा सकता है। इन दोनों को अलग करना अच्छी प्रथा है। हम उन्हें अलग-अलग चलाने में सक्षम होना चाहिए। अधिक यूनिट टेस्ट्स और कम इंटीग्रेशन टेस्ट्स होने से आपके टेस्ट्स तेज़ और अधिक विश्वसनीय चलते हैं।
इस वर्कफ़्लो के कुछ फायदे इस प्रकार हैं:
- यूनिट और इंटीग्रेशन टेस्ट्स के बीच अलगाव।
- चूंकि सभी CI सेवाएं कंटेनर्स पर चलती हैं, लोकल और CI में चलने वाले टेस्ट्स का व्यवहार समान होगा। यह लोकल मशीन और CI सर्वर में टेस्ट व्यवहार की स्थिरता सुनिश्चित करने में मदद करता है।
- एप्लिकेशन को डिप्लॉय करने के लिए कंटेनर्स का उपयोग करना प्रोडक्शन सर्वर में स्थिरता सुनिश्चित करने में भी मदद करता है।
- विश्वसनीय टेस्ट्स, क्योंकि निर्भरताएं अच्छी तरह से प्रबंधित हैं।
- पोर्टेबल टेस्ट्स, जब कई डेवलपर्स द्वारा उपयोग किया जाता है तो सेटअप में कम प्रयास की आवश्यकता होती है।