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

نظرية
بفضل حقنة التبعية والموجهة نحو البروتوكول ، فإن جميع مكونات المشهد في Clean Swift مستقلة عن بعضها البعض ويمكن اختبارها بشكل منفصل. على سبيل المثال ، يعتمد Interactor على المقدم والعامل ، ولكن هذه التبعيات اختيارية وقائمة على البروتوكول. وبالتالي ، يمكن لـ Interactor القيام بعملها (على الرغم من أنه أقل شأنا) بدون Presenter'a و Worker'a ، ويمكننا أيضًا استبدالها بكائنات أخرى موقعة بموجب بروتوكولاتها.
بما أننا نرغب في اختبار كل مكون على حدة ، فنحن بحاجة إلى استبدال التبعيات بمكونات زائفة . هذا سوف يساعدنا الجواسيس (الجاسوس). تجسس هي كائنات الاختبار التي تنفذ البروتوكولات التي نريد حقن وتتبع طريقة المكالمات فيها. بمعنى آخر ، نقوم بإنشاء Spy لـ Presenter و Worker ، ثم نحقنها في Interactor لتتبع مكالمات الطريقة.

في الإنصاف ، سأضيف أن هناك أيضًا كائنات اختبار ( اختبار الزوجي ): الدمية ، المزيفة ، كعب الروتين . لكن ضمن إطار هذه المقالة ، لن نؤثر عليها. اقرأ المزيد هنا - TestDoubles
ممارسة
مع الانتهاء من الكلمات ، دعونا ننكب على العمل. لتوفير وقتك ، سننظر في الكود التجريدي دون الخوض في تفاصيل تنفيذ كل طريقة. يمكن العثور على تفاصيل رمز التطبيق هنا: CleanSwiftTests
لكل مكون من المشهد ، نقوم بإنشاء ملف مع اختبارات واختبار الزوجي لتبعيات المكون ( اختبار الزوجي ).
مثال على هذا الهيكل:

