Search code examples
springspring-bootspring-mvcintegration-testingspring-boot-test

Writing a test to Spring boot REST API that retrieve data from a DB


I have a spring boot REST API with a GET method that returns data available in a DB. I am attempting to write an integration test to test this API method. I have configured the test to use the H2 database. I am trying to add some mock data to the database before the test is executed and see if the API retrieves that data. Following is the code I have written so far.

@RunWith(SpringRunner.class)
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(locations = "classpath:application-test.properties")
public class MetaControllerTest {

    @Autowired
    private MockMvc mvc;

    @Autowired
    private ProvinceDAO provinceDAO;

    @Transactional
    @Before
    public void addData () {
        Province southern = getProvinceEntity("Southern", "දකුණ", "தென்");
        provinceDAO.createEntity(southern);
        System.out.println(provinceDAO.findAll(Province.class).size());
    }

    @Test
    public void testGetProvinces() throws Exception {

        MvcResult result = mvc.perform(get("/meta/provinces"))
                .andExpect(status().isOk())
                .andExpect(content().contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
                .andReturn();
        System.out.println(result.getResponse().getContentAsString());


    }
}

However, when I run this code, I am getting an error saying "org.springframework.dao.InvalidDataAccessApiUsageException: No transactional EntityManager available; nested exception is java.lang.IllegalStateException: No transactional EntityManager available"

I have also attempted using @MockBean instead of @Autowired to bind the provinceDAO. Even though this prevents the error, it does not persist the entity in the database.

How should I write my testcase to test my method here?

Update:

application-test.properties

spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect

Entity -> Province.java

@Entity
@Table(name = "w4a_province")
public class Province {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private int id;

    @Column(name = "province_name")
    private String name;

    @Column(name = "province_name_si")
    private String nameSi;

    @Column(name = "province_name_ta")
    private String nameTa;
    .
    .
}

GenericDAO.java

@Repository
public class GenericDAO<T> implements IGenericDAO<T> {

    @PersistenceContext
    private EntityManager em;


    @Override
    public Session getCurrentSession() {
        return this.em.unwrap(Session.class);
    }
    @Override
    public T findByPrimaryKey(Class<T> clazz, Object primaryKey) {
        return getCurrentSession().find(clazz, primaryKey);
    }


    @Override
    public List<T> findAll(Class<T> clazz) {
        DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
        return criteria.getExecutableCriteria(getCurrentSession()).list();
    }


    @Override
    public T createEntity(T entity) {
        getCurrentSession().save(entity);
        return entity;
    }

ProvinceDAOImpl.java

@Repository
public class ProvinceDAOImpl  extends GenericDAO<Province> implements ProvinceDAO {
}

MetaController.java

@RestController
@PreAuthorize("permitAll()")
public class MetaController {

    private final MetaService metaService;


    @Autowired
    public MetaController(MetaService metService) {
        this.metaService = metService;
    }

    @GetMapping("/meta/provinces")
    public ResponseEntity<List<ProvinceDTO>> getProvinces() {
        if (logger.isDebugEnabled()) {
            logger.debug("Retrieving list of provinces.");
        }

        List<ProvinceDTO> provinces =  metaService.getProvinces();
        return ResponseEntity.ok(provinces);
    }
}

MetaServiceImpl.java

@Service
@Transactional
public class MetaServiceImpl implements MetaService {

    private final ProvinceDAO provinceDAO;
    @Autowired
    public MetaServiceImpl(ProvinceDAO provnceDAO) {
        this.provinceDAO = provnceDAO;
    }

    public List<ProvinceDTO> getProvinces() {
        if (logger.isDebugEnabled()) {
            logger.debug("Obtaining a list of provinces from database.");
        }
        List<Province> entities = provinceDAO.findAll(Province.class);
        if (logger.isDebugEnabled()) {
            logger.debug("Converting province entities to dtos.");
        }
        List<ProvinceDTO> dtos = new ArrayList<>();
        for (int i = 0; i < entities.size(); i++) {
            Province entity = entities.get(i);
            if (LocaleContextHolder.getLocale().getLanguage().equals(
                    GlobalConstants.LanguageIdentifiers.SINHALA_LANGUAGE_TAG)) {
                dtos.add(new ProvinceDTO(entity.getId(), entity.getNameSi()));
            } else if (LocaleContextHolder.getLocale().getLanguage().equals(
                    GlobalConstants.LanguageIdentifiers.TAMIL_LANGUAGE_TAG)) {
                dtos.add(new ProvinceDTO(entity.getId(), entity.getNameTa()));
            } else {
                dtos.add(new ProvinceDTO(entity.getId(), entity.getName()));
            }
        }
        return dtos;
    }
}

Solution

  • I managed to feed the database with the required data by placing a SQL script data-h2.sql with insert queries at the test/resources folder. This prevented the requirement to use an EntityManager or a DAO.

    Furthermore, I added the following property to the application-test.properties file.

    spring.datasource.platform=h2