首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在JUnit参数化测试中使用非静态注入服务

在JUnit参数化测试中使用非静态注入服务
EN

Stack Overflow用户
提问于 2015-06-17 15:17:24
回答 4查看 4.5K关注 0票数 4

我想使用Guice和GuiceBerry将非静态遗留服务注入到工厂类中。然后,我想将该工厂注入到参数化JUnit测试中。

但是,问题是JUnit要求@Parameters方法是静态的。

工厂示例:

代码语言:javascript
复制
@Singleton
public class Ratings {
    @Inject
    private RatingService ratingService;

    public Rating classicRating() {
         return ratingService.getRatingById(1002)
    }

    // More rating factory methods
}

示例测试使用情况:

代码语言:javascript
复制
@RunWith(Parameterized.class)
public class StaticInjectParamsTest {
    @Rule
    public GuiceBerryRule guiceBerryRule = new GuiceBerryRule(ExtendedTestMod.class)

    @Inject
    private static Ratings ratings;

    @Parameter
    public Rating rating;

    @Parameters
    public static Collection<Rating[]> ratingsParameters() {
    return Arrays.asList(new Rating[][]{
            {ratings.classicRating()}
            // All the other ratings
        });
    }

    @Test
    public void shouldWork() {
        //Use the rating in a test

    }
}

我尝试请求工厂方法的静态注入,但是Parameters方法在GuiceBerry @Rule之前被调用。我还考虑过只使用评级的Id作为参数,但我想找到一个可重用的解决方案。也许我的方法有缺陷?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2015-06-18 16:09:55

我的解决方案是添加一个包装整数的RatingId类,并创建一个工厂RatingIds,然后我可以返回静态,并将其用作参数。我在RatingService接口中重载了RatingService方法以接受新的RatingId类型,然后将评级服务注入到我的测试中并直接使用它。

新增工厂:

代码语言:javascript
复制
public class RatingIds {
    public static RatingId classic() {
        return new RatingId(1002);
    }
    // Many more
}

测试:

代码语言:javascript
复制
@RunWith(Parameterized.class)
public class StaticInjectParamsTest {
    @Rule
    public GuiceBerryRule guiceBerryRule = new GuiceBerryRule(ExtendedTestMod.class)

    @Inject
    private RatingService ratingService

    @Parameter
    public RatingId ratingId;

    @Parameters
    public static Collection<RatingId[]> ratingsParameters() {
    return Arrays.asList(new RatingId[][]{
        {RatingIds.classic()}
        // All the other ratings
        });
    }

    @Test
    public void shouldWork() {
        Rating rating = ratingService.getRatingById(ratingId.getValue())
        //Use the rating in a test

    }
}
票数 1
EN

Stack Overflow用户

发布于 2015-06-18 00:44:27

不幸的是,JUnit需要能够在运行任何测试之前枚举所有测试,因此必须在规则之前调用参数方法。

您可以为评级类型定义一个枚举:

代码语言:javascript
复制
@RunWith(Parameterized.class)
public class StaticInjectParamsTest {
  @Rule
  public GuiceBerryRule guiceBerryRule
      = new GuiceBerryRule(ExtendedTestMod.class);

  @Inject
  private Ratings ratings;

  @Parameter
  public RatingType ratingType;

  @Parameters
  public static Collection<RatingType> types() {
    return Arrays.asList(RatingType.values());
  }

  @Test
  public void shouldWork() {
    Rating rating = ratings.get(ratingType);
    // Use the rating in a test
  }
}

编辑:enum的代码:

代码语言:javascript
复制
public enum RatingType {
  CLASSIC(1002),
  COMPLEX(1020);

  private final int ratingId;

  private RatingType(int ratingId) {
    this.ratingId = ratingId;
  }

  // option 1: keep rating ID private by having a method like this
  public get(RatingService ratingService) {
    return ratingService.getRatingById(ratingId);
  }

  // option 2: have a package-scope accessor
  int getRatingId() {
    return ratingId;
  }
}

编辑:如果您使用选项2,那么您将添加一个新方法,从一个Rating获得一个RatingType,该方法将委托给传递ratingId的服务

代码语言:javascript
复制
@Singleton
public class Ratings {
    @Inject
    private RatingService ratingService;

    public Rating getRating(RatingType ratingType) {
      return ratingService.getRatingById(
          ratingType.getRatingId());
    }

    // More rating factory methods
}

如果您不希望RatingType在您的公共API中,您可以在测试中定义它,并在枚举中有一个名为getRating()的方法。

代码语言:javascript
复制
public enum RatingType {
  CLASSIC {
    @Override public Rating getRating(Ratings ratings) {
      return ratings.getClassicRating();
    }
  },
  COMPLEX {
    @Override public Rating getRating(Ratings ratings) {
      return ratings.getComplexRating();
    }
  };

  public abstract Rating getRating(Ratings ratings);
}

还可以创建值类型,而不是枚举。

这假设您可以编写所有Rating实例都应该通过的测试。

如果您有一些公共测试,但是一些特定于评级的测试,我将创建一个包含公共测试的抽象基类和一个抽象createRating()方法,并对每个评级类型进行子类化。

票数 2
EN

Stack Overflow用户

发布于 2019-04-30 04:59:44

在您的情况下,生成的参数集的总数是预先已知的,但是构建参数本身需要一些上下文(例如,使用Spring的自动处理服务实例),您可以使用函数方法(使用junit5 &参数化)

显然,如果createParameter函数本身依赖于这样的contex:-/,则这是行不通的。

代码语言:javascript
复制
class MyTestClass {

    // may be autowired, cannot be static but is required in parameter generation
    SomeInstance instance;

    private interface SomeParamBuilder { SomeParam build(SomeInstance i);}

    private static Stream<Arguments> createParamterFactories() {
         return Stream.of(
            Arguments.of((SomeParamBuilder)(i)->     
                            {
                                return new SomeParam(i);
                            })
                         );
    }

    // does not work, because SomeParam needs SomeInstance for construction
    // which is not available in static context of createParameters.
    //@ParameterizedTest(name = "[{index}] {0}")
    //@MethodSource("createParameters")
    //void myTest(SomeParam param) {
    //}


    @ParameterizedTest(name = "[{index}] {0}")
    @MethodSource("createParamterFactories")
    void myTest(SomeParamBuilder builder) {
        SomeParam param = builder.build(instance);
        // rest of your test code can use param.
    }
}

maven dep:

代码语言:javascript
复制
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.2.0</version>
            <scope>test</scope>
        </dependency>
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/30895711

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档