我想使用Guice和GuiceBerry将非静态遗留服务注入工厂类。然后我想将该工厂注入我的Parameterized JUnit测试。
但是,问题是JUnit要求@Parameters
方法是静态的。
示例工厂:
@Singleton
public class Ratings {
@Inject
private RatingService ratingService;
public Rating classicRating() {
return ratingService.getRatingById(1002)
}
// More rating factory methods
}
测试用法示例:
@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
}
}
我已尝试为工厂方法请求静态注入,但在GuiceBerry Parameters
之前调用@Rule
方法。我也考虑过只使用评级的Id作为参数,但我想找到一个可重用的解决方案。也许我的方法有缺陷?
答案 0 :(得分:2)
不幸的是,JUnit需要能够在运行任何测试之前枚举所有测试,因此必须在规则之前调用参数方法。
您可以为评级类型定义枚举:
@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
}
}
编辑:枚举代码:
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
将委托给通过{{1的服务}}:
@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()
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()
方法,并为每个评级类型创建子类。
答案 1 :(得分:1)
我的解决方案是添加一个包含整数的RatingId
类并创建一个工厂RatingIds
,然后我可以返回静态并用作参数。我在getRatingById
界面中重载RatingService
方法以接受新的RatingId
类型,然后将评分服务注入我的测试并直接使用它。
添加工厂:
public class RatingIds {
public static RatingId classic() {
return new RatingId(1002);
}
// Many more
}
测试:
@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
}
}
答案 2 :(得分:0)
在像您这样的情况下,已预先知道生成的参数集的总数,但是构建参数本身需要一定的上下文(例如,使用Spring自动装配服务实例),您可以采用功能性方法(使用junit5和参数化)< / p>
如果createParameter
函数本身依赖于这样的上下文,显然这是行不通的:-/
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.
}
}
行家部:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.2.0</version>
<scope>test</scope>
</dependency>
答案 3 :(得分:-1)
我没有运行guiceberry(古代依赖项),但是使用JUnitParamters和简单的guice,这很简单:
@RunWith(JUnitParamsRunner.class)
public class GuiceJunitParamsTest {
public static class SquareService {
public int calculate(int num) {
return num * num;
}
}
@Inject
private SquareService squareService;
@Before
public void setUp() {
Guice.createInjector().injectMembers(this);
}
@Test
@Parameters({ "1,1", "2,4", "5,25" })
public void calculateSquares(int num, int result) throws Exception {
assertThat(squareService.calculate(num), is(result));
}
}
如果您查看JUnitParams网站,您会发现许多其他方法来定义参数列表。使用injecte服务很容易做到这一点。