Guice에서 바인딩 재정의


138

방금 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와 InterfaceB를 그대로 유지하면서 InterfaceC 만 재정의하고 싶습니다. 그래서 다음과 같은 것을 원합니다.

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 태그를 사용하고 테스트 케이스에 바인딩을 제공하면 원하는 것을 얻을 수있는 것 같습니다. 인터페이스와 구현.

또한이를 동료와 논의한 후 전체 모듈을 재정의하고 모듈을 올바르게 정의했는지 확인해야합니다. 바인딩이 모듈에 잘못 배치되어 이동 해야하는 경우 문제가 발생할 수 있으므로 바인딩을 더 이상 재정의 할 수 없으므로 테스트로드가 중단 될 수 있습니다.


7
문구처럼 "잘못 짚었": D를
보리스 파블로

답변:


149

이것은 당신이 찾고있는 대답이 아닐 수도 있지만 단위 테스트를 작성하는 경우 인젝터를 사용하지 말고 손으로 가짜 또는 가짜 물건을 주입해야합니다.

반면에 단일 바인딩을 실제로 바꾸려면 다음을 사용할 수 있습니다 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()));

자세한 내용은 여기를 참조 하십시오 .

그러나 javadoc에서 Modules.overrides(..)권장하는대로 바인딩을 대체 할 필요가없는 방식으로 모듈을 설계해야합니다. 제시 한 예에서 바인딩을 InterfaceC별도의 모듈 로 이동하여이를 수행 할 수 있습니다.


9
고마워 앨버트, 그게 내가 원하는 일을하는 길을 어쨌든 가져옵니다. 그것은 아직 프로덕션 릴리스에 있습니다! 이것은 유닛 테스트가 아닌 통합 테스트를위한 것입니다. 다른 모든 것이 올바르게 구축되고 있는지 확인하고 싶습니다.
tddmonkey

1
코드에 구체적인 예를 추가했습니다. 더 당신을 따라가나요?
albertb

1
내가 착각하지 않는 한, 그렇게하는 동안 ovveride적절한 것을 푼다 Stage(즉, 개발은 체계적으로 사용된다).
pdeschen

4
크기가 중요합니다. 의존성 그래프가 커지면 손으로 배선하는 것이 상당히 어려울 수 있습니다. 또한 배선 변경시 모든 수동 배선 장소를 수동으로 업데이트해야합니다. 재정의하면 자동으로 처리 할 수 ​​있습니다.
yoosiba

3
@pdeschen 즉 그 3 Guice 버그 I 고정 Guice 4. 위해
Tavian 반스

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());

3
@Override작동하지 않습니다. 특히 그것이 방법에 관한 일이라면 @Provides.
Sasanka Panguluri

4

생산 모듈을 변경하지 않으려는 경우와 같은 기본 Maven과 같은 프로젝트 구조가있는 경우

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

ConcreteC원래 클래스와 동일한 패키지를 사용하여 테스트 디렉토리에 새 클래스 를 작성할 수 있습니다 . 그러면 Guice는 테스트 디렉토리에서 바인드 InterfaceC되며 ConcreteC다른 모든 인터페이스는 프로덕션 클래스에 바인드됩니다.


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();
    }
}

1

다른 설정에서는 별도의 모듈에 둘 이상의 활동이 정의되어 있습니다. 주입되는 활동은 AndroidManifest.xml 파일에 자체 RoboGuice 모듈 정의가있는 Android 라이브러리 모듈에 있습니다.

설정은 다음과 같습니다. 라이브러리 모듈에는 다음과 같은 정의가 있습니다.

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);
    }
}

마지막으로 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우리의 단위 테스트에서, 그것은 완벽하게 작동합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.