JUnit Rule using a spring bean
Asked Answered
P

2

14

I have a test class which loads a test spring application context, now I want to create a junit rule which will setup some test data in mongo db. For this I created a rule class.

public class MongoRule<T> extends ExternalResource {

    private MongoOperations mongoOperations;
    private final String collectionName;
    private final String file;

    public MongoRule(MongoOperations mongoOperations, String file, String collectionName) {
        this.mongoOperations = mongoOperations;
        this.file = file;
        this.collectionName = collectionName;
    }

    @Override
    protected void before() throws Throwable {
        String entitiesStr = FileUtils.getFileAsString(file);
        List<T> entities = new ObjectMapper().readValue(entitiesStr, new TypeReference<List<T>>() {
        });
        entities.forEach((t) -> {            
            mongoOperations.save(t, collectionName);
        });
    }
}

Now I am using this rule inside my test class and passing the mongoOperations bean.

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringTestConfiguration.class)
public class TransactionResourceTest {

    @Autowired
    private ITransactionResource transactionResource;

    @Autowired
    private MongoOperations mongoOperations;

    @Rule
    public MongoRule<PaymentInstrument> paymentInstrumentMongoRule 
        = new MongoRule(mongoOperations, "paymentInstrument.js", "paymentInstrument");    
....
}

The problem is that Rule is getting executed before application context gets loaded, so mongoOperations reference is passed as null. Is there a way to make rules run after the context is loaded?

Pageantry answered 28/7, 2016 at 4:34 Comment(0)
S
2

Here's a solution, using some abstract super class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringTestConfiguration.class)
public abstract class AbstractTransactionResourceTest<T> {

    @Autowired
    private ITransactionResource transactionResource;

    @Autowired
    private MongoOperations mongoOperations;

    @Before
    public void setUpDb() {
        String entitiesStr = FileUtils.getFileAsString(entityName() + ".js");
        List<T> entities = new ObjectMapper().readValue(entitiesStr, new TypeReference<List<T>>() {});
        entities.forEach((t) -> {            
            mongoOperations.save(t, entityName());
        }); 
    }    

    protected abstract String entityName();
}

then

public class TransactionResourceTest extends AbstractTransactionResourceTest<PaymentInstrument> {
    @Override
    protected String entityName() {
        return "paymentInstrument";
    };

    // ...
}
Siphonophore answered 28/7, 2016 at 5:16 Comment(0)
B
5

As far as I know what you are trying to achieve is not possible in such straight forward way because:

  1. the rule is instantiated prior Spring's Application Context.
  2. SpringJUnit4ClassRunner will not attempt to inject anything on the rule's instance.

There is an alternative described here: https://blog.jayway.com/2014/12/07/junit-rule-spring-caches/ but I think it would fall short in terms of what can be loaded into mongodb.

In order to achieve what you want to achieve, you would probably require a test execution listener that would inject whatever dependencies you require on your rule object.

Blastocoel answered 22/2, 2017 at 17:58 Comment(0)
S
2

Here's a solution, using some abstract super class:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringTestConfiguration.class)
public abstract class AbstractTransactionResourceTest<T> {

    @Autowired
    private ITransactionResource transactionResource;

    @Autowired
    private MongoOperations mongoOperations;

    @Before
    public void setUpDb() {
        String entitiesStr = FileUtils.getFileAsString(entityName() + ".js");
        List<T> entities = new ObjectMapper().readValue(entitiesStr, new TypeReference<List<T>>() {});
        entities.forEach((t) -> {            
            mongoOperations.save(t, entityName());
        }); 
    }    

    protected abstract String entityName();
}

then

public class TransactionResourceTest extends AbstractTransactionResourceTest<PaymentInstrument> {
    @Override
    protected String entityName() {
        return "paymentInstrument";
    };

    // ...
}
Siphonophore answered 28/7, 2016 at 5:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.