Xcode में हर परिदृश्य का अपना परिणाम और उसे फिर से चलाने का नियंत्रण पाने के लिए, लूप-आधारित XCTest यूनिट टेस्ट को Swift Testing के पैरामीटरयुक्त @Test में बदलें। यह चरण-दर-चरण मार्गदर्शिका माइग्रेशन दिखाने और उसके रिपोर्टिंग व्यवहार की पुष्टि करने के लिए स्थानीय टाइल-लेआउट गणना का उपयोग करती है।
XCTest लूप हर परिदृश्य की अलग रिपोर्ट क्यों नहीं करता?
XCTest, XCTestCase सबक्लास पर टेस्ट मेथड खोजता है। इन मेथड में पैरामीटर नहीं होते और इनके नाम test से शुरू होते हैं। खोजे गए एक मेथड के भीतर का लूप उसी मेथड का हिस्सा रहता है; उसकी पुनरावृत्तियाँ अलग-अलग खोजे गए टेस्ट नहीं बनतीं। किसी assertion में परिदृश्य का नाम जोड़ने से निदान बेहतर हो सकता है, लेकिन इससे उसे अलग से चलाने का नियंत्रण नहीं मिलता। Apple का Defining Test Cases and Test Methods देखें।
जीवनचक्र की सीमा भी यही है। XCTest हर टेस्ट मेथड से पहले उसके इंस्टेंस सेटअप मेथड चलाता है और बाद में टियरडाउन मेथड। इसलिए पाँच पुनरावृत्तियों वाले लूप को अपने-आप पाँच सेटअप और टियरडाउन चक्र नहीं मिलते। यदि कोई पुनरावृत्ति फिक्स्चर में बदलाव करती है, तो टेस्ट उसे स्पष्ट रूप से रीसेट न करे तो अगली पुनरावृत्ति वही बदली हुई स्थिति पा सकती है। Apple ने यह क्रम Set Up and Tear Down State in Your Tests में बताया है।
Swift Testing यहाँ आवश्यक परिणाम देता है: एक फ़ंक्शन परिदृश्य आर्ग्युमेंट स्वीकार करता है और रनर उसकी हर कॉल को स्वतंत्र केस मानता है। Xcode उनके आर्ग्युमेंट दिखाता है और अलग-अलग चलाने के नियंत्रण देता है। Apple Go further with Swift Testing में यह व्यवहार दिखाता है।
विफलताओं का पता लगाते समय यह अंतर मायने रखता है। Assertion की विफलता और समय से पहले बाहर निकलना अलग घटनाएँ हैं: सामान्य assertion के बाद लूप जाँच जारी रख सकता है, जबकि फेंकी गई त्रुटि या स्पष्ट return आगे की पुनरावृत्तियाँ रोक सकता है। परिदृश्यों को अलग-अलग केस में बाँटने से उनके बीच यह साझा नियंत्रण प्रवाह हट जाता है। इससे प्रक्रिया समाप्त करने वाली विफलता को संभालने योग्य नहीं बनाया जा सकता।
मौजूदा यूनिट-टेस्ट टार्गेट तैयार करें
इस कार्यप्रवाह के लिए Xcode 16 या उसके बाद का संस्करण इस्तेमाल करें। Swift Testing इन Xcode वितरणों में और Swift.org टूलचेन में Swift 6.0 से शामिल है। Swift प्रोजेक्ट सामान्य परीक्षण कार्य के लिए अंतर्निहित कॉपी इस्तेमाल करने की सलाह देता है; इस उदाहरण को किसी बाहरी पैकेज निर्भरता की ज़रूरत नहीं है। Obtaining Swift Testing देखें।
मौजूदा यूनिट-टेस्ट बंडल बनाए रखें। Apple स्पष्ट रूप से एक ही टार्गेट में Swift Testing और XCTest के इस्तेमाल का समर्थन करता है, इसलिए क्रमिक माइग्रेशन एक नई Swift फ़ाइल से शुरू हो सकता है। Xcode में File > New > New File From Template चुनें, Swift Testing Unit Test चुनें और फ़ाइल को मौजूदा यूनिट-टेस्ट टार्गेट में जोड़ें। यदि प्रोजेक्ट में कोई टेस्ट टार्गेट नहीं है, तो File > New > Target के ज़रिए Unit Testing Bundle बनाएँ। ये चरण Adding tests to your Xcode project में दर्ज हैं।
यह मार्गदर्शिका सीधे गणना कोड का परीक्षण करती है। यह डेस्कटॉप विंडो को स्वचालित नहीं करती। UI स्वचालन के लिए Apple का दस्तावेज़ीकृत तरीका XCTest का उपयोग जारी रखता है; लेआउट तय करने वाले मूल लॉजिक के लिए यह पैरामीटरयुक्त तरीका अपनाएँ। वही guide to adding tests इस फ़्रेमवर्क की सीमा बताती है।
हर परिदृश्य को पूरा आर्ग्युमेंट रिकॉर्ड बनाएँ
मान लें कि Mac ऐप टाइलों को पंक्तियों में व्यवस्थित करता है। चार कॉलम के साथ, शून्य टाइलों के लिए शून्य पंक्तियाँ चाहिए, चार टाइलें एक पंक्ति भरती हैं और पाँच टाइलों के लिए दो पंक्तियाँ चाहिए। ठोस लक्ष्य यह है कि इन जाँचों को बनाए रखते हुए हर परिदृश्य को अलग से दिखाया और चलाया जा सके।
एक ही रिकॉर्ड में परिदृश्य का नाम, इनपुट और अपेक्षित आउटपुट रखें। इससे केस जोड़ने या उनका क्रम बदलने पर अपेक्षित परिणाम अपने इनपुट के साथ जुड़ा रहता है। नीचे के पाँच रिकॉर्ड उदाहरण के लिए टेस्ट डेटा हैं, किसी मौजूदा ऐप से लिए गए अवलोकन नहीं।
| परिदृश्य | टाइलों की संख्या | कॉलम | अपेक्षित पंक्तियाँ | उद्देश्य |
|---|---|---|---|---|
empty |
0 | 4 | 0 | खाली इनपुट |
one-tile |
1 | 4 | 1 | पहली आंशिक पंक्ति |
exact-row |
4 | 4 | 1 | ठीक सीमा पर |
wrap |
5 | 4 | 2 | सीमा से आगे की पहली टाइल |
two-full-rows |
8 | 4 | 2 | पूरा गुणज |
इस पूरी, स्वयंपूर्ण फ़ाइल को यूनिट-टेस्ट टार्गेट में रखें। इसमें एक कृत्रिम कार्यान्वयन शामिल है, ताकि इसे ऐप मॉड्यूल या अन्य फ़ाइलों के बिना आज़माया जा सके। इसमें किसी सेवा, मॉक, फ़ाइल या नेटवर्क एक्सेस का उपयोग नहीं होता।
import Testing
private struct TileLayout {
func rowCount(tileCount: Int, columns: Int) -> Int {
precondition(tileCount >= 0)
precondition(columns > 0)
let fullRows = tileCount / columns
let partialRow = tileCount % columns == 0 ? 0 : 1
return fullRows + partialRow
}
}
private struct LayoutScenario:
Codable, Sendable, CustomTestStringConvertible {
let name: String
let tileCount: Int
let columns: Int
let expectedRows: Int
var testDescription: String { name }
static let examples: [LayoutScenario] = [
.init(name: "empty", tileCount: 0,
columns: 4, expectedRows: 0),
.init(name: "one-tile", tileCount: 1,
columns: 4, expectedRows: 1),
.init(name: "exact-row", tileCount: 4,
columns: 4, expectedRows: 1),
.init(name: "wrap", tileCount: 5,
columns: 4, expectedRows: 2),
.init(name: "two-full-rows", tileCount: 8,
columns: 4, expectedRows: 2)
]
}
struct TileLayoutTests {
@Test("Tile rows", arguments: LayoutScenario.examples)
fileprivate func rowCountMatchesScenario(
_ scenario: LayoutScenario
) {
let layout = TileLayout()
let actualRows = layout.rowCount(
tileCount: scenario.tileCount,
columns: scenario.columns
)
#expect(actualRows == scenario.expectedRows)
}
}
माइग्रेशन में पुनरावृत्ति को @Test(arguments:) में ले जाया जाता है: हर तत्व एक कॉल का आर्ग्युमेंट बनता है। चुनिंदा केस फिर से चलाने के लिए Codable एन्कोडिंग सहायता देता है, जबकि CustomTestStringConvertible पढ़ने योग्य परिदृश्य लेबल देता है। Apple पैरामीटराइज़ेशन को Implementing parameterized tests और कस्टम आर्ग्युमेंट विवरण को Go further with Swift Testing में समझाता है।
फिक्स्चर जानबूझकर टेस्ट फ़ंक्शन के भीतर बनाया गया है। हर कॉल अपना TileLayout बनाती है; कुछ भी पिछले परिदृश्य पर निर्भर नहीं है। यदि मौजूदा फिक्स्चर माइग्रेट कर रहे हैं, तो उसका सेटअप सोच-समझकर ले जाएँ; यह अपेक्षा न करें कि XCTest के विरासत में मिले हुक चलेंगे। Swift Testing सुइट XCTestCase से इनहेरिट नहीं करते; Apple Migrating a test from XCTest में बताता है कि सेटअप को initializer और क्लास या actor टियरडाउन को deinit में कैसे बदला जाता है।
यह गणना गैर-ऋणात्मक टाइल संख्या और शून्य से बड़ी कॉलम संख्या स्वीकार करती है। इसकी precondition यही अनुबंध तय करती हैं; अमान्य मान इस उदाहरण के केस से बाहर हैं। यदि वास्तविक ऐप को अमान्य इनपुट पर सुधार योग्य त्रुटि लौटानी चाहिए, तो इस तालिका को बढ़ाने से पहले API अनुबंध बदलें और संबंधित टेस्ट जोड़ें।
अलग-अलग परिणामों की पुष्टि करें और विफल केस को फिर से चलाएँ
पैरामीटरयुक्त फ़ंक्शन को उसकी घोषणा के पास वाले डायमंड से चलाएँ। फिर Test navigator खोलें और उसके केस देखने के लिए फ़ंक्शन को विस्तृत करें। एक केस चलाने के लिए, उस केस के डायमंड पर पॉइंटर ले जाएँ और उसके प्ले नियंत्रण पर क्लिक करें। पैरेंट फ़ंक्शन का प्ले नियंत्रण क्लिक करने से उसके सभी केस चलते हैं। Apple इन दोनों क्रियाओं को Running tests and interpreting results में दर्ज करता है।
रिपोर्टिंग की नियंत्रित जाँच के लिए, अस्थायी रूप से wrap रिकॉर्ड का expectedRows, 2 से बदलकर 3 करें। पूरा पैरामीटरयुक्त फ़ंक्शन चलाएँ। अपेक्षित परिणाम पाँच केस निष्पादन हैं; wrap तुलना विफल होनी चाहिए, क्योंकि कार्यान्वयन 2 लौटाता है। उस केस को चुनकर अकेले फिर से चलाएँ, फिर अपेक्षित मान को 2 पर वापस रखें। यह जानबूझकर किया गया टेस्ट-डेटा बदलाव है, कार्यान्वयन में दोष का प्रमाण नहीं।
गणना किए गए मान की परिदृश्य इनपुट से तुलना करने के लिए विफल अपेक्षा के Results और Arguments विवरण देखें। पूरे रन के लिए View > Navigators > Reports खोलें, टेस्ट ऐक्शन चुनें और उसका Tests सेक्शन देखें। इन निरीक्षण नियंत्रणों का वर्णन Apple की test-results guide में है।
चुनिंदा निष्पादन के लिए पढ़ने योग्य लेबल से अतिरिक्त आवश्यकता है: रनर को हर आर्ग्युमेंट से निश्चित रूप से मेल बैठाना होगा। Apple का मौजूदा दस्तावेज़ CustomTestArgumentEncodable, कुछ RawRepresentable प्रकारों, Encodable या एन्कोड किए जा सकने वाले identifier वाले Identifiable को स्वीकार करता है। उदाहरण का Codable रिकॉर्ड एन्कोडिंग वाले विकल्प को पूरा करता है। केवल कस्टम डिस्प्ले स्ट्रिंग इस आवश्यकता को पूरा नहीं करती। Implementing parameterized tests देखें।
पाँचों रिकॉर्ड के अपेक्षित अंकगणित की स्थानीय रूप से Python integer division और remainder से जाँच की गई। केवल floor division का उपयोग करने वाला उदाहरणार्थ गलत कार्यान्वयन one-tile और wrap में विफल होता है। Swift फ़ाइल का निरीक्षण किया गया, लेकिन इस शोध परिवेश में उसे कंपाइल नहीं किया गया क्योंकि Swift उपलब्ध नहीं था; ऊपर दिया गया Xcode रन ही निष्पादन योग्य सत्यापन चरण है।
अपेक्षित केस संख्या और जीवनचक्र बनाए रखें
माइग्रेशन की एक उपयोगी जाँच तीन बातों की तुलना करती है: परिदृश्य रिकॉर्ड की संख्या, पूरे रन में निष्पादित केसों की संख्या, और यह कि चुना हुआ केस अकेले चल सकता है या नहीं। इस उदाहरण में अपेक्षित संख्या पाँच है। यदि संख्या अलग हो, तो गणना बदलने से पहले आर्ग्युमेंट निर्माण और टेस्ट चयन की जाँच करें।
एक-दूसरे से जुड़ी अपेक्षाएँ साथ रखें। arguments: में दो अलग संग्रह देने से उनका Cartesian product बनता है। पाँच इनपुट और पाँच अपेक्षित आउटपुट देने पर पाँच मिलान वाले परिदृश्यों के बजाय 25 संयोजन बनेंगे। हर संयोजन अर्थपूर्ण हो तभी अलग संग्रह इस्तेमाल करें; युग्मित डेटा के लिए रिकॉर्ड या zip() इस्तेमाल करें। Apple यह अंतर Implementing parameterized tests में समझाता है।
यदि zip() इस्तेमाल कर रहे हैं, तो युग्म बनाने से पहले संग्रहों की लंबाई जाँचें: Swift छोटी सीक्वेंस पर रुक जाता है। इससे अतिरिक्त अपेक्षित परिणाम बिना कोई केस बनाए अनुपयोगी रह सकता है। एकल परिदृश्य रिकॉर्ड से यह खास असंगति नहीं होती। यह छँटाई व्यवहार Swift के zip(::) संदर्भ में दर्ज है।
निष्पादन का क्रम भी माइग्रेशन का निर्णय बदलता है। Swift Testing डिफ़ॉल्ट रूप से केस समानांतर चलाता है। इस उदाहरण में आर्ग्युमेंट अपरिवर्तनीय हैं और फिक्स्चर स्थानीय है, इसलिए परिणाम क्रम पर निर्भर नहीं करते। यदि किसी पैरामीटरयुक्त फ़ंक्शन को अस्थायी रूप से क्रमिक निष्पादन चाहिए, तो उसके @Test attribute में .serialized जोड़ें। यह trait किसी सुइट पर लगाने से उसमें शामिल टेस्ट और केस भी क्रमिक रूप से चलते हैं, लेकिन असंबंधित सुइट क्रमिक नहीं होते। Running tests serially or in parallel देखें।
क्रमिक निष्पादन से नई स्थिति नहीं बनती और न ही परिदृश्यों के बीच निर्भरता का अनुबंध तय होता है। यदि कोई केस तभी सफल होता है जब दूसरे केस ने कुछ बदला हो, तो उसकी व्यवस्था नए सिरे से बनाएँ ताकि वह स्वतंत्र रूप से चल सके। यदि चरण जानबूझकर एक सतत कार्रवाई बनाते हैं, तो उन्हें एक टेस्ट में रखें और पूरी कार्रवाइयों को पैरामीटरयुक्त करें।
इस माइग्रेट किए गए उदाहरण में हर जगह Swift Testing assertions का इस्तेमाल करें। XCTest assertions वाले हेल्पर दोबारा इस्तेमाल करते समय टूलचेन के अंतर-संचालन व्यवहार की जाँच करें: Apple की मौजूदा माइग्रेशन गाइड ऐसे मोड और डिफ़ॉल्ट बताती है जो टूलचेन और पैकेज टूल्स संस्करण के अनुसार बदलते हैं। इस फ़ाइल को पूरी तरह Testing में रखने से यह निर्भरता नहीं रहती। Migrating a test from XCTest देखें।
यदि माइग्रेशन टालना पड़े, तो समर्थित XCTest विकल्प है हर परिदृश्य के लिए एक नामित, पैरामीटर-रहित टेस्ट मेथड बनाना और ज़रूरत के अनुसार गणना तथा assertion हेल्पर साझा करना। Apple अपनी guide to adding tests में हर परिदृश्य के लिए एक टेस्ट की सलाह देता है। इससे Swift Testing पैरामीटराइज़ेशन के बिना भी अलग-अलग मेथड परिणाम मिलते हैं।
रूपांतरण तब पूरा होता है जब हर मूल परिदृश्य के अनुरूप निष्पादित केस मौजूद हो, उसका फिक्स्चर इच्छित स्थिति से शुरू हो और चुना हुआ केस अपने-आप सफलतापूर्वक चल सके। बदलाव के दौरान सीमा वाले इनपुट और अपेक्षित आउटपुट बनाए रखें: अलग-अलग रिपोर्टिंग उपयोगी है क्योंकि इससे मौजूदा जाँचों को ढूँढ़ना, निदान करना और दोहराना आसान होता है।
स्रोत
- Defining Test Cases and Test Methods — Apple
- Set Up and Tear Down State in Your Tests — Apple
- Go further with Swift Testing — Apple
- Obtaining Swift Testing — Swift project
- Adding tests to your Xcode project — Apple
- Implementing parameterized tests — Apple
- Migrating a test from XCTest — Apple
- Running tests and interpreting results — Apple
- zip(::) — Apple
- Running tests serially or in parallel — Apple