2009-01-27 115 views
112

我剛剛開始玩Guice,我可以想到的一個用例是,在測試中我只想覆蓋單個綁定。我想我想使用剩餘的生產級綁定來確保一切安裝正確並避免重複。Guice中的覆蓋綁定

所以,想象一下,我有以下模塊

public class ProductionModule implements Module { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceA.class).to(ConcreteA.class); 
     binder.bind(InterfaceB.class).to(ConcreteB.class); 
     binder.bind(InterfaceC.class).to(ConcreteC.class); 
    } 
} 

而在我的測試中,我只想要覆蓋但InterfaceC,同時保持了InterfaceA和InterfaceB機智,所以我想要的東西,如:

Module testModule = new Module() { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceC.class).to(MockC.class); 
    } 
}; 
Guice.createInjector(new ProductionModule(), testModule); 

我也試過以下,沒有運氣:

Module testModule = new ProductionModule() { 
    public void configure(Binder binder) { 
     super.configure(binder); 
     binder.bind(InterfaceC.class).to(MockC.class); 
    } 
}; 
Guice.createInjector(testModule); 

有誰知道有可能做我想做的事,或者我完全吠叫錯誤的樹?

---跟進: 看來我可以實現我想要的,如果我在界面上使用@ImplementedBy標記,然後在測試用例中提供一個綁定,當出現1-1接口與實現之間的映射。

另外,在與一位同事討論這件事後,我們似乎會走上重寫整個模塊的道路,並確保我們的模塊已正確定義。這似乎可能會導致問題,但是綁定在模塊中放置錯誤並且需要移動,因此可能會破壞大量測試,因爲綁定可能不再可用於重寫。

+7

就像「咆哮錯誤的樹」一樣:D – 2009-01-27 14:34:48

回答

124

這可能不是您正在尋找的答案,但是如果您正在編寫單元測試,那麼您可能不應該使用注入器,而應該手動注入模擬對象或假對象。

在另一方面,如果你真的想更換一個單一的結合,你可以使用Modules.override(..)

public class ProductionModule implements Module { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceA.class).to(ConcreteA.class); 
     binder.bind(InterfaceB.class).to(ConcreteB.class); 
     binder.bind(InterfaceC.class).to(ConcreteC.class); 
    } 
} 
public class TestModule implements Module { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceC.class).to(MockC.class); 
    } 
} 
Guice.createInjector(Modules.override(new ProductionModule()).with(new TestModule())); 

查看詳情here

但作爲javadoc Modules.overrides(..)建議,你應該設計你的模塊,你不需要重寫綁定。在您給出的示例中,您可以通過將InterfaceC的綁定移動到單獨的模塊來完成此操作。

+7

感謝艾伯特,這使我得以順利完成自己想做的事。這是在生產版本,但壽!這是爲了集成測試,而不是單元測試,我爲什麼要確保正確構建其他所有東西 – tddmonkey 2009-02-10 08:58:48

+1

我已經爲代碼添加了一個具體示例。它是否讓你更進一步? – albertb 2009-02-11 02:42:13

9

爲什麼不使用繼承?您可以覆蓋overrideMe方法中的特定綁定,並在configure方法中留下共享實現。

public class DevModule implements Module { 
    public void configure(Binder binder) { 
     binder.bind(InterfaceA.class).to(TestDevImplA.class); 
     overrideMe(binder); 
    } 

    protected void overrideMe(Binder binder){ 
     binder.bind(InterfaceC.class).to(ConcreteC.class); 
    } 
}; 

public class TestModule extends DevModule { 
    @Override 
    public void overrideMe(Binder binder) { 
     binder.bind(InterfaceC.class).to(MockC.class); 
    } 
} 

最後創建噴油器是這樣的:

Guice.createInjector(new TestModule()); 
2

你想用Juckito在那裏你可以宣佈你的自定義配置爲每個測試類。

@RunWith(JukitoRunner.class) 
class LogicTest { 
    public static class Module extends JukitoModule { 

     @Override 
     protected void configureTest() { 
      bind(InterfaceC.class).to(MockC.class); 
     } 
    } 

    @Inject 
    private InterfaceC logic; 

    @Test 
    public testLogicUsingMock() { 
     logic.foo(); 
    } 
} 
3

如果你不想改變你的生產模塊,如果你有一個默認的Maven樣的項目結構像

src/test/java/... 
src/main/java/... 

您可以在測試目錄使用只需要創建一個新的類ConcreteC與原始課程相同的包裝。然後Guice將綁定InterfaceCConcreteC從您的測試目錄,而所有其他接口將綁定到您的生產類。

1

在不同的設置中,我們在單獨的模塊中定義了多個活動。被注入的活動位於Android庫模塊中,在AndroidManifest.xml文件中具有自己的RoboGuice模塊定義。

設置看起來像這樣。在庫模塊有這些定義:

的AndroidManifest.xml:

<application android:allowBackup="true"> 
    <activity android:name="com.example.SomeActivity/> 
    <meta-data 
     android:name="roboguice.modules" 
     android:value="com.example.MainModule" /> 
</application> 

那麼我們有一種被注入:

interface Foo { } 

一些默認實現的Foo:

class FooThing implements Foo { } 

MainModule配置Foo的FooThing實現:

public class MainModule extends AbstractModule { 
    @Override 
    protected void configure() { 
     bind(Foo.class).to(FooThing.class); 
    } 
} 

最後,消耗符的活動:

public class SomeActivity extends RoboActivity { 
    @Inject 
    private Foo foo; 
} 

在消費的Android應用模塊,我們想用SomeActivity但是,爲了進行測試,注入自己的Foo

public class SomeOtherActivity extends Activity { 
    @Override 
    protected void onResume() { 
     super.onResume(); 

     Intent intent = new Intent(this, SomeActivity.class); 
     startActivity(intent); 
    } 
} 

可能有人會說,露出模塊處理客戶端應用程序,但是,我們需要隱藏大多被注入的組件,因爲庫模塊是一個SDK,並揭露件具有較大的影響。 (請記住,這是爲了測試,所以我們知道SomeActivity的內部,並知道它消耗(包可見)Foo)。

我發現作品的方式很有道理;使用建議覆蓋了測試

public class SomeOtherActivity extends Activity { 
    private class OverrideModule 
      extends AbstractModule { 

     @Override 
     protected void configure() { 
      bind(Foo.class).to(OtherFooThing.class); 
     } 
    } 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 
     RoboGuice.overrideApplicationInjector(
       getApplication(), 
       RoboGuice.newDefaultRoboModule(getApplication()), 
       Modules 
         .override(new MainModule()) 
         .with(new OverrideModule())); 
    } 

    @Override 
    protected void onResume() { 
     super.onResume(); 

     Intent intent = new Intent(this, SomeActivity.class); 
     startActivity(intent); 
    } 
} 

現在,當SomeActivity啓動時,它會得到OtherFooThing爲其注入Foo實例。

這是一個非常具體的情況,在我們的例子中,OtherFooThing內部用於記錄測試情況,而FooThing默認情況下用於所有其他用途。

請記住,我們在我們的單元測試中使用#newDefaultRoboModule,它的工作完美無瑕。