ضع تحضير التجهيزات المشتركة غير المتزامن في init() async throws لمجموعة Swift Testing. يُكمل Swift Testing هذا المُهيِّئ قبل استدعاء دالة اختبار خاصة بنسخة، لذلك تتلقى الدالة حالة جاهزة للاستخدام. في XCTest، استخدم setUp() async throws بدلًا من ذلك.

هذا الدليل موجّه إلى مطوّر Mac يجهّز بيانات اختبارات الوحدات محليًا في Xcode. تتمثل المهمة في إكمال التهيئة غير المتزامنة قبل بدء التأكيدات، باستخدام حسابات حتمية ومدخلات اصطناعية. توثّق Apple أسلوب المُهيِّئ في ترحيل اختبار من XCTest.

كيف ينتظر Swift Testing تهيئة التجهيزات؟

بالنسبة إلى دالة اختبار خاصة بنسخة، يهيّئ Swift Testing نوع المجموعة الحاوية، ثم يستدعي الدالة على تلك النسخة. يجب أن يكون للمجموعة مُهيِّئ يمكن استدعاؤه من دون وسيطات؛ ويمكن أن يكون هذا المُهيِّئ غير متزامن أو قابلًا للرمي أو كليهما. تعمل كل دالة اختبار خاصة بنسخة على نسخة مستقلة. هذه قواعد موثقة لدورة الحياة في تنظيم دوال الاختبار باستخدام أنواع المجموعات.

الاعتماد الناتج واضح: ينتهي تحضير التجهيزات، ويكتمل المُهيِّئ، ثم تبدأ دالة الاختبار. يجب أن تنتظر عملية التحضير فعليًا باستخدام await في المُهيِّئ. مجرد بدء العمل داخله لا ينشئ هذا الاعتماد.

استخدم دليل القرار التالي لاختيار موضع التحضير:

الحالة موضع التحضير ما الذي يثبت الجاهزية؟
تحتاج عدة اختبارات خاصة بنسخ إلى وصفة التجهيز نفسها init() async throws للمجموعة يكتمل المُهيِّئ المنتظَر قبل كل دالة
يحتاج اختبار واحد فقط إلى التجهيز متن اختبار async throws try await صريح قبل التأكيدات
اختبار حالي يرث من XCTestCase setUp() async throws يُكمل XCTest الإعداد غير المتزامن قبل الدالة
اختبار Swift Testing هو static أو اختبار عام متن الاختبار أو مساعد يُستدعى صراحةً ينتظر الاختبار التحضير صراحةً

يطبّق هذا الجدول قواعد دورة الحياة الموثقة في الإطارين على موضع التجهيزات. وبالتحديد، مُهيِّئ المجموعة هو نقطة تحضير لكل نسخة؛ وهذا لا يعني أن التحضير يحدث مرة واحدة للمجموعة بأكملها. راجع قواعد تهيئة المجموعة وتسلسل الإعداد في XCTest من Apple.

أضف مثالًا كاملًا لتجهيز غير متزامن في Xcode

استخدم Xcode 16 أو أحدث مع دعم Swift Testing المضمّن فيه. يوضح مشروع Swift أن Swift Testing مضمّن مع Swift 6 وXcode 16، وأنه لا حاجة إلى اعتماد إضافي على حزمة. راجع ملف README الرسمي لـ Swift Testing.

في هدف اختبارات وحدات موجود، اختر File > New > New File From Template، ثم حدد Swift Testing Unit Test. استبدل محتويات الملف المُنشأ بالمثال الكامل أدناه. ويمكن لحزمة اختبارات وحدات XCTest الموجودة أن تحتوي أيضًا على اختبارات Swift Testing؛ فلا حاجة إلى حزمة منفصلة. تتبع هذه الخطوات إضافة اختبارات إلى مشروع Xcode من Apple.

import Testing

enum FixtureError: Error {
    case invalidInput
}

private actor SquareCalculator {
    let values: [Int]

    init(values: [Int]) {
        self.values = values
    }

    func squares() -> [Int] {
        values.map { $0 * $0 }
    }
}

struct CalculationFixture {
    let squares: [Int]
    let total: Int

    static func make(values: [Int]) async throws -> Self {
        guard !values.isEmpty,
              values.count <= 100,
              values.allSatisfy({ (0...100).contains($0) }) else {
            throw FixtureError.invalidInput
        }

        let calculator = SquareCalculator(values: values)
        let squares = await calculator.squares()

        return Self(
            squares: squares,
            total: squares.reduce(0, +)
        )
    }
}

@Suite
struct CalculationTests {
    let fixture: CalculationFixture

    init() async throws {
        fixture = try await CalculationFixture.make(values: [2, 4, 6])
    }

    @Test
    func squaresAreReady() {
        #expect(fixture.squares == [4, 16, 36])
    }

    @Test
    func totalIsReady() {
        #expect(fixture.total == 56)
    }
}

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

يرفض مُنشئ التجهيزات المدخلات الفارغة، وأكثر من 100 قيمة، والقيم خارج 0...100. تحافظ هذه الحدود التوضيحية على صغر العمليات الحسابية: فمربع كل قيمة لا يتجاوز 10,000 والمجموع لا يتجاوز 1,000,000. تنتج عن القيم غير الصالحة أخطاء بدلًا من حذفها أو استبدالها بصمت.

