मराठी

मजबूत आणि विश्वसनीय सॉफ्टवेअर डेव्हलपमेंटसाठी तुमच्या टेस्टिंग स्ट्रॅटेजीमध्ये मॉक फंक्शन्सचा प्रभावीपणे वापर कसा करायचा ते शिका. हे मार्गदर्शक मॉक्स कधी, का आणि कसे लागू करावे हे उदाहरणांसह स्पष्ट करते.

मॉक फंक्शन्स: डेव्हलपर्ससाठी एक सर्वसमावेशक मार्गदर्शक

सॉफ्टवेअर डेव्हलपमेंटच्या जगात, मजबूत आणि विश्वसनीय कोड लिहिणे हे सर्वात महत्त्वाचे आहे. हे ध्येय साध्य करण्यासाठी सखोल टेस्टिंग अत्यंत आवश्यक आहे. विशेषतः, युनिट टेस्टिंग हे स्वतंत्र घटकांची किंवा फंक्शन्सची वेगळेपणाने चाचणी करण्यावर लक्ष केंद्रित करते. तथापि, वास्तविक-जगातील ऍप्लिकेशन्समध्ये अनेकदा गुंतागुंतीच्या डिपेंडन्सीज (dependencies) सामील असतात, ज्यामुळे युनिट्सची पूर्णपणे वेगळी चाचणी करणे आव्हानात्मक बनते. इथेच मॉक फंक्शन्स उपयोगी पडतात.

मॉक फंक्शन्स म्हणजे काय?

मॉक फंक्शन हे एका खऱ्या फंक्शनचे सिम्युलेटेड (simulated) व्हर्जन आहे जे तुम्ही तुमच्या टेस्ट्समध्ये वापरू शकता. प्रत्यक्ष फंक्शनची लॉजिक कार्यान्वित करण्याऐवजी, मॉक फंक्शन तुम्हाला त्याचे वर्तन नियंत्रित करण्याची, ते कसे कॉल केले जात आहे हे पाहण्याची आणि त्याचे रिटर्न व्हॅल्यूज (return values) परिभाषित करण्याची परवानगी देते. ते टेस्ट डबल (test double) चा एक प्रकार आहेत.

याचा विचार असा करा: कल्पना करा की तुम्ही कारच्या इंजिनची (चाचणी अंतर्गत युनिट) चाचणी करत आहात. इंजिन इतर विविध घटकांवर अवलंबून असते, जसे की इंधन इंजेक्शन सिस्टम आणि कूलिंग सिस्टम. इंजिन चाचणी दरम्यान प्रत्यक्ष इंधन इंजेक्शन आणि कूलिंग सिस्टम चालवण्याऐवजी, तुम्ही मॉक सिस्टम वापरू शकता जे त्यांच्या वर्तनाचे अनुकरण करतात. यामुळे तुम्हाला इंजिन वेगळे करून विशेषतः त्याच्या कामगिरीवर लक्ष केंद्रित करता येते.

मॉक फंक्शन्स खालील गोष्टींसाठी शक्तिशाली साधने आहेत:

मॉक फंक्शन्स कधी वापरावीत

मॉक फंक्शन्स या परिस्थितीत सर्वात उपयुक्त आहेत:

१. बाह्य डिपेंडन्सीजसह युनिट्स वेगळे करणे

जेव्हा तुमचे चाचणी अंतर्गत युनिट बाह्य सेवा, डेटाबेस, एपीआय (APIs) किंवा इतर घटकांवर अवलंबून असते, तेव्हा टेस्टिंग दरम्यान वास्तविक डिपेंडन्सीज वापरल्याने अनेक समस्या उद्भवू शकतात:

उदाहरण: कल्पना करा की तुम्ही रिमोट API वरून वापरकर्त्याचा डेटा मिळवणाऱ्या फंक्शनची चाचणी करत आहात. टेस्टिंग दरम्यान प्रत्यक्ष API कॉल्स करण्याऐवजी, तुम्ही API प्रतिसादाचे अनुकरण करण्यासाठी मॉक फंक्शन वापरू शकता. यामुळे तुम्हाला बाह्य API च्या उपलब्धतेवर किंवा कार्यक्षमतेवर अवलंबून न राहता फंक्शनच्या लॉजिकची चाचणी करता येते. हे विशेषतः महत्त्वाचे आहे जेव्हा API ला रेट लिमिट्स किंवा प्रत्येक विनंतीसाठी संबंधित खर्च असतो.