تبدو بنية كل ملف اختبار (للمكونات) كما هي وتلتزم بتسلسل الكتابة التالي تقريبًا:
- نعلن متغير مع SUT (الكائن الذي سنختبره) والمتغيرات مع التبعيات الرئيسية
- نقوم بتهيئة SUT وتبعياته في setUp () ، ثم نقوم بمسحها في tearDown ()
- طرق الاختبار
كما ناقشنا من الناحية النظرية ، يمكن اختبار كل مكون من مكونات المشهد بشكل منفصل. يمكننا حقن اختبار التكرارات ( الجاسوس ) في تبعياته ومن ثم مراقبة تشغيل طرق SUT لدينا. دعونا نلقي نظرة فاحصة على عملية كتابة الاختبارات باستخدام مثال Interactor للمشهد الرئيسي.
HomeInteractor يعتمد على كائنين - مقدم والعامل . كل المتغيرات في الفئة لديها نوع البروتوكول. هذا يعني أنه يمكننا إنشاء تكرارات اختبار موقعة بموجب بروتوكولات HomePresentationLogic و HomeWorkingLogic ، ثم ضخها في HomeInteractor .
final class HomeInteractor: HomeBusinessLogic, HomeDataStore {
سنختبر طريقتين:
- fetchUsers (:) . المسؤول عن الحصول على قائمة المستخدمين عن طريق API . يتم إرسال طلب API باستخدام Worker .
- selectUser (:) . مسؤول عن اختيار المستخدم النشط ( selectUser ) من قائمة المستخدمين المحملين ( المستخدمين ).
لبدء كتابة اختبارات Interactor ، نحتاج إلى إنشاء جواسيس لتتبع استدعاء الأساليب في HomePresentationLogic و HomeWorkingLogic . للقيام بذلك ، قم بإنشاء فئة HomePresentationLogicSpy في الدليل "CleanSwiftTestsTestsTests / Stores / Home / TestDoubles / Spies" ، قم بالتوقيع على البروتوكول HomePresentationLogic وقم بتنفيذ طريقة هذا البروتوكول.
final class HomePresentationLogicSpy: HomePresentationLogic {
كل شيء شفاف للغاية هنا. إذا تم استدعاء الأسلوب presentFetchedUsers ( HomePresentationLogic ) ، فسنقوم بتعيين قيمة المتغير isCalledPresentFetchedUsers إلى true . وبالتالي ، يمكننا تتبع ما إذا كان تم استدعاء هذه الطريقة أثناء اختبار Interactor .
باستخدام نفس المبدأ ، قم بإنشاء HomeWorkingLogicSpy . فرق واحد ، ونحن ندعو الانتهاء ، لأنه سيتم لف جزء من الكود في Interactor في إغلاق هذه الطريقة. تتعامل أساليب HomeWorkingLogic مع طلبات الشبكة. نحن بحاجة إلى تجنب طلبات الشبكة الحقيقية أثناء الاختبار. للقيام بذلك ، استبدلناها بأخذ اختبار ، والذي يتتبع مكالمات الطريقة ويعيد بيانات القالب ، لكنه لا يقدم أي طلبات إلى الشبكة.
final class HomeWorkingLogicSpy: HomeWorkingLogic {
بعد ذلك ، نقوم بإنشاء فئة HomeInteractorTests ، والتي سنقوم باختبار HomeInteractor بها .
final class HomeInteractorTests: XCTestCase {
نشير إلى ثلاثة متغيرات رئيسية - سوت ، عامل ومقدم .
في setUp () ، قم بتهيئة الكائنات الضرورية ، وحقن التبعيات في Interactor ، وقم بتعيين الكائنات لمتغيرات الفئة.
في tearDown () ، نقوم بمسح متغيرات الفصل لنقاء التجربة.
يتم استدعاء أسلوب setUp () قبل بدء طريقة الاختبار ، على سبيل المثال testFetchUsers () ، و tearDown () عندما تنتهي هذه الطريقة من عملها. وبالتالي ، فإننا نقوم بإعادة إنشاء كائن الاختبار ( sut ) قبل تشغيل كل طريقة اختبار.
التالي هي طرق الاختبار نفسها. ينقسم الهيكل إلى 3 كتل منطقية رئيسية - إنشاء الكائنات الضرورية ، وإطلاق الطريقة المختبرة في SUT والتحقق من النتائج. في المثال أدناه ، نقوم بإنشاء طلب (في حالتنا ، ليس له أي معلمات) ، وقم بتشغيل fetchUsers (:) أسلوب Interactor'a ، ثم تحقق مما إذا كانت الطرق الضرورية قد تم استدعاؤها في HomeWorkingLogicSpy و HomePresentationLogicSpy . نحن نتحقق أيضًا مما إذا كان Interactor قد حفظ بيانات الاختبار الواردة من Worker إلى DataStore الخاص به.
func testFetchUsers() { let request = HomeModels.FetchUsers.Request() sut.fetchUsers(request) XCTAssertTrue(worker.isCalledFetchUsers, "Not started worker.fetchUsers(:)") XCTAssertTrue(presenter.isCalledPresentFetchedUsers, "Not started presenter.presentFetchedUsers(:)") XCTAssertEqual(sut.users.count, worker.users.count) }
سنختبر اختيار المستخدم من خلال بنية مماثلة. نعلن عن المتغيرات توقع و اسم توقع ، حيث سنقوم بمقارنة نتيجة اختيار المستخدم. يقوم متغير المستخدمين بتخزين قائمة اختبار للمستخدمين الذين نقوم بتعيينهم إلى Interactor . لأن يتم استدعاء طرق الاختبار بشكل مستقل عن بعضها البعض ، وفي tearDown () نقوم بتفريغ البيانات ، ثم تكون قائمة مستخدمي Interactor فارغة ونحتاج إلى تعبئتها بشيء ما. ثم نتحقق مما إذا كان المستخدم قد تم تعيينه في DataStore Interactor'a ، بعد استدعاء sut.selectUser (:) ، وما إذا كان المستخدم هو الصحيح.
func testSelectUser() { let expectationId = 2 let expectationName = "Vasya" let users = [ User(id: 1, name: "Ivan", username: "ivan"), User(id: 2, name: "Vasya", username: "vasya91"), User(id: 3, name: "Maria", username: "maria_love") ] let request = HomeModels.SelectUser.Request(index: 1) sut.users = users sut.selectUser(request) XCTAssertNotNil(sut.selectedUser, "User not selected") XCTAssertEqual(sut.selectedUser?.id, expectationId) XCTAssertEqual(sut.selectedUser?.name, expectationName) }
اختبار مقدم العرض و ViewController'a يحدث على نفس المبدأ ، مع الحد الأدنى من الاختلافات. أحد الاختلافات هو أنه لاختبار ViewController ، ستحتاج إلى إنشاء UIWindow والحصول على جهاز التحكم من لوحة العمل في setUp () ، وكذلك إنشاء كائنات Spy على الجداول والمجموعات. لكن هذه الفروق الدقيقة تختلف عن الاحتياجات.
للتأكد من اكتمالها ، أوصي بأن تتعرف على المشروع من خلال الرابط الموجود في نهاية المقالة.
استنتاج
لقد قمنا بتغطية المبادئ الأساسية لاختبار التطبيقات على بنية Clean Swift . لا يوجد لديه اختلافات قوية بشكل أساسي من اختبار المشاريع على أبنية أخرى ، كل نفس الاختبار يتضاعف والحقن والبروتوكولات. الشيء الرئيسي هو عدم نسيان أن كل دورة من دورات VIP يجب أن يكون لها مسؤولية واحدة (وواحدة فقط!). وهذا سيجعل رمز نظافة والاختبارات أكثر وضوحا.
رابط للمشروع: CleanSwiftTests
مساعدة في كتابة مقال: باستيان
سلسلة من المقالات
- نظرة عامة على هندسة سويفت النظيفة
- توجيه البيانات ونقلها في النظيفة سويفت الهندسة المعمارية
- العمال في النظيفة سويفت الهندسة المعمارية
- اختبار وحدة في الهندسة النظيفة سويفت (أنت هنا)
- مثال على بنية متجر بسيط عبر الإنترنت Clean Swift