4

I'm trying to write a unit test for a Controller in a Spring Boot application. The application runs smoothly, my problem is with running its tests.

Here is the test code:

@RunWith(SpringRunner.class)
@WebMvcTest(MyController.class)
@AutoConfigureTestEntityManager
public class MyControllerTest {

@Autowired
private MockMvc mockMvc;

@Mock
private MyRepository myRepository;

@Mock
ZendeskNotifier zendeskNotifier;

@Mock
ActivityLogger activityLogger;

@Before
public void setUp() throws Exception {
    MockitoAnnotations.initMocks(this);
}

@Test
public void cannotSendFooWithoutMessageBody() throws Exception {
    this.mockMvc.perform(post("/api/v1/foo/1/send"))
            .andDo(print())
            .andExpect(status().is4xxClientError())
            .andExpect(content().string(containsString("The message body cannot be empty.")));
}
}

When I try running it I get:

***************************
APPLICATION FAILED TO START
***************************

Description:

Field jobEventRepository in foo.bar.util.memsource.svc.MemsourceEventProcessor required a bean named 'entityManagerFactory' that could not be found.


Action:

Consider defining a bean named 'entityManagerFactory' in your configuration.

And that feels weird to me, since I'm providing the AutoConfigureTestEntityManager annotation and would expect all the EntityManager-related stuff to be in place.

Alvaro Cavalcanti
  • 2,938
  • 4
  • 21
  • 31

6 Answers6

2

If Google brought you here and you're using Spring Boot, you may need to add @DataJpaTest to your test class. It's found in org.springframework.boot:spring-boot-test-autoconfigure. You may also discover upon rerunning that you need to declare a dependency on org.hibernate:hibernate-validator.

Idcmp
  • 659
  • 1
  • 8
  • 19
  • 3
    But why do I need to add @DataJpaTest to a controller test? I've created completely empty controller with completely empty test and it still fails with "NoSuchBeanDefinitionException: No bean named 'entityManagerFactory' available". Why? :( Edit: also, you can't have both \@WebMvcTest and \@DataJpaTest. – Dalibor Filus Nov 19 '18 at 14:42
  • @Dalibor Thanks for that comment about the empty test - it helped me figure out the answer, which is that your application's configuration (and mine) were being found and used by the unit tests. I posted an answer with a full explanation, and ways to fix. – Bampfer Oct 09 '19 at 13:13
1

Inject Spring's TestEntityManager in your mockito test class.

@Autowired
private TestEntityManager entityManager;

You are already using @AutoConfigureTestEntityManager on the test class to auto configure this test entity manager. So you don't have to do anything else in the config file.

VHS
  • 9,534
  • 3
  • 19
  • 43
1

Spring Boot is loading your application's configuration, causing your data layer to be initialized.

Excerpt from Spring Boot's docs, Detecting Test Configuration:

Spring Boot’s @*Test annotations search for your primary configuration automatically whenever you do not explicitly define one.

The search algorithm works up from the package that contains the test until it finds a class annotated with @SpringBootApplication or @SpringBootConfiguration. As long as you structured your code in a sensible way, your main configuration is usually found.

When the scanning hits your main class it is likely finding an annotation like @EnableJpaRepositories, which initializes your data layer and thus requires the entity manager factory. (You may see other side effects too, like Hibernate initializing your in-memory database if your application uses one.)

As other answers suggest, you could initialize the data layer, or try to wire/mock the missing beans. But since you aren't testing the data layer here it would be better to control the test's configuration.

The docs suggest some solutions:

  1. Move @EnableJpaRepositories from the main class to a config class in child package. It will get scanned by the application (from the top package down) but not by the unit tests. This is discussed in the section User Configuration and Slicing.

  2. Add a nested @Configuration class to override the configuration used by the unit test.

The first one seems like a good rule to follow in general. Other annotations like @EnableBatchProcessing and @EnableScheduling can be treated the same way, which will speed up your unit tests.

Bampfer
  • 2,120
  • 16
  • 25
  • Note that if you move @EnableJpaRepositories to a config class you will probably run into a different problem when you run the app, which is your repositories suddenly not autowiring. Solution here: https://stackoverflow.com/a/30209023/2436878 – Bampfer Oct 08 '19 at 18:39
0

you need to configure entityManagerFactory, you can refer bellow code

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="packagesToScan" value="org.demo.test.entity" />
<property name="dataSource" ref="dataSource" />

<property name="jpaProperties">
    <props>
        <prop key="hibernate.show_sql">true</prop>
        <prop key="hibernate.hbm2ddl.auto">create</prop>
        <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
    </props>
</property>

<property name="persistenceProvider">
    <bean class="org.hibernate.jpa.HibernatePersistenceProvider">
</bean>
</property>

</bean>
Anshul Sharma
  • 3,432
  • 1
  • 12
  • 17
0

Move @EnableJpaRepositories from the main class to a config class in a child package. It will get scanned by the application (from the top package down) but not by the unit tests.

@Configuration
@EnableJpaRepositories(
    value = "com.company.repository"
)
public class JpaConfig {
}
  • 1
    Please tend to give an explanation along with code as well as a good practice and for better understanding. – Siddhant Oct 16 '21 at 06:08
0

I found out that there is an Annotation you can use to add JPA-Support to your WebMVCTest (@AutoConfigureDataJpa)

@ContextConfiguration(classes = { SurchargesTestConfiguration.class })
@WebMvcTest(SurchargesApiController.class)
@AutoConfigureDataJpa
@ActiveProfiles("local")
class SurchargesApiControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    void testNotFound() throws Exception {
        mvc.perform(get("/surcharges/marketnumber") //
                .headers(getDefaultHeaders())) //
                .andExpect(status().isNotFound());
    }
}

Mustafa Poya
  • 2,615
  • 5
  • 22
  • 36
Starryk
  • 18
  • 3