२. गुंतागुंतीच्या परस्परसंवादांची चाचणी करणे

काही प्रकरणांमध्ये, तुमचे चाचणी अंतर्गत युनिट इतर घटकांशी गुंतागुंतीच्या मार्गांनी संवाद साधू शकते. मॉक फंक्शन्स तुम्हाला या परस्परसंवादांचे निरीक्षण आणि पडताळणी करण्याची परवानगी देतात.

उदाहरण: पेमेंट ट्रान्झॅक्शनवर प्रक्रिया करणाऱ्या फंक्शनचा विचार करा. हे फंक्शन पेमेंट गेटवे, डेटाबेस आणि नोटिफिकेशन सेवेसह संवाद साधू शकते. मॉक फंक्शन्स वापरून, तुम्ही हे सत्यापित करू शकता की फंक्शन योग्य ट्रान्झॅक्शन तपशिलांसह पेमेंट गेटवेला कॉल करते, ट्रान्झॅक्शनच्या स्थितीसह डेटाबेस अपडेट करते आणि वापरकर्त्याला नोटिफिकेशन पाठवते.

३. एरर परिस्थितींचे अनुकरण करणे

तुमच्या ॲप्लिकेशनची मजबुती सुनिश्चित करण्यासाठी एरर हाताळणीची (error handling) चाचणी करणे महत्त्वाचे आहे. मॉक फंक्शन्समुळे वास्तविक वातावरणात पुनरुत्पादित करणे कठीण किंवा अशक्य असलेल्या एरर परिस्थितींचे अनुकरण करणे सोपे होते.

उदाहरण: समजा तुम्ही क्लाउड स्टोरेज सेवेवर फाइल्स अपलोड करणाऱ्या फंक्शनची चाचणी करत आहात. अपलोड प्रक्रियेदरम्यान नेटवर्क एररचे अनुकरण करण्यासाठी तुम्ही मॉक फंक्शन वापरू शकता. यामुळे तुम्हाला हे सत्यापित करता येते की फंक्शन एरर योग्यरित्या हाताळते, अपलोड पुन्हा प्रयत्न करते किंवा वापरकर्त्याला सूचित करते.

४. असिंक्रोनस कोडची चाचणी करणे

असिंक्रोनस कोड, जसे की कॉलबॅक, प्रॉमिस किंवा async/await वापरणारा कोड, चाचणीसाठी आव्हानात्मक असू शकतो. मॉक फंक्शन्स तुम्हाला असिंक्रोनस ऑपरेशन्सची वेळ आणि वर्तन नियंत्रित करण्यात मदत करू शकतात.

उदाहरण: कल्पना करा की तुम्ही असिंक्रोनस विनंती वापरून सर्व्हरवरून डेटा मिळवणाऱ्या फंक्शनची चाचणी करत आहात. तुम्ही सर्व्हर प्रतिसादाचे अनुकरण करण्यासाठी आणि प्रतिसाद कधी परत केला जातो हे नियंत्रित करण्यासाठी मॉक फंक्शन वापरू शकता. यामुळे तुम्हाला फंक्शन वेगवेगळ्या प्रतिसाद परिस्थिती आणि टाइमआउट्स कसे हाताळते याची चाचणी करता येते.

५. अनपेक्षित साईड इफेक्ट्स टाळणे

कधीकधी, टेस्टिंग दरम्यान प्रत्यक्ष फंक्शन कॉल केल्याने अनपेक्षित दुष्परिणाम (side effects) होऊ शकतात, जसे की डेटाबेसमध्ये बदल करणे, ईमेल पाठवणे किंवा बाह्य प्रक्रिया सुरू करणे. मॉक फंक्शन्स तुम्हाला प्रत्यक्ष फंक्शनला नियंत्रित सिम्युलेशनने बदलण्याची परवानगी देऊन हे साईड इफेक्ट्स टाळतात.

