58
@RunWith(SpringJUnit4ClassRunner.class)
public void ITest {
    @Autowired
    private EntityRepository dao;

    @BeforeClass
    public static void init() {
        dao.save(initialEntity); //not possible as field is not static
    }
}

How can I have my service injected already in the static init class?

membersound
  • 81,582
  • 193
  • 585
  • 1,120
  • 3
    This blog post may help you - http://saltnlight5.blogspot.in/2012/09/enhancing-spring-test-framework-with.html – Mithun Mar 30 '15 at 08:02

6 Answers6

52

With Junit 5 you can do this (@BeforeAll instead of @BeforeClass)

public void ITest {
    @Autowired
    private EntityRepository dao;

    @BeforeAll
    public static void init(@Autowired EntityRepository dao) {
        dao.save(initialEntity); //possible now as autowired function parameter is used
    }
}

By leaving the field it means it can be used in other tests

Vladtn
  • 2,506
  • 3
  • 27
  • 23
32

One workaround that I have been using to get this working is to use @Before with a flag to skip it being executed for each testcase

@RunWith(SpringJUnit4ClassRunner.class)
public class BaseTest {

@Autowired
private Service1 service1;

@Autowired
private Service2 service2;

private static boolean dataLoaded = false;

@Before
public void setUp() throws Exception {

    if (!dataLoaded) {
        service1.something();
        service2.somethingElse();
        dataLoaded = true;
    }
  }
}
Narain Mittal
  • 1,150
  • 11
  • 25
  • 3
    I keep doing this, but there really has to be a better way, especially, when you're relying on an injected/autowired field, because in that case `@BeforeClass` will not work. – carlspring Feb 19 '17 at 18:59
  • What about the @AfterClass part after the execution of your tests. Any strategy to keep it simple as this first part? – emecas Jan 11 '18 at 15:00
  • @emecas do the same thing as the post mention, but use `@After` instead of `@Before`, and each time it's called, you update a test counter, when the test counter goes to your last test, execute your statement. – Alex Apr 03 '18 at 15:16
  • The biggest problem with that approach is to do the same workaround with @After annotation. In order to do that it is mandatory to know the number of tests dynamically. – Daniel Ferreira Castro Nov 05 '19 at 17:04
13

UPD for Spring 2.x versions.

Spring 2.x supports new feature a SpringExtension for Junit 5 Jupiter, where all you have to do is:

  1. Declare your test class with @ExtendWith(SpringExtension.class)

  2. Inject your @BeforeAll (replacement for @BeforeClass in JUnit 5) with the bean

For example:

@ExtendWith(SpringExtension.class)
...
public void ITest {

    @BeforeAll
    public static void init(@Autowired EntityRepository dao) {
        dao.save(initialEntity);
    }

}

Assuming you correctly configured JUnit 5 Jupiter with Spring 2.x

More about it here: https://docs.spring.io/spring/docs/current/spring-framework-reference/testing.html#testcontext-junit-jupiter-extension

yeralin
  • 1,357
  • 13
  • 24
9

It looks to me that you are trying to populate DB before tests.

I would give a try to two options:

  • If you can extract initial scripts to sql file (if that is option for you without using repository bean) you can use this approach and annotate your test with @Sql
  • You can explore DbUnit and here is link to spring dbunit connector which is doing exactly that and helping you populate DB before tests. Here is a github link for integrating between spring test framework and dbunit. After you do that you have @DatabaseSetup and @DatabaseTearDown which will do thing on DB you need

I know that this does not answer how to inject bean in static @BeforeClass but form code it looks it is solving your problem.

Update: I recently run into same problem in my project and dug out this article which helped me and I think it is elegant way of dealing with this type of problem. You can extend SpringJUnit4ClassRunner with listener which can do instance level setup with all your defined beans.

Nenad Bozic
  • 3,724
  • 19
  • 45
  • The last link (in the last paragraph) is broken. What happens to be sad, as it seems to have interesting information, according to your explanation :( – ElPiter May 21 '20 at 16:28
  • @ElPiter, it was moved to https://dzone.com/articles/enhancing-spring-test – RyanD Jul 22 '20 at 21:35
3

To answer this question we should recap Spring 2.x versions.

If you want to "autowire" a bean in your @BeforeTest class you can use the ApplicationContext interface. Let's see an example:

@BeforeClass
    public static void init() {
        ApplicationContext context = new ClassPathXmlApplicationContext("application-context.xml");
        EntityRepository dao2 = (EntityRepository) context.getBean("dao");
        List<EntityRepository> all = dao2.getAll();
        Assert.assertNotNull(all);
    }

What's happening: using the ClassPathXmlApplicationContext we are instantiating all beans contained in the application-context.xml file.

With context.getBean() we read the bean specified (it must match the name of the bean!); and then you can use it for your initialization.

You should give to the bean another name (that's the dao2!) otherwise Spring normal "autowired" cannot work on the predefined bean.

As a side note, if your test extends AbstractTransactionalJUnit4SpringContextTests you can do some initialization using executeSqlScript(sqlResourcePath, continueOnError); method, so you don't depend on a class/method that you also have to test separately.

musikele
  • 355
  • 2
  • 15
  • I cannot instantiate the `ApplicationContext` myself as I also use `@IntegrationTest` to autowire my normal `@EnableAutoConfiguration` classes during test (annotation based config). – membersound Mar 30 '15 at 08:38
  • Why don't you use the `executeSqlScript` then ? It is a static method that you get for free when you extend the `AbstractTransactionalJUnit4SpringContextTests` class. – musikele Mar 30 '15 at 08:41
  • The question is of course just an example. I could as well have to execute any logic in @BeforeClass, not only database initialization... – membersound Mar 30 '15 at 08:46
  • I think that in static methods you can only call static variables. So the best thing to do is create a Service with static methods that perform your logic. For example, you can create a Service that autowires stuff, and static getters that return autowired classes. – musikele Mar 30 '15 at 09:06
0

If you just want to use some DB data in your tests, you could also mock the repository and use the @Before workaround Narain Mittal describes:

@RunWith(SpringJUnit4ClassRunner.class)
public void ITest {
    @MockBean
    private EntityRepository dao;

    @Before
    public static void init() {
        when(dao.save(any())).thenReturn(initialEntity);
    }
}
Jacob van Lingen
  • 8,989
  • 7
  • 48
  • 78