بالنسبة إلى المدخل المعطى، ناتج الحساب المتوقع هو 2² + 4² + 6² = 4 + 16 + 36 = 56. يخزّن المُهيِّئ التجهيز المكتمل في خاصية let غير اختيارية. لذلك لا تحتاج دوال الاختبار إلى علامة جاهزية أو فك تغليف اختياري.

يمكن أن تظل الدوال نفسها متزامنة لأن متطلبها السابق غير المتزامن اكتمل بالفعل في المُهيِّئ. وعندما يكون العمل غير المتزامن داخل متن الاختبار، فإن النمط الموثق من Apple هو تعريف تلك الدالة على أنها async وانتظار العملية مباشرةً. راجع اختبار الشيفرة غير المتزامنة.

تم التحقق محليًا من الحساب الاصطناعي باستخدام Python. لغة Swift غير متاحة في بيئة البحث هذه، لذا جرت مراجعة أمثلة Swift، لكن لم تُجمّع أو تُنفّذ هنا. تصف النتائج المتوقعة أعلاه حسابات المثال، ولا تمثل تشغيلًا مُبلّغًا عنه لاختبارات Xcode.

لماذا لا يعني بدء مهمة أن الإعداد اكتمل؟

من الأخطاء الشائعة تشغيل Task { ... } من مُهيِّئ متزامن ثم العودة فورًا. عندئذ يكون المُهيِّئ قد انتهى من تشغيل العمل، لكن ربما يظل هذا العمل جاريًا عند بدء دالة الاختبار.

يصف دليل لغة Swift Task { ... } بأنه ينشئ مهمة غير منظّمة. ويمكن استخدام المقبض الذي يعيده لانتظار النتيجة عبر await handle.value. يؤدي تجاهل المقبض إلى إزالة نقطة الانتظار الصريحة هذه. راجع قسم التزامن غير المنظّم في كتاب Swift الرسمي.

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

إضافة فترة نوم ثابتة لا تعبّر عن هذا الاعتماد. فهي تنتظر مرور الوقت، وتترك الجاهزية متوقفة على ما إذا كان التحضير سينتهي خلال تلك المدة. وبالمثل، فإن جعل دالة الاختبار async يتيح الاستدعاءات غير المتزامنة فحسب؛ وما زال على الدالة انتظار العملية التي تعتمد عليها.

ما الإعداد المكافئ في XCTest؟

يوفر XCTest نقطة إعداد مخصصة غير متزامنة. والترتيب الموثق قبل كل دالة اختبار هو setUp() async throws، ثم setUpWithError()، ثم setUp() المتزامن. تأتي دالة الاختبار بعد هذه النقاط. راجع إعداد الحالة وإنهاؤها في اختباراتك.

لمقارنة التجهيز نفسه في XCTest، أضف الملف المصاحب الكامل التالي إلى هدف اختبارات الوحدات نفسه المستخدم في المثال الأول. يستورد الملف XCTest ويستخدم CalculationFixture المعرّف سابقًا والمتاح على مستوى الوحدة. أبقِ تعريفات المساعد في الملف الأول، ولا تكررها.

import XCTest

final class CalculationXCTests: XCTestCase {
    private var fixture: CalculationFixture?

    override func setUp() async throws {
        try await super.setUp()
        fixture = try await CalculationFixture.make(values: [2, 4, 6])
    }

    func testTotalIsReady() throws {
        let readyFixture = try XCTUnwrap(fixture)
        XCTAssertEqual(readyFixture.squares, [4, 16, 36])
        XCTAssertEqual(readyFixture.total, 56)
    }
}

يمكن أن تكون دالة XCTest متزامنة أيضًا: إذ يحدث التحضير غير المتزامن في الإعداد. وتتيح الخاصية الاختيارية مرحلة الإعداد المنفصلة في XCTest، بينما يجعل XCTUnwrap غياب التجهيز إخفاقًا صريحًا. توثّق Apple نمط فك التغليف هذا في دليل الترحيل.

عند تحويل هذا الاختبار إلى Swift Testing، انقل التحضير إلى مُهيِّئ المجموعة واستخدم import Testing و@Test وتوقعات Swift Testing. الدالة التي يُكتفى بتسميتها setUp في مجموعة Swift Testing عادية ليست آلية الإعداد الموثقة؛ إذ يستبدل مسار الترحيل من Apple عائلة دوال الإعداد بالتهيئة.

تحقّق من هذه الحالات عندما يبدو أن التهيئة تسبق أوانها

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

ثانيًا، افحص سلسلة استدعاءات التحضير كاملةً. اعثر على العملية التي تنتج الحالة الجاهزة فعليًا، وتأكد من أن كل مستدعٍ ينتظرها. في المثال، يكتمل حساب الممثل قبل أن يعود المصنع، ويعود المصنع قبل اكتمال المُهيِّئ. لا يتسرب أي عمل تحتاج إليه التأكيدات خارج هذا التسلسل.

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

أخيرًا، ميّز بين ترتيب التهيئة وجدولة الاختبارات. يشغّل Swift Testing الاختبارات بالتوازي افتراضيًا. ويمكن لسمة .serialized للمجموعة تشغيل الاختبارات التابعة لها بالتسلسل، لكن التسلسل لا يحوّل المهمة التي لم يُنتظر اكتمالها إلى تحضير منتظَر، ولا يجعل مُهيِّئ المجموعة يعمل مرة واحدة فقط. توثّق Apple نطاق السمة في تشغيل الاختبارات بالتسلسل أو بالتوازي.

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

المصادر