उदाहरण: तुम्ही नवीन वापरकर्त्यांना स्वागत ईमेल पाठवणाऱ्या फंक्शनची चाचणी करत आहात. मॉक ईमेल सेवा वापरून, तुम्ही हे सुनिश्चित करू शकता की ईमेल पाठवण्याचे कार्य तुमच्या टेस्ट स्यूट रन दरम्यान प्रत्यक्ष वापरकर्त्यांना ईमेल पाठवत नाही. त्याऐवजी, तुम्ही हे सत्यापित करू शकता की फंक्शन योग्य माहितीसह ईमेल पाठवण्याचा प्रयत्न करते.

मॉक फंक्शन्स कसे वापरावे

मॉक फंक्शन्स वापरण्याच्या विशिष्ट पायऱ्या तुम्ही वापरत असलेल्या प्रोग्रामिंग लँग्वेज आणि टेस्टिंग फ्रेमवर्कवर अवलंबून असतात. तथापि, सामान्य प्रक्रियेमध्ये सामान्यतः खालील पायऱ्या समाविष्ट असतात:

  1. डिपेंडन्सीज ओळखा: तुम्हाला कोणत्या बाह्य डिपेंडन्सीज मॉक करण्याची आवश्यकता आहे ते ठरवा.
  2. मॉक ऑब्जेक्ट्स तयार करा: प्रत्यक्ष डिपेंडन्सीज बदलण्यासाठी मॉक ऑब्जेक्ट्स किंवा फंक्शन्स तयार करा. या मॉक्समध्ये अनेकदा `called`, `returnValue` आणि `callArguments` सारखे गुणधर्म असतात.
  3. मॉक वर्तन कॉन्फिगर करा: मॉक फंक्शन्सचे वर्तन परिभाषित करा, जसे की त्यांचे रिटर्न व्हॅल्यूज, एरर परिस्थिती आणि कॉल संख्या.
  4. मॉक्स इंजेक्ट करा: तुमच्या चाचणी अंतर्गत युनिटमधील प्रत्यक्ष डिपेंडन्सीज मॉक ऑब्जेक्ट्सने बदला. हे अनेकदा डिपेंडन्सी इंजेक्शन वापरून केले जाते.
  5. टेस्ट कार्यान्वित करा: तुमची टेस्ट चालवा आणि चाचणी अंतर्गत युनिट मॉक फंक्शन्सशी कसे संवाद साधते ते पहा.
  6. परस्परसंवाद सत्यापित करा: मॉक फंक्शन्स अपेक्षित आर्ग्युमेंट्स, रिटर्न व्हॅल्यूज आणि संख्येसह कॉल केले गेले होते का ते सत्यापित करा.
  7. मूळ कार्यक्षमता पुनर्संचयित करा: टेस्टनंतर, मॉक ऑब्जेक्ट्स काढून आणि प्रत्यक्ष डिपेंडन्सीजवर परत जाऊन मूळ कार्यक्षमता पुनर्संचयित करा. हे इतर टेस्ट्सवरील साईड इफेक्ट्स टाळण्यास मदत करते.

वेगवेगळ्या भाषांमधील मॉक फंक्शनची उदाहरणे

येथे लोकप्रिय प्रोग्रामिंग भाषा आणि टेस्टिंग फ्रेमवर्कमध्ये मॉक फंक्शन्स वापरण्याची उदाहरणे आहेत:

जावास्क्रिप्ट Jest सह

Jest हे एक लोकप्रिय जावास्क्रिप्ट टेस्टिंग फ्रेमवर्क आहे जे मॉक फंक्शन्ससाठी अंगभूत समर्थन प्रदान करते.

// Function to test
function fetchData(callback) {
  setTimeout(() => {
    callback('Data from server');
  }, 100);
}

// Test case
test('fetchData calls callback with correct data', (done) => {
  const mockCallback = jest.fn();
  fetchData(mockCallback);

  setTimeout(() => {
    expect(mockCallback).toHaveBeenCalledWith('Data from server');
    done();
  }, 200);
});

या उदाहरणात, `jest.fn()` एक मॉक फंक्शन तयार करते जे प्रत्यक्ष कॉलबॅक फंक्शनला बदलते. टेस्ट हे सत्यापित करते की मॉक फंक्शनला `toHaveBeenCalledWith()` वापरून योग्य डेटासह कॉल केले गेले आहे.

