سؤال

أنا أعمل حاليًا على فصل يستخدم فئة أخرى لها وظائف ثابتة فقط.

كل شيء يعمل بشكل جيد حتى حاولت اختبار صفي.

فيما يلي مثال رمز بسيط للمشكلة:

class A {
    static String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B {
    B() {}
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return A::getSomething();
        } else {
            return "Invalid name!";
        }
    }
}

على افتراض أن الفئة A تعمل بشكل صحيح (وقد تم اختبارها بواسطة اختبارات الوحدة) ، أود التحقق من شيء وظيفة في الفئة ب

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

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

سؤالي لك هو: هل هناك بطرق أفضل لاختبار فئات C ++ التي تعتمد على الفئات/الوظائف الثابتة من خياراتي الحالية؟

شكرا مقدما،

تال.

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

المحلول

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

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

نصائح أخرى

إذا كنت لا تستخدم مجموعة اختبار متجانسة ، فهذا سهل. أفترض أن لديك الفئة A في A.CPP والفئة B في B.CPP ، واختبارات B في B_Test.cpp.

إنشاء ملف يسمى a_mock.cpp

class A
{
    static String getSometing() {
        return String("Expected Something");
    }
};

ثم عند تجميع ملف B_Test ، ما عليك سوى الارتباط باستخدام a_mock.o بدلاً من AO.

g++ -Wall B_test.cpp B.cpp A_mock.cpp

يمكنك تمرير مؤشر إلى الوظيفة إلى مُنشئ الفئة A. ثم للاختبار ، يمكنك تمرير بعض المؤشر إلى وظيفة وهمية حيث يمكنك فعل ما تريد.

لماذا الوظيفة الثابتة؟ أود أن أقترح عدم جعلها ثابتة.

يمكنك بعد ذلك إنشاء واجهة للفئة A (في C ++ ، وهذا يعني فئة ذات رؤوس وظائف افتراضية نقية فقط) المسمى AINTERFACE. من شأن الفئة A تنفيذ (مورث) واجهة وتنفيذ هذه الوظائف الافتراضية.

ثم قم بتمرير مؤشر إلى هذه الواجهة إلى مُنشئ الفئة B وقم بتخزينه في متغير عضو يسمى M_A. ثم في الاختبار الخاص بك ، قم بإنشاء mockclassa التي تنفذ واجهة A. تمرير mockclassa إلى مُنشئ الفئة B وقم بتعيين M_A على الإدخال.

class AInterface
{
   virtual String getSomething() = 0;
}

class A : public AInterface
{
    String getSometing() {
        return String("Something: ") + heavyCalculation().asString();
    }
}

class B 
{
    B(AInterface A) :  { m_A = A; }
    ~B() {}
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return m_A.getSomething();
        } else {
            return "Invalid name!";
        }
    }
    AInterface m_A;
}

رمز الاختبار

class MockClassA : public AInterface
{
    String getSometing() {
        return String("Whatever I want. This is a test");
    }
}   

void test ()
{
   // "MockClassA" would just be "A" in regular code
   auto instanceOfB = B(MockClassA());

   String returnValue = instanceOfB.runSomething("something");
   :
   :
}

أود أن أقول ، "رجل ، بعض الناس يأخذون وحدة اختبار الوحدة بعيدا جدا!"

ما عليك سوى اختبار الفئتين كوحدة واحدة. يتم ترميز الفئة A إلى الفئة B على أي حال.

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

template<typename T> class BImpl {
    String runSomething(const String& name) {
        if(name.equals("something")) {
            return T::getSomething();
        } else {
            return "Invalid name!";
        }
    }
};
typedef BImpl<A> B; // Just plugs in to existing code.

يمكنك الآن استبدال فصل وهمية لـ A ، على الرغم من أنك لا تستطيع أن ترث منها. Infact ، هذا يمكن تمديده أيضًا بطريقة أخرى - CRTP.

class A : public BImpl<A> {
    String getSomething() {
        // Now it's non-static! IT'S A MIRACLE!
    }
}

عجائب القوالب لا تتوقف أبدًا عن دهشتي.

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