Я только начал играть с 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 в интерфейсе, а затем просто предоставлю привязку в тестовом примере, который отлично работает, когда между интерфейс и реализация.
Кроме того, после обсуждения этого с коллегой кажется, что мы пойдем по пути переопределения всего модуля и обеспечения правильного определения наших модулей. Похоже, что это может вызвать проблему, если привязка неуместна в модуле и ее нужно переместить, что может нарушить нагрузку на тесты, поскольку привязки больше не могут быть доступны для переопределения.
источник
Ответы:
Возможно, это не тот ответ, который вы ищете, но если вы пишете модульные тесты, вам, вероятно, не следует использовать инжектор, а лучше вручную вводить фиктивные или поддельные объекты.
С другой стороны, если вы действительно хотите заменить одну привязку, вы можете использовать
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()));
Подробности смотрите здесь .
Но, как
Modules.overrides(..)
рекомендует javadoc for , вы должны проектировать свои модули таким образом, чтобы вам не приходилось переопределять привязки. В приведенном вами примере это можно сделать, переместив привязкуInterfaceC
в отдельный модуль.источник
ovveride
теряет должноеStage
(т.е. систематически используется РАЗРАБОТКА).Почему бы не использовать наследование? Вы можете переопределить свои конкретные привязки в
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());
источник
@Override
Не похоже на работу. Особенно если это делается по методу чего-@Provides
то.Если вы не хотите изменять свой производственный модуль и если у вас есть структура проекта по умолчанию, подобная maven, например
Вы можете просто создать новый класс
ConcreteC
в своем тестовом каталоге, используя тот же пакет, что и для исходного класса. Затем GuiceInterfaceC
выполнит привязкуConcreteC
из вашего тестового каталога, тогда как все остальные интерфейсы будут привязаны к вашим производственным классам.источник
Вы хотите использовать 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(); } }
источник
В другой настройке у нас есть несколько действий, определенных в отдельных модулях. Внедренная активность находится в модуле библиотеки Android с собственным определением модуля RoboGuice в файле AndroidManifest.xml.
Настройка выглядит так. В модуле библиотеки есть следующие определения:
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 настраивает реализацию FooThing для Foo:
public class MainModule extends AbstractModule { @Override protected void configure() { bind(Foo.class).to(FooThing.class); } }
И, наконец, Activity, потребляющее Foo:
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
наших модульных тестов, и она работает безупречно.источник