मॉड्यूल्स वापरून अधिक प्रगत उदाहरण:

// user.js
import { getUserDataFromAPI } from './api';

export async function displayUserName(userId) {
  const userData = await getUserDataFromAPI(userId);
  return userData.name;
}

// api.js
export async function getUserDataFromAPI(userId) {
  // Simulate API call
  return new Promise(resolve => {
    setTimeout(() => {
      resolve({ id: userId, name: 'John Doe' });
    }, 50);
  });
}

// user.test.js
import { displayUserName } from './user';
import * as api from './api';

describe('displayUserName', () => {
  it('should display the user name', async () => {
    // Mock the getUserDataFromAPI function
    const mockGetUserData = jest.spyOn(api, 'getUserDataFromAPI');
    mockGetUserData.mockResolvedValue({ id: 123, name: 'Mocked Name' });

    const userName = await displayUserName(123);
    expect(userName).toBe('Mocked Name');

    // Restore the original function
    mockGetUserData.mockRestore();
  });
});

येथे, `./api` मॉड्यूलमधून आयात केलेल्या `getUserDataFromAPI` फंक्शनसाठी मॉक फंक्शन तयार करण्यासाठी `jest.spyOn` वापरला जातो. मॉकचे रिटर्न व्हॅल्यू निर्दिष्ट करण्यासाठी `mockResolvedValue` वापरला जातो. इतर टेस्ट्स अनवधानाने मॉक केलेली आवृत्ती वापरणार नाहीत याची खात्री करण्यासाठी `mockRestore` आवश्यक आहे.

पायथन pytest आणि unittest.mock सह

पायथनमध्ये मॉकिंगसाठी अनेक लायब्ररी आहेत, ज्यात `unittest.mock` (अंगभूत) आणि pytest सह सोप्या वापरासाठी `pytest-mock` सारख्या लायब्ररींचा समावेश आहे.

# Function to test
def get_data_from_api(url):
    # In a real scenario, this would make an API call
    # For simplicity, we simulate an API call
    if url == "https://example.com/api":
        return {"data": "API data"}
    else:
        return None

def process_data(url):
    data = get_data_from_api(url)
    if data:
        return data["data"]
    else:
        return "No data found"

# Test case using unittest.mock
import unittest
from unittest.mock import patch

class TestProcessData(unittest.TestCase):
    @patch('__main__.get_data_from_api') # Replace get_data_from_api in the main module
    def test_process_data_success(self, mock_get_data_from_api):
        # Configure the mock
        mock_get_data_from_api.return_value = {"data": "Mocked data"}

        # Call the function being tested
        result = process_data("https://example.com/api")

        # Assert the result
        self.assertEqual(result, "Mocked data")
        mock_get_data_from_api.assert_called_once_with("https://example.com/api")

    @patch('__main__.get_data_from_api')
    def test_process_data_failure(self, mock_get_data_from_api):
        mock_get_data_from_api.return_value = None
        result = process_data("https://example.com/api")
        self.assertEqual(result, "No data found")

if __name__ == '__main__':
    unittest.main()

हे उदाहरण `get_data_from_api` फंक्शनला मॉकने बदलण्यासाठी `unittest.mock.patch` वापरते. टेस्ट मॉकला विशिष्ट व्हॅल्यू परत करण्यासाठी कॉन्फिगर करते आणि नंतर `process_data` फंक्शन अपेक्षित परिणाम परत करते का हे सत्यापित करते.

हेच उदाहरण `pytest-mock` वापरून:

# pytest version
import pytest

def get_data_from_api(url):
    # In a real scenario, this would make an API call
    # For simplicity, we simulate an API call
    if url == "https://example.com/api":
        return {"data": "API data"}
    else:
        return None

def process_data(url):
    data = get_data_from_api(url)
    if data:
        return data["data"]
    else:
        return "No data found"


def test_process_data_success(mocker):
    mocker.patch('__main__.get_data_from_api', return_value={"data": "Mocked data"})
    result = process_data("https://example.com/api")
    assert result == "Mocked data"


