为测试目的注入通用化存根

时间:2014-04-28 16:35:33

标签: java unit-testing generics dependency-injection hk2

我目前已经安排了以下类/接口。类型T表示从DataProvider实现返回的数据的格式。我正在使用工厂,所以我不需要将类型信息附加到MyStreamingOutput。我正在使用HK2DataProviderFactory注入MyStreamingOutput

public interface DataProvider<T> {
    public T next() { ... }
    ...
}

public final class SQLDataProvider<T> {
    public SQLDataProvider(final String query, final RowMapper<T> rowMapper) { ... }
}

public interface DataProviderFactory {
    public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper);
    ...
}

public final class SQLDataProviderFactory {
    public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
        return new SQLDataProvider<T>(query, rowMapper);
    }
}

public final class MyStreamingOutput implements StreamingOutput {
    public MyStreamingOutput(final DataProviderFactory dpFactory) { ... }
    @Override public void write(final OutputStream outputStream) throws IOException { ... }
}

一切正常。现在我正在尝试为MyStreamingOutput设置单元测试,但我遇到了几个障碍。为了测试目的,我编写了以下附加类:

public final class DataProviderFactoryStub implements DataProviderFactory {
    private final DataProvider dataProvider;

    public DataProviderFactoryStub() {
        this.dataProvider = new DataProviderStub();
    }

    public DataProviderFactoryStub(final DataProvider dataProvider) {
        this.dataProvider = dataProvider;
    }

    @Override
    public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
        return this.dataProvider;
    }
}

绑定发生在

final class QueryTestResourceConfig extends ResourceConfig {

    public QueryTestResourceConfig() {
        ...

        this.register(new AbstractBinder() {
            @Override
            protected void configure() {
                bind(DataProviderFactoryStub.class).to(DataProviderFactory.class);
            }
        });
    }

}

我可以成功将此类注入MyStreamingOutput,但它有一个编译器警告,因为getDataProvider()使用的输入信息不会被传递到工厂的实例共享。我无法向DataProviderFactoryStub类添加类型信息,因为它不再实现DataProviderFactory接口。我不希望界面上有类型信息,因为它是错误的 - 在Stub案例之外,工厂不应该关心DataProvider实例返回的类型。我非常希望避免对queryrowMapper参数使用setter,因为我认为在这种情况下设计不好。

我无法摆脱这种感觉,即我在遗传学的应用中缺少一些微妙的东西,或者在我的依赖注入应用中显而易见的东西。解决此用例的正确方法是什么?看来这是DI要解决的问题,但我看不出如何解决它。

2 个答案:

答案 0 :(得分:1)

当使用DI时,我们通常最终得到非常基本的工厂类(即,它们的创建方法通常足够简单以适合单行)。你的SQLDataProviderFactory课就是一个很好的例子。

之所以这样,是因为工厂对象只是用于创建对象的占位符。我们希望避免使用new关键字乱丢我们的代码,因为这样做会将代码紧密地耦合到特定类型。因此,我们最终得到的工厂的方法基本上只是美化new关键字。

我提出这一点,指出产品的类型在这里很重要;工厂只是一个管道。当您使用测试双重替换工厂时,您真正在做的是用测试双重替换产品。这意味着每当我定义一个测试双工厂时,我总是要定义一个测试双产品。

例如,您的存根工厂只是尝试返回存根产品。问题是它返回的存根产品的类型与调用代码所期望的类型不匹配。如果您定义自己的存根产品,则代码将落实到位:

public final class DataProviderStub<T> implements DataProvider<T> {
    private final T dummy;
    public DataProviderStub() { }
    public T next() { return this.dummy; } // Just for example
}

public final class DataProviderFactoryStub implements DataProviderFactory {
    public DataProviderFactoryStub() { }

    @Override
    public <T> DataProvider<T> getDataProvider(final String query, final RowMapper<T> rowMapper) {
        return new DataProviderStub<T>();
    }
}

存根工厂只存在,因此您可以将存根DataProvider注入您的SUT。

答案 1 :(得分:1)

不幸的是,由于类型擦除,我不可能做我想要的。我将不得不考虑重构现有代码。

相关问题