سؤال

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

لقد قمت بتطوير منشئ أكواد برمجية ينقل واجهة python الخاصة بنا إلى كود C++ الخاص بنا (الذي تم إنشاؤه عبر SWIG) ويقوم بإنشاء التعليمات البرمجية اللازمة لكشف هذا كخدمات ويب.عندما قمت بتطوير هذا الكود، قمت بذلك باستخدام TDD، لكنني وجدت أن اختباراتي هشة للغاية.نظرًا لأن كل اختبار أراد بشكل أساسي التحقق من أنه بالنسبة لجزء معين من كود الإدخال (والذي يصادف أنه رأس C++) سأحصل على جزء معين من الكود الناتج، فقد كتبت محركًا صغيرًا يقرأ تعريفات الاختبار من ملفات إدخال XML ويقوم بإنشاء اختبار حالات من هذه التوقعات.

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

لذا أحاول التفكير في طرق بديلة لهذه المشكلة، ويبدو لي أنني ربما أتعامل معها بطريقة خاطئة.ربما أحتاج إلى التركيز أكثر على النتيجة، IE:هل يتم تشغيل الكود الذي أقوم بإنشائه فعليًا ويفعل ما أريده، بدلاً من أن يبدو الكود بالطريقة التي أريدها؟

هل لدى أي شخص أي تجارب لشيء مماثل لهذا ويود مشاركته؟

هل كانت مفيدة؟

المحلول

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

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

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

نصائح أخرى

تذكر أن "اختبار الوحدة" هو نوع واحد فقط من الاختبارات.يجب أن تكون قادرًا على اختبار الوحدة داخلي أجزاء من مولد التعليمات البرمجية الخاص بك.ما تنظر إليه حقًا هنا هو اختبار مستوى النظام (ويعرف أيضًا باسم.اختبار الانحدار).ولا يقتصر الأمر على الدلالات فقط..هناك عقليات وأساليب وتوقعات مختلفة، وما إلى ذلك.إنه بالتأكيد المزيد من العمل، ولكن ربما تحتاج إلى لدغة الرصاصة وإعداد مجموعة اختبار الانحدار الشامل:ملفات C++ الثابتة -> واجهات SWIG -> وحدات بايثون -> المخرجات المعروفة.أنت تريد حقًا التحقق من المدخلات المعروفة (رمز C++ الثابت) مقابل المخرجات المتوقعة (ما يخرج من برنامج Python النهائي).سيكون التحقق من نتائج منشئ التعليمات البرمجية مباشرة مثل ملفات الكائنات المختلفة ...

نعم النتائج هي الشيء الوحيد الذي يهم.العمل الرتيب الحقيقي هو كتابة إطار عمل يسمح بتشغيل التعليمات البرمجية التي تم إنشاؤها بشكل مستقل ...اقضي وقتك هناك.

إذا كنت تعمل على *nux، فقد تفكر في التخلص من إطار عمل Unittest لصالح نص bash أو ملف makefile.على نظام التشغيل Windows، قد تفكر في إنشاء تطبيق/وظيفة Shell التي تقوم بتشغيل المولد ثم تستخدم الكود (كعملية أخرى) وتختبر ذلك.

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

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

int x = 0;
GENERATED_CODE
assert(x == 100);

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

اختبار الوحدة هو مجرد اختبار لوحدة معينة.لذا، إذا كنت تكتب مواصفات للفئة (أ)، فمن المثالي أن لا تحتوي الفئة (أ) على الإصدارات الحقيقية الحقيقية للفئة (ب) و(ج).

حسنًا، لاحظت بعد ذلك أن العلامة الخاصة بهذا السؤال تتضمن C++/Python، لكن المبادئ هي نفسها:

    public class A : InterfaceA 
    {   
      InterfaceB b;

      InterfaceC c;

      public A(InterfaceB b, InterfaceC c)   {
          this._b = b;
          this._c = c;   }

      public string SomeOperation(string input)   
      {
          return this._b.SomeOtherOperation(input) 
               + this._c.EvenAnotherOperation(input); 
      } 
    }

نظرًا لأن النظام A المذكور أعلاه يقوم بإدخال واجهات إلى النظامين B وC، فيمكنك اختبار الوحدة فقط على النظام A، دون الحاجة إلى تنفيذ وظائف حقيقية بواسطة أي نظام آخر.هذا هو اختبار الوحدة.

فيما يلي طريقة ذكية للتعامل مع النظام من الإنشاء إلى الاكتمال، مع مواصفات مختلفة لكل جزء من السلوك:

public class When_system_A_has_some_operation_called_with_valid_input : SystemASpecification
{
    private string _actualString;

    private string _expectedString;

    private string _input;

    private string _returnB;

    private string _returnC;

    [It]
    public void Should_return_the_expected_string()
    {
        _actualString.Should().Be.EqualTo(this._expectedString);
    }

    public override void GivenThat()
    {
        var randomGenerator = new RandomGenerator();
        this._input = randomGenerator.Generate<string>();
        this._returnB = randomGenerator.Generate<string>();
        this._returnC = randomGenerator.Generate<string>();

        Dep<InterfaceB>().Stub(b => b.SomeOtherOperation(_input))
                         .Return(this._returnB);
        Dep<InterfaceC>().Stub(c => c.EvenAnotherOperation(_input))
                         .Return(this._returnC);

        this._expectedString = this._returnB + this._returnC;
    }

    public override void WhenIRun()
    {
        this._actualString = Sut.SomeOperation(this._input);
    }
}

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

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

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

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

أجد أنك بحاجة إلى اختبار ما تقوم بتوليده أكثر من كيفية إنشائه.

في حالتي، يقوم البرنامج بإنشاء العديد من أنواع التعليمات البرمجية (C#، HTML، SCSS، JS، وما إلى ذلك) التي يتم تجميعها في تطبيق ويب.أفضل طريقة وجدتها لتقليل أخطاء الانحدار بشكل عام هي اختبار تطبيق الويب نفسه، وليس عن طريق اختبار المولد.

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

نظرًا لأننا نقوم بإنشائه، فإننا نقوم أيضًا بإنشاء تجريد جميل في JS يمكننا استخدامه لاختبار التطبيق برمجيًا.لقد اتبعنا بعض الأفكار الموضحة هنا: http://code.tutsplus.com/articles/maintainable-automated-ui-tests--net-35089

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

انها حلوة جدا.

حظ سعيد!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top