def test_process_data_failure(mocker):
    mocker.patch('__main__.get_data_from_api', return_value=None)
    result = process_data("https://example.com/api")
    assert result == "No data found"

`pytest-mock` लायब्ररी `mocker` फिक्स्चर प्रदान करते जे pytest टेस्ट्समध्ये मॉक्स तयार करणे आणि कॉन्फिगर करणे सोपे करते.

जावा Mockito सह

Mockito हे जावासाठी एक लोकप्रिय मॉकिंग फ्रेमवर्क आहे.

import org.junit.jupiter.api.Test;
import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

interface DataFetcher {
    String fetchData(String url);
}

class DataProcessor {
    private final DataFetcher dataFetcher;

    public DataProcessor(DataFetcher dataFetcher) {
        this.dataFetcher = dataFetcher;
    }

    public String processData(String url) {
        String data = dataFetcher.fetchData(url);
        if (data != null) {
            return "Processed: " + data;
        } else {
            return "No data";
        }
    }
}

public class DataProcessorTest {

    @Test
    public void testProcessDataSuccess() {
        // Create a mock DataFetcher
        DataFetcher mockDataFetcher = mock(DataFetcher.class);

        // Configure the mock
        when(mockDataFetcher.fetchData("https://example.com/api")).thenReturn("API Data");

        // Create the DataProcessor with the mock
        DataProcessor dataProcessor = new DataProcessor(mockDataFetcher);

        // Call the function being tested
        String result = dataProcessor.processData("https://example.com/api");

        // Assert the result
        assertEquals("Processed: API Data", result);

        // Verify that the mock was called
        verify(mockDataFetcher).fetchData("https://example.com/api");
    }

    @Test
    public void testProcessDataFailure() {
        DataFetcher mockDataFetcher = mock(DataFetcher.class);
        when(mockDataFetcher.fetchData("https://example.com/api")).thenReturn(null);

        DataProcessor dataProcessor = new DataProcessor(mockDataFetcher);
        String result = dataProcessor.processData("https://example.com/api");
        assertEquals("No data", result);
        verify(mockDataFetcher).fetchData("https://example.com/api");
    }
}

या उदाहरणात, `Mockito.mock()` हे `DataFetcher` इंटरफेससाठी मॉक ऑब्जेक्ट तयार करते. `when()` मॉकचे रिटर्न व्हॅल्यू कॉन्फिगर करण्यासाठी वापरले जाते, आणि `verify()` मॉक अपेक्षित आर्ग्युमेंट्ससह कॉल झाला होता का हे सत्यापित करण्यासाठी वापरले जाते.

मॉक फंक्शन्स वापरण्यासाठी सर्वोत्तम पद्धती

मॉक फंक्शन्सचे पर्याय

मॉक फंक्शन्स हे एक शक्तिशाली साधन असले तरी, ते नेहमीच सर्वोत्तम उपाय नसतात. काही प्रकरणांमध्ये, इतर तंत्रे अधिक योग्य असू शकतात:

निष्कर्ष

मॉक फंक्शन्स प्रभावी युनिट टेस्ट्स लिहिण्यासाठी एक आवश्यक साधन आहे, जे तुम्हाला युनिट्स वेगळे करण्यास, वर्तन नियंत्रित करण्यास, एरर परिस्थितींचे अनुकरण करण्यास आणि असिंक्रोनस कोडची चाचणी करण्यास सक्षम करते. सर्वोत्तम पद्धतींचे पालन करून आणि पर्याय समजून घेऊन, तुम्ही अधिक मजबूत, विश्वसनीय आणि देखरेख करण्यायोग्य सॉफ्टवेअर तयार करण्यासाठी मॉक फंक्शन्सचा फायदा घेऊ शकता. सर्वसमावेशक आणि प्रभावी टेस्टिंग स्ट्रॅटेजी तयार करण्यासाठी प्रत्येक परिस्थितीसाठी योग्य टेस्टिंग तंत्र निवडण्याचे आणि ट्रेड-ऑफ विचारात घेण्याचे लक्षात ठेवा, तुम्ही जगाच्या कोणत्याही भागातून काहीही तयार करत असाल तरीही.