العربية

تعلم كيفية استخدام الدوال الوهمية بفعالية في استراتيجية الاختبار الخاصة بك لتطوير برامج قوية وموثوقة. يغطي هذا الدليل متى ولماذا وكيفية تنفيذ الدوال الوهمية مع أمثلة عملية.

الدوال الوهمية (Mock Functions): دليل شامل للمطورين

في عالم تطوير البرمجيات، تعد كتابة أكواد قوية وموثوقة أمرًا بالغ الأهمية. والاختبار الشامل ضروري لتحقيق هذا الهدف. يركز اختبار الوحدات (Unit testing)، على وجه الخصوص، على اختبار المكونات أو الدوال الفردية بشكل منعزل. ومع ذلك، غالبًا ما تتضمن التطبيقات في العالم الحقيقي تبعيات معقدة، مما يجعل من الصعب اختبار الوحدات في عزلة تامة. وهنا يأتي دور الدوال الوهمية.

ما هي الدوال الوهمية؟

الدالة الوهمية هي نسخة محاكاة من دالة حقيقية يمكنك استخدامها في اختباراتك. بدلاً من تنفيذ منطق الدالة الفعلي، تتيح لك الدالة الوهمية التحكم في سلوكها، ومراقبة كيفية استدعائها، وتحديد قيمها المرجعة. إنها نوع من بدائل الاختبار (test double).

فكر في الأمر على هذا النحو: تخيل أنك تختبر محرك سيارة (الوحدة قيد الاختبار). يعتمد المحرك على مكونات أخرى مختلفة، مثل نظام حقن الوقود ونظام التبريد. بدلاً من تشغيل أنظمة حقن الوقود والتبريد الفعلية أثناء اختبار المحرك، يمكنك استخدام أنظمة وهمية تحاكي سلوكها. هذا يسمح لك بعزل المحرك والتركيز بشكل خاص على أدائه.

الدوال الوهمية هي أدوات قوية من أجل:

متى تستخدم الدوال الوهمية

تكون الدوال الوهمية أكثر فائدة في هذه الحالات:

1. عزل الوحدات ذات التبعيات الخارجية

عندما تعتمد وحدتك قيد الاختبار على خدمات خارجية أو قواعد بيانات أو واجهات برمجة تطبيقات (APIs) أو مكونات أخرى، فإن استخدام التبعيات الحقيقية أثناء الاختبار يمكن أن يسبب عدة مشاكل:

مثال: تخيل أنك تختبر دالة تسترد بيانات المستخدم من واجهة برمجة تطبيقات بعيدة. بدلاً من إجراء استدعاءات فعلية لواجهة برمجة التطبيقات أثناء الاختبار، يمكنك استخدام دالة وهمية لمحاكاة استجابة الواجهة. هذا يسمح لك باختبار منطق الدالة دون الاعتماد على توفر أو أداء الواجهة الخارجية. هذا مهم بشكل خاص عندما يكون للواجهة حدود للمعدل (rate limits) أو تكاليف مرتبطة بكل طلب.

2. اختبار التفاعلات المعقدة

في بعض الحالات، قد تتفاعل وحدتك قيد الاختبار مع مكونات أخرى بطرق معقدة. تسمح لك الدوال الوهمية بمراقبة هذه التفاعلات والتحقق منها.

مثال: فكر في دالة تعالج معاملات الدفع. قد تتفاعل هذه الدالة مع بوابة دفع وقاعدة بيانات وخدمة إشعارات. باستخدام الدوال الوهمية، يمكنك التحقق من أن الدالة تستدعي بوابة الدفع بتفاصيل المعاملة الصحيحة، وتحدّث قاعدة البيانات بحالة المعاملة، وترسل إشعارًا إلى المستخدم.

3. محاكاة حالات الخطأ

يعد اختبار معالجة الأخطاء أمرًا بالغ الأهمية لضمان قوة تطبيقك. تجعل الدوال الوهمية من السهل محاكاة حالات الخطأ التي يصعب أو يستحيل إعادة إنتاجها في بيئة حقيقية.

مثال: لنفترض أنك تختبر دالة ترفع الملفات إلى خدمة تخزين سحابية. يمكنك استخدام دالة وهمية لمحاكاة خطأ في الشبكة أثناء عملية الرفع. هذا يسمح لك بالتحقق من أن الدالة تعالج الخطأ بشكل صحيح، أو تعيد محاولة الرفع، أو تخطر المستخدم.

4. اختبار الكود غير المتزامن

