سؤال

أود اختبار فئة مجردة. بالتأكيد، أستطيع كتابة وهمية يدويا هذا يرث من الفصل.

هل يمكنني القيام بذلك باستخدام إطار السخرية (أنا أستخدم Mockito) بدلا من صياغة يدي؟ كيف؟

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

المحلول

دعا الاقتراح التالي، يمكنك اختبار فصول مجردة دون إنشاء فرعية "حقيقية" - وهمية يكون الفئة الفرعية.

استعمال Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS), ، ثم يسخر من أي طرق مجردة يتم الاحتجاج بها.

مثال:

public abstract class My {
  public Result methodUnderTest() { ... }
  protected abstract void methodIDontCareAbout();
}

public class MyTest {
    @Test
    public void shouldFailOnNullIdentifiers() {
        My my = Mockito.mock(My.class, Mockito.CALLS_REAL_METHODS);
        Assert.assertSomething(my.methodUnderTest());
    }
}

ملاحظة: جمال هذا الحل هو أنك لا تفعل ذلك يملك لتنفيذ الطرق التجريدية، طالما لم يتم الاحتجاج بها أبدا.

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

نصائح أخرى

إذا كنت بحاجة فقط إلى اختبار بعض الأساليب الخرسانية دون لمس أي من الملخصات، يمكنك استخدامها CALLS_REAL_METHODS (يرى إجابة مورتن)، ولكن إذا كانت الطريقة الخرسانية قيد الاختبار تدعو بعض الملخصات، أو أساليب الواجهة غير المستقرة، فلن تعمل هذا - سوف يشكو Mockito "لا يمكن الاتصال بالطريقة الحقيقية على واجهة Java."

(نعم، إنه تصميم رديء، ولكن بعض الأطر، مثل نسيج 4، نوع من القوة عليك.)

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

public abstract class MyClass {
    @SomeDependencyInjectionOrSomething
    public abstract MyDependency getDependency();

    public void myMethod() {
        MyDependency dep = getDependency();
        dep.doSomething();
    }
}

public class MyClassTest {
    @Test
    public void myMethodDoesSomethingWithDependency() {
        MyDependency theDependency = mock(MyDependency.class);

        MyClass myInstance = mock(MyClass.class);

        // can't do this with CALLS_REAL_METHODS
        when(myInstance.getDependency()).thenReturn(theDependency);

        doCallRealMethod().when(myInstance).myMethod();
        myInstance.myMethod();

        verify(theDependency, times(1)).doSomething();
    }
}

تحديث لإضافة:

لطرق غير الفراغ، ستحتاج إلى استخدامها thenCallRealMethod() بدلا من ذلك، على سبيل المثال:

when(myInstance.myNonVoidMethod(someArgument)).thenCallRealMethod();

خلاف ذلك سوف يشكو mockito "المكتشفة غير مكتملة."

يمكنك تحقيق ذلك باستخدام Spy (استخدم أحدث إصدار من Mockito 1.8+ على الرغم من).

public abstract class MyAbstract {
  public String concrete() {
    return abstractMethod();
  }
  public abstract String abstractMethod();
}

public class MyAbstractImpl extends MyAbstract {
  public String abstractMethod() {
    return null;
  }
}

// your test code below

MyAbstractImpl abstractImpl = spy(new MyAbstractImpl());
doReturn("Blah").when(abstractImpl).abstractMethod();
assertTrue("Blah".equals(abstractImpl.concrete()));

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

عند اختبار فئة مجردة، تريد تنفيذ الأساليب غير المجردة للموضوع قيد الاختبار (Sut)، لذلك إطار السخرية ليس ما تريد.

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

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

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

حاول استخدام إجابة مخصصة.

علي سبيل المثال:

import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;

public class CustomAnswer implements Answer<Object> {

    public Object answer(InvocationOnMock invocation) throws Throwable {

        Answer<Object> answer = null;

        if (isAbstract(invocation.getMethod().getModifiers())) {

            answer = Mockito.RETURNS_DEFAULTS;

        } else {

            answer = Mockito.CALLS_REAL_METHODS;
        }

        return answer.answer(invocation);
    }
}

سيعود السخرية إلى الطرق المجردة وسوف تسمي الطريقة الحقيقية بطرق ملموسة.

ما الذي يجعلني حقا أشعر بأنني أشعر بالسخرية في الفصول الدراسية المجردة هي الحقيقة، والتي لا يتم استدعاء المنشئ افتراضي yourampstractclass () مع وجود قائمة صفيف فارغة أو قائمة مرتبطة).

لا توفر فئتي المجردة (أساسا رمز مصدر الفصل) حقن عبء التبعية لعناصر القائمة، ولا منشئا حيث تهيئة عناصر القائمة (التي حاولت إضافتها يدويا).

فقط السمات الفئة تستخدم التهيئة الافتراضية: قائمة خاصة DEP1 = قائمة ArrayList الجديدة؛ قائمة خاصة Dep2 = قائمة صفيف جديدة

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

سيء للغاية أن Powermock فقط سيساعد هنا كذلك.

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

YourClass yourObject = mock(YourClass.class);

واتصل بالطرق التي تريد اختبارها تماما كما تفعل أي طريقة أخرى.

تحتاج إلى توفير توقعات لكل طريقة تم استدعاؤها بالتوقعات على أي طرق ملموسة تدعو إلى الطريقة الفائقة - لست متأكدا من كيفية القيام بذلك مع Mockito، لكنني أعتقد أنه من الممكن مع EasyMock.

كل هذا يفعل هو خلق مثيل ملموس YouClass وإنقاذك بذل جهد توفير تطبيقات فارغة لكل طريقة مجردة.

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

يمكنك تمديد فئة مجردة مع فئة مجهولة في الاختبار. على سبيل المثال (باستخدام Junit 4):

private AbstractClassName classToTest;

@Before
public void preTestSetup()
{
    classToTest = new AbstractClassName() { };
}

// Test the AbstractClassName methods.

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

@RunWith(MockitoJUnitRunner.class)
public class ClassUnderTest_Test {

    private ClassUnderTest classUnderTest;

    @Mock
    MyDependencyService myDependencyService;

    @Before
    public void setUp() throws Exception {
        this.classUnderTest = getInstance();
    }

    private ClassUnderTest getInstance() {
        return new ClassUnderTest() {

            private ClassUnderTest init(
                    MyDependencyService myDependencyService
            ) {
                this.myDependencyService = myDependencyService;
                return this;
            }

            @Override
            protected void myMethodToTest() {
                return super.myMethodToTest();
            }
        }.init(myDependencyService);
    }
}

ضع في اعتبارك أن الرؤية يجب أن تكون protected للممتلكات myDependencyService من فئة مجردة ClassUnderTest.

يمكن أن يكون Whitebox.invokemethod (..) مفيدا في هذه الحالة.

يسمح Mockito بالسخرية فصول مجردة عن طريق @Mock حاشية. ملاحظة:

public abstract class My {

    public abstract boolean myAbstractMethod();

    public void myNonAbstractMethod() {
        // ...
    }
}

@RunWith(MockitoJUnitRunner.class)
public class MyTest {

    @Mock(answer = Answers.CALLS_REAL_METHODS)
    private My my;

    @Test
    private void shouldPass() {
        BDDMockito.given(my.myAbstractMethod()).willReturn(true);
        my.myNonAbstractMethod();
        // ...
    }
}

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

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