How to properly inject Entity Manager for an integration test in Spring Boot 3?

117 Views Asked by At

My Problem

I'm currently trying to write an integration test that will call my service layer and eventually perform operations in my test MySQL database. However, I can't correctly inject the Entity Manager in my test class.

I'm currently receiving this exception: Unsatisfied dependency expressed through field 'entityManager': No qualifying bean of type 'jakarta.persistence.EntityManager' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}

This is my first side project with Spring, so if you see anything irregular or bad practice feel free to point it out with suggestions to improve.

Basic Overview Of Code

My application has a pretty standard setup with a service layer that makes calls to my DAO interface which extends the JPARepository (to get basic CRUD) and a custom DAO interface containing some custom queries. Lastly, I have Entity classes modeling a MySQL database. Most of these features have been tested using a CommandLineRunner and I know the overall flow works, but I wanted to set up unit and integration testing to clean everything up.

As for testing, I have an "application-test.properties" file which specifies the datasource and how to connect to the test database. I just have a single test class, which should call service methods and assert that the correct changes in the database are reflected. You can see my entire test class below.

DAO Layer

public interface FoodDAOCustom {... my custom query functions ...}

@Repository
public class FoodDAOCustomImpl implements FoodDAOCustom {

    private EntityManager entityManager;

    @Autowired
    public FoodDAOCustomImpl(EntityManager entityManager) {
        this.entityManager = entityManager;
    }

    ... implementing custom query functions ...
}

public interface FoodDAO extends JpaRepository<Food, Integer>, FoodDAOCustom {}

Service Layer

public interface UserMealFoodService {...}

@Service
public class UserMealFoodServiceImpl implements UserMealFoodService {

    private FoodDAO foodDAO;
    private MealDAO mealDAO;
    private UserDAO userDAO;


    @Autowired
    public UserMealFoodServiceImpl(FoodDAO foodDAO, MealDAO mealDAO, UserDAO userDAO) {
        this.foodDAO = foodDAO;
        this.mealDAO = mealDAO;
        this.userDAO = userDAO;
    }
  
    ......
}

Test Code

application-test.properties

spring.datasource.url=jdbc:mysql://localhost:3306/database-test
spring.datasource.username=test
spring.datasource.password=test
spring.jpa.hibernate.ddl-auto=create-drop

Integration Test

@ExtendWith(SpringExtension.class)
@ActiveProfiles("test")
@SpringBootTest(classes = MyApplication.class)
public class UserMealFoodIntegrationTest {

    @Autowired
    private TestEntityManager entityManager;
    @Autowired
    private MealDAO mealDAO;
    @Autowired
    private FoodDAO foodDAO;
    @Autowired
    private UserDAO userDAO;

    @Autowired
    private UserMealFoodService umfService;

    @BeforeAll
    public static void setup() {
        Food f1 = new Food("Scrambled Eggs", "test", 155, "egg(s)", 2);
        Food f2 = new Food("Potatoes", "test", 95, "g", 100);
        Meal m1 = new Meal("breakfast_1");
        m1.add(Arrays.asList(f1, f2));
        Meal m2 = new Meal("breakfast_2");
        m2.add(Arrays.asList(f1, f2));
        User u1 = new User("u1", "u1", "John", "[email protected]");
        u1.add(Arrays.asList(m1, m2));
        User u2 = new User("u2", "u2", "Jane", "[email protected]");
        u2.add(Arrays.asList(m1, m2));
    }

    @Test
    public void testFindAllUsers() {
        List<User> userList = umfService.findAllUsers();
        assertEquals(2, userList.size());
        assertEquals("u1", userList.get(0).getUsername());
        assertEquals("u2", userList.get(1).getUsername());
    }

What I've tried:

So far I've tried constructor injection, @DataJpaTest, and "TestEntityManager". None of these worked, but it's more likely that I was just using them wrong. I think a lot of my confusion comes from looking at older versions of Spring Boot and JPA/Hibernate. Lots of solutions were suggesting using @PersistanceContext, but in my learning so far I've not seen any modern examples.

Constructor Injection

Here is my constructor injection for reference. This is how I normally inject EntityManager into my DAO classes.

private EntityManager entityManager;

@Autowired
public MyIntegrationTest(EntityManager entityManager) { this.entityManager = entityManager; }

0

There are 0 best solutions below