2014-02-19 25 views
8

我有以下類:進樣嘲笑由工廠類創建的對象

public class MyClass {   
    private Apple apple; 

    public void myMethod() { 
     apple = AppleFactory.createInstance(someStringVariable); 
     .... 
     .... 
     .... 
    } 
} 

,並且測試類:

@RunWith(MockitoJUnitRunner.class) 
public class MyClassTest { 

     @InjectMocks 
     MyClass myClass; 

     @Test 
     public void myMethod(){ 
     ... 
     ... 
     ... 
     } 
    } 

我怎麼能注入一個蘋果的實例作爲MyClass的一個模擬的?

回答

16

你有3種可能性來解決這個問題:

抽象工廠:而不是使用一個靜態方法,用一個具體的工廠類:

public abstract class AppleFactory { 
    public Apple createInstance(final String str); 
} 

public class AppleFactoryImpl implements AppleFactory { 
    public Apple createInstance(final String str) { // Implementation } 
} 

在您的測試類,模擬工廠:

@RunWith(MockitoJUnitRunner.class) 
public class MyClassTest { 

    @Mock 
    private AppleFactory appleFactoryMock; 

    @Mock 
    private Apple appleMock; 

    @InjectMocks 
    MyClass myClass; 

    @Before 
    public void setup() { 
     when(appleFactoryMock.createInstance(Matchers.anyString()).thenReturn(appleMock); 
    } 

    @Test 
    public void myMethod(){ 
    ... 
    ... 
    ... 
    } 
} 

PowerMock:使用PowerMock創建STA的模擬抽動法。看看my answer to a relevant question看看它是如何完成的。

可測試類:讓Apple創建包裹在一個protected方法,並創建一個測試類中的覆蓋:

public class MyClass { 
    private Apple apple; 

    public void myMethod() { 
     apple = createApple(); 
     .... 
     .... 
     .... 
    } 

    protected Apple createApple() { 
     return AppleFactory.createInstance(someStringVariable); 
    } 
} 


@RunWith(MockitoJUnitRunner.class) 
public class MyClassTest { 

    @Mock 
    private Apple appleMock; 

    @InjectMocks 
    MyClass myClass; 

    @Test 
    public void myMethod(){ 
    ... 
    ... 
    ... 
    } 

    private class TestableMyClass extends MyClass { 
     @Override 
     public void createApple() { 
      return appleMock; 
     } 
    } 
} 

當然,在您的測試類,你應該測試TestableMyClass而不是MyClass

我會告訴你在每個方法我認爲:

  1. 抽象工廠方法是最好的一個 - 這是一個明確的設計,隱藏實現細節

  2. 可測試類 - 是需要最小變化的第二個選項

  3. PowerMock選項是我最不喜歡的 - 不要去尋求更好的設計,而是忽略並隱藏 你的問題。但這仍然是一個有效的選擇。
+0

太棒了,謝謝。我必須在我的情況下使用Mockito,所以我將使用Abstract Factory或TestableClass選項。 –

+1

@saravana_pc - 我添加了我的排名問題。儘管如此,你可以使用'mockito'進行功能模擬。您可以使用它們的等價方法(因此,在實現抽象工廠模式時,可以不使用@ @ Mock和@InjectMock'(因此您可以擺脫「@RunWith(MockitoJUnitRunner.class)」聲明) – Avi

+1

,更合適嗎? –

0

除了由阿維提出的解決方案,你可以選擇第四個可能性:

注入廠: 這是對我來說,最好的選擇,當你已經有代碼refacrot。使用此解決方案,您不必更改生產代碼,只需更改工廠類和測試。

public class AppleFactory 
{ 
    private static Apple _injectedApple; 

    public static createInstance(String str) 
    { 
     if (_injectedApple != null) 
     { 
      var currentApple = _injectedApple; 
      _injectedApple = null; 
      return currentApple; 
     } 

     //standard implementation 
    } 

    public static setInjectedApple(Apple apple) 
    { 
     _injectedApple = apple; 
    } 
} 

現在你可以使用你的靜態工廠乾脆:

@RunWith(MockitoJUnitRunner.class) 
public class MyClassTest { 

    @Mock 
    private Apple appleMock; 

    @InjectMocks 
    MyClass myClass; 

    @Before 
    public void setup() { 
     AppleFactory.setInjectedApple(appleMock); 
    } 

    @Test 
    public void myMethod(){ 
    ... 
    ... 
    ... 
    } 
} 
+0

那你怎麼測試AppleFactory呢? – Ciberman

1

關於阿維& Ev0oD的第一個答案。抽象類只能擴展和不實現。

public abstract class AppleFactory { 
    public abstract Apple createInstance(final String str); 
} 

public class AppleFactoryImpl extends AppleFactory { 
    public Apple createInstance(final String str) { // Implementation } 
}