2012-05-26 6 views
5

ich eine Unit-Test schreibe mit JUnit + Mockito zu testen, eine Methode, wie:Mocking einen Anruf auf einer öffentlichen Methode einer abstrakten Klasse ohne die abstrakte Klasse Subklassen, mit Mockito prefererably

public someObject methodUnderTest(){ 
    SomeObject obj = SomeAbstractClass.someMethod(); 

    if(obj!=null){ 
    obj.someOtherMethod(); 
    } 

    return someThing; 
} 

Und ich möchte den Anruf auf abstract Class "SomeAbstractClass" in obigem Codefragment erwähnt verspotten, so dass ich Anruf auf „obj“ wie überprüfen kann:

verify(SomeAbstractClass).someMethod(); 
verify(obj).someOtherMethod(); 

ich versucht habe, mit Mockito Features wie: Mockito.CALLS_REAL_METHODS Mockito. RETURNS_MOCKS

, aber sie funktionieren nicht aufgrund von Abhängigkeiten, die für die SomeAbstractClass nicht verfügbar sind.

Hinweis:

1) Someobject ist eine Schnittstelle.

2) Ich brauche eine Technik, um das obige Codefragment zu testen. Ich bin gezwungen, das obige Codefragment zu verwenden und kann das Codefragment nicht ändern.

Antwort

1

Annahme: Wenn Sie Komponententest schreiben, können Sie die getestete Methode noch etwas ändern.

Lösung:

  1. Extrakt statischen Methodenaufruf überschreibbare Methode:
public someObject methodUnderTest() { 
    SomeObject obj = getSomeObject(); 

    if(obj!=null){ 
     obj.someOtherMethod(); 
    } 

    return someThing; 
} 

protected SomeObject getSomeObject() { 
    return SomeAbstractClass.someMethod(); 
} 
  1. dann können Sie Mockito Spy verwenden teilweise um das Objekt zu verspotten Sie tatsächlich Test:
private ClassUnderTest classUnderTest; 

@Before 
public void setUp() { 
    classUnderTest= new ClassUnderTest(); 
    classUnderTest = Mockito.spy(classUnderTest); 
} 

@Test 
public void test() { 
    SomeObject someObject = Mockito.mock(SomeObject.class); 
    when(classUnderTest.getSomeObject()).thenReturn(someObject); 
    classUnderTest.methodUnderTest(); 
    verify(someObject).someOtherMethod(); 
} 

@Test 
public void testNull() { 
    when(classUnderTest.getSomeObject()).thenReturn(null); 
    classUnderTest.methodUnderTest(); 
    verify(something); 
} 
+0

Danke miheys für Ihre Hilfe. Ich habe genau das Gleiche gemacht. –

1

Verwenden anonyme Klassen:

public interface SomeObject { 
    public Object someOtherMethod(); 
} 

public abstract class SomeAbstractClass { 
    abstract SomeObject someMethod(); 
} 

@Test 
public void test() { 
    SomeAbstractClass target = new SomeAbstractClass() { 
     SomeObject someMethod() { 
      // some impl 
      SomeObject someObject = new SomeObject() { 
       public Object someOtherMethod() { 
        // some other impl 
       } 
      }; 
      return someObject; 
     } 
    }; 

    // now test target 
} 
+0

Danke Bohemian, aber ich habe vergessen zu erwähnen, dass SomeObject zurückgegeben ist eine Schnittstelle sowie "SomeAbstractClass.someMethod()" ist eine öffentliche und statische Methode. Daher bin ich gezwungen, die Schnittstelle "SomeObject" zu implementieren, die ich nicht machen möchte, sowohl "SomeAbstractClass" als auch "SomeObject" sind Teil einer 3rd-Party-Bibliothek, die ich in meiner Webanwendung verwende. –

+0

Sie können auch eine anonyme Klasse für eine Schnittstelle (wie SomeObject) verwenden - siehe bearbeitete Antwort – Bohemian

2

können Sie PowerMock verwenden statische und letzte Methoden zu verspotten.

2

Es klingt wie das Problem ist, dass die Nutzung von CALLS_REAL_METHODS auf die gesamte Klasse bewirbt, wo man wirklich spezifische Methoden verspotten wollen aus (d mache einen "partiellen Mock"). Sie haben zwei Möglichkeiten, eine thenCallRealMethod verwenden, und ein mit CALLS_REAL_METHODS und dann die Anrufe speziell spöttischen Sie brauchen:

public void testMethodUnderTest_mockSpecificThings() { 
    SomeAbstractClass myAbstractClass = Mockito.mock(SomeAbstractClass.class); 
    SomeAbstractClass myObject = Mockito.mock(SomeObject.class); 
    when(myAbstractClass.someMethod()).thenReturn(foo); 
    when(myAbstractClass.methodUnderTest()).thenCallRealMethod(); 

    myAbstractClass.methodUnderTest(); 

    verify(myAbstractClass).someMethod(); 
    verify(myObject).someOtherMethod(); 
} 

public void testMethodUnderTest_makeSpecificRealCalls() { 
    SomeAbstractClass myAbstractClass = 
     Mockito.mock(SomeAbstractClass.class, CALLS_REAL_METHODS); 
    SomeAbstractClass myObject = Mockito.mock(SomeObject.class); 
    // overrides the default answer 
    when(myAbstractClass.someMethod()).thenReturn(myObject); 

    myAbstractClass.methodUnderTest(); 

    verify(myAbstractClass).someMethod(); 
    verify(myObject).someOtherMethod(); 
} 

seien Sie gewarnt, dass SomeAbstractClass nie tatsächlich instanziiert, also wenn Sie verlassen sich auf jedes Verhalten in der abstrakten Klasse Konstruktor, wie Variableninitialisierung - einschließlich Inline-Initialisierung, wo die Felder deklariert werden - Sie müssen diese Aufrufe explizit selbst vornehmen.

Verwandte Themen