Using non-static injected services in JUnit Parameterized tests

I want to use Guice and GuiceBerry to inject a non-static legacy service into a factory class. Then I want to add that factory to my JUnit test parameter.

However, the JUnit issue requires the method to @Parameters

be static.

Factory example:

@Singleton
public class Ratings {
    @Inject
    private RatingService ratingService;

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

    // More rating factory methods
}

      

An example of using the test:

@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

    }
}

      

I've tried requesting static injection for the factory method, but the method Parameters

is called before GuiceBerry @Rule

. I've also considered using just the rating id as parameters, but I want to find a reusable solution. Maybe my approach is flawed?

+3


source to share


4 answers


My solution was to add a class RatingId

that wraps an integer and creates a factory RatingIds

, after which I can return static and use as parameters. I have overloaded the method getRatingById

in my interface RatingService

to accept the new type RatingId

and then inject the grading service into my test and use it directly.

Added factory:

public class RatingIds {
    public static RatingId classic() {
        return new RatingId(1002);
    }
    // Many more
}

      



Test:

@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


source


Unfortunately JUnit must be able to list all tests before running any tests, so the parameters method must be called before rules.

You can define an enum for the rating type:

@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
  }
}

      

Edit: Code to enumerate:

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

      

Edit: if you go with option 2, you add a new method to get Rating

from RatingType

that delegates the transfer of services ratingId

:



@Singleton
public class Ratings {
    @Inject
    private RatingService ratingService;

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

    // More rating factory methods
}

      

If you don't want to RatingType

be in your public API, you can define it in your test and have a method in an enum namedgetRating()

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

      

You can also create a value type instead of an enumeration.

It is assumed that you can write tests that must pass for all instances Rating

.

If you have generic tests, but some tests to score , I would make an abstract base class containing generic tests and an abstract createRating()

method and subclass for each rating type.

+2


source


In cases like yours where the total number of generated parameter sets is known in advance, but some context is required to build the parameters themselves (for example, an autoconnect service instance with Spring), you can use a functional approach (with junit5 & parameterized)

Obviously this doesn't work if the createParameter

function createParameter

depends on a context like this: - /

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:

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-params</artifactId>
            <version>5.2.0</version>
            <scope>test</scope>
        </dependency>

      

0


source


I didn't manage to use guicberry (ancient dependencies), but using JUnitParamters and a simple example, it's pretty straightforward:

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

      

If you check the JUnitParams website, you will find many other ways to define a parameter list. It's really easy to do this with an injection service.

-1


source







All Articles