يمكن أن يكون اختبار الكود غير المتزامن، مثل الكود الذي يستخدم دوال رد النداء (callbacks) أو الوعود (promises) أو async/await، أمرًا صعبًا. يمكن أن تساعدك الدوال الوهمية في التحكم في توقيت وسلوك العمليات غير المتزامنة.

مثال: تخيل أنك تختبر دالة تجلب البيانات من خادم باستخدام طلب غير متزامن. يمكنك استخدام دالة وهمية لمحاكاة استجابة الخادم والتحكم في وقت إرجاع الاستجابة. هذا يسمح لك باختبار كيفية تعامل الدالة مع سيناريوهات الاستجابة المختلفة والمهلات الزمنية.

5. منع الآثار الجانبية غير المقصودة

في بعض الأحيان، يمكن أن يكون لاستدعاء دالة حقيقية أثناء الاختبار آثار جانبية غير مقصودة، مثل تعديل قاعدة بيانات أو إرسال رسائل بريد إلكتروني أو تشغيل عمليات خارجية. تمنع الدوال الوهمية هذه الآثار الجانبية من خلال السماح لك باستبدال الدالة الحقيقية بمحاكاة خاضعة للرقابة.

مثال: أنت تختبر دالة ترسل رسائل بريد إلكتروني ترحيبية للمستخدمين الجدد. باستخدام خدمة بريد إلكتروني وهمية، يمكنك التأكد من أن وظيفة إرسال البريد الإلكتروني لا ترسل بالفعل رسائل بريد إلكتروني إلى مستخدمين حقيقيين أثناء تشغيل مجموعة الاختبارات الخاصة بك. بدلاً من ذلك، يمكنك التحقق من أن الدالة تحاول إرسال البريد الإلكتروني بالمعلومات الصحيحة.

كيفية استخدام الدوال الوهمية

تعتمد الخطوات المحددة لاستخدام الدوال الوهمية على لغة البرمجة وإطار الاختبار الذي تستخدمه. ومع ذلك، تتضمن العملية العامة عادةً الخطوات التالية:

  1. تحديد التبعيات: حدد التبعيات الخارجية التي تحتاج إلى محاكاتها.
  2. إنشاء كائنات وهمية: أنشئ كائنات أو دوالاً وهمية لتحل محل التبعيات الحقيقية. غالبًا ما تحتوي هذه الدوال الوهمية على خصائص مثل `called` و `returnValue` و `callArguments`.
  3. تكوين سلوك الدالة الوهمية: حدد سلوك الدوال الوهمية، مثل قيمها المرجعة وحالات الخطأ وعدد الاستدعاءات.
  4. حقن الدوال الوهمية: استبدل التبعيات الحقيقية بالكائنات الوهمية في وحدتك قيد الاختبار. غالبًا ما يتم ذلك باستخدام حقن التبعية (dependency injection).
  5. تنفيذ الاختبار: قم بتشغيل اختبارك وراقب كيف تتفاعل الوحدة قيد الاختبار مع الدوال الوهمية.
  6. التحقق من التفاعلات: تحقق من أن الدوال الوهمية قد تم استدعاؤها بالوسائط المتوقعة والقيم المرجعة والعدد المطلوب من المرات.
  7. استعادة الوظائف الأصلية: بعد الاختبار، استعد الوظائف الأصلية عن طريق إزالة الكائنات الوهمية والعودة إلى التبعيات الحقيقية. هذا يساعد على تجنب الآثار الجانبية على الاختبارات الأخرى.

أمثلة على الدوال الوهمية في لغات مختلفة

فيما يلي أمثلة على استخدام الدوال الوهمية في لغات البرمجة وأطر الاختبار الشائعة:

جافاسكريبت مع Jest

Jest هو إطار عمل اختبار جافاسكريبت شائع يوفر دعمًا مدمجًا للدوال الوهمية.

// دالة للاختبار
function fetchData(callback) {
  setTimeout(() => {
    callback('بيانات من الخادم');
  }, 100);
}

// حالة الاختبار
test('fetchData تستدعي دالة رد النداء بالبيانات الصحيحة', (done) => {
  const mockCallback = jest.fn();
  fetchData(mockCallback);

  setTimeout(() => {
    expect(mockCallback).toHaveBeenCalledWith('بيانات من الخادم');
    done();
  }, 200);
});

في هذا المثال، `jest.fn()` تنشئ دالة وهمية تحل محل دالة رد النداء الحقيقية. يتحقق الاختبار من أن الدالة الوهمية قد تم استدعاؤها بالبيانات الصحيحة باستخدام `toHaveBeenCalledWith()`.

مثال أكثر تقدمًا باستخدام الوحدات (modules):

// 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) {
  // محاكاة استدعاء API
  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('يجب أن يعرض اسم المستخدم', async () => {
    // محاكاة دالة getUserDataFromAPI
    const mockGetUserData = jest.spyOn(api, 'getUserDataFromAPI');
    mockGetUserData.mockResolvedValue({ id: 123, name: 'اسم وهمي' });

    const userName = await displayUserName(123);
    expect(userName).toBe('اسم وهمي');

    // استعادة الدالة الأصلية
    mockGetUserData.mockRestore();
  });
});

هنا، يتم استخدام `jest.spyOn` لإنشاء دالة وهمية لدالة `getUserDataFromAPI` المستوردة من وحدة `./api`. يتم استخدام `mockResolvedValue` لتحديد القيمة المرجعة للدالة الوهمية. `mockRestore` ضرورية لضمان عدم استخدام الاختبارات الأخرى للنسخة الوهمية عن غير قصد.

بايثون مع pytest و unittest.mock

توفر بايثون العديد من المكتبات للمحاكاة، بما في ذلك `unittest.mock` (مدمجة) ومكتبات مثل `pytest-mock` للاستخدام المبسط مع pytest.

# دالة للاختبار
def get_data_from_api(url):
    # في سيناريو حقيقي، هذا سيقوم باستدعاء API
    # للتبسيط، نحاكي استدعاء API
    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"

# حالة الاختبار باستخدام unittest.mock
import unittest
from unittest.mock import patch

class TestProcessData(unittest.TestCase):
    @patch('__main__.get_data_from_api') # استبدال get_data_from_api في الوحدة الرئيسية
    def test_process_data_success(self, mock_get_data_from_api):
        # تكوين الدالة الوهمية
        mock_get_data_from_api.return_value = {"data": "Mocked data"}

        # استدعاء الدالة قيد الاختبار
        result = process_data("https://example.com/api")

        # التأكد من النتيجة
        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()

يستخدم هذا المثال `unittest.mock.patch` لاستبدال دالة `get_data_from_api` بدالة وهمية. يقوم الاختبار بتكوين الدالة الوهمية لإرجاع قيمة محددة ثم يتحقق من أن دالة `process_data` تعيد النتيجة المتوقعة.

وهنا نفس المثال باستخدام `pytest-mock`:

# نسخة pytest
import pytest

def get_data_from_api(url):
    # في سيناريو حقيقي، هذا سيقوم باستدعاء API
    # للتبسيط، نحاكي استدعاء API
    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() {
        // إنشاء كائن DataFetcher وهمي
        DataFetcher mockDataFetcher = mock(DataFetcher.class);

        // تكوين الكائن الوهمي
        when(mockDataFetcher.fetchData("https://example.com/api")).thenReturn("API Data");

        // إنشاء DataProcessor مع الكائن الوهمي
        DataProcessor dataProcessor = new DataProcessor(mockDataFetcher);

        // استدعاء الدالة قيد الاختبار
        String result = dataProcessor.processData("https://example.com/api");

        // التأكد من النتيجة
        assertEquals("Processed: API Data", result);

        // التحقق من أن الكائن الوهمي قد تم استدعاؤه
        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()` للتحقق من أن الكائن الوهمي قد تم استدعاؤه بالوسائط المتوقعة.

أفضل الممارسات لاستخدام الدوال الوهمية

بدائل للدوال الوهمية

في حين أن الدوال الوهمية أداة قوية، إلا أنها ليست دائمًا الحل الأفضل. في بعض الحالات، قد تكون تقنيات أخرى أكثر ملاءمة:

الخلاصة

الدوال الوهمية هي أداة أساسية لكتابة اختبارات وحدات فعالة، مما يتيح لك عزل الوحدات، والتحكم في السلوك، ومحاكاة حالات الخطأ، واختبار الكود غير المتزامن. من خلال اتباع أفضل الممارسات وفهم البدائل، يمكنك الاستفادة من الدوال الوهمية لبناء برامج أكثر قوة وموثوقية وقابلية للصيانة. تذكر أن تأخذ في الاعتبار الموازنات واختيار تقنية الاختبار المناسبة لكل موقف لإنشاء استراتيجية اختبار شاملة وفعالة، بغض النظر عن أي جزء من العالم تبني منه.

الدوال الوهمية (Mock Functions): دليل شامل للمطورين | MLOG