Spring boot JUnit tests pass individually but not when run together - spring-boot

I'm trying to create tests for a courseRepository and a queueRepository, and all the tests pass when run individually, but not when the whole file is ran.
This is the courseRepo:
#DataJpaTest
class CourseRepositoryTest {
#Autowired
private CourseRepository courseRepository;
#Autowired
private TestEntityManager entityManager;
#BeforeEach
void setUp() {
Course course =
Course.builder()
.id(1L)
.name("Fullstack applikasjonsutvikling")
.code("IDATT2105")
.semester("V22")
.queueActive(false)
.totalWork(6)
.rules("6_1_6")
.nrOfStudents(100)
.build();
entityManager.persist(entityManager.merge(course));
}
#Test
void findByCodeAndSemester() {
Course course = courseRepository.findByCodeAndSemester("IDATT2105", "V22").get();
assertEquals("Fullstack applikasjonsutvikling", course.getName());
}
#Test
void findById() {
Course course = courseRepository.findById(1L).get();
assertEquals("Fullstack applikasjonsutvikling", course.getName());
}
#Test
void deleteById(){
courseRepository.deleteById(1L);
assertTrue(courseRepository.findById(1L).isEmpty());
}
}
This is the QueueRepo:
#DataJpaTest
class QueueRepositoryTest {
#Autowired
private QueueRepository queueRepository;
#Autowired
private TestEntityManager entityManager;
#BeforeEach
void setUp() {
System.out.println("before each called");
Course course =
Course.builder()
.id(1L)
.name("Fullstack applikasjonsutvikling")
.code("IDATT2105")
.semester("V22")
.queueActive(false)
.totalWork(6)
.rules("6_1_6")
.nrOfStudents(100)
.build();
Queue queue = Queue.builder()
.id(1L)
.course(course)
.build();
entityManager.persist(entityManager.merge(queue));
}
#AfterEach
void teardown(){
queueRepository.deleteAll();
}
#Test
void getByCourseId() {
Queue queue = queueRepository.getByCourseId(1L).get();
assertEquals(1L, queue.getId());
}
#Test
void getByCourseId_falseId(){
assertTrue(queueRepository.getByCourseId(2L).isEmpty());
}
}
I've tried importing different test annotations to no success and I don't think its scope related, but I may be wrong.

There is no #AfterEach on CourseRepositoryTest.
They also could be #Before and #After, as you can reuse the same course.
When running against a real DB, you would get duplicate key conflicts.

Related

Not able to save data in H2 Database using SpringBootTest and Spring JPA Repository

I am using #SpringBootTest to test SpringSecurity basic authentication.When I test it,the h2 database does not save the data.I do not see the insert statement in the console,which I apparently see when I am running my actual SpringBoot Application and inserting the data from frontend. Please help.
Below is my test:
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
#ContextConfiguration(classes=ConnectionsAppApplication.class)
#Transactional
public class AuthenticationTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Mock
CustDetailsRepository custRepository;
#Mock
BCryptPasswordEncoder encrypt;
#InjectMocks
CustomerServiceImpl customerServiceImpl;
#BeforeEach
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void testAuthentication() {
CustomerDetails customer = new CustomerDetails();
customer.setEmailid("abc.com");
customer.setPassword("abc#123456");
customerServiceImpl.saveUser(customer);
try {
this.mockMvc.perform(get("/api/login")
.with(httpBasic("abc.com","abc#123456")))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
saveUser method in CustomerServiceImpl class:
public void saveUser(CustomerDetails customerDetails) {
customerDetails.setPassword(bCryptPasswordEncoder.encode(customerDetails.getPassword()));
custDetailsRepository.save(customerDetails);
}
You have 2 options to implement this test:
Option 1: use real h2
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
#ContextConfiguration(classes=ConnectionsAppApplication.class)
#Transactional
public class AuthenticationTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#Autowired
CustomerServiceImpl customerServiceImpl;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void testAuthentication() {
CustomerDetails customer = new CustomerDetails();
customer.setEmailid("abc.com");
customer.setPassword("abc#123456");
customerServiceImpl.saveUser(customer);
try {
this.mockMvc.perform(get("/api/login")
.with(httpBasic("abc.com","abc#123456")))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Option 2: Mock your service / repository
#SpringBootTest(webEnvironment=WebEnvironment.RANDOM_PORT)
#ContextConfiguration(classes=ConnectionsAppApplication.class)
#Transactional
public class AuthenticationTest {
#Autowired
private WebApplicationContext context;
private MockMvc mockMvc;
#MockBean
CustomerServiceImpl customerServiceImpl;
#BeforeEach
public void setup() {
mockMvc = MockMvcBuilders
.webAppContextSetup(context)
.apply(springSecurity())
.build();
}
#Test
void testAuthentication() {
// set expectations on CustomerServiceImpl
CustomerDetails customer = new CustomerDetails();
customer.setEmailid("abc.com");
customer.setPassword("abc#123456");
// mock the method you use to fetch the customer
when(customerServiceImpl.getUser("abc.com").thenReturn(customer);
try {
this.mockMvc.perform(get("/api/login")
.with(httpBasic("abc.com","abc#123456")))
.andDo(print())
.andExpect(status().isOk())
.andReturn();
} catch (Exception e) {
e.printStackTrace();
}
}
}
Note that you also can use #WebMvcTest to test only the web slice of your app (meaning no other beans will be instantiated, for example all sercies you depend in the controller must be delivered by #MockBean)

Spring test transaction thread

I have moved a synchronous process to asynchronous, and now I have some troubles to maintain integration tests. It seems related to the fact that when you create a new thread inside a #Transactional method, then call a new #Transactional, Spring create a new transaction.
During integration tests the problem occurs with #Transactional tests. It seems that the thread transaction is rollbacked before the test finishes because of TransactionalTestExecutionListener in test configuration.
I'have tried many things like
- autowiring EntityManager and manually flushing after thread was finished
- using #Rollback instead of #Transactional in test methods
- managing transactions with TestTransaction
- using #Rollback and TestTransaction together
Here is the simplified source code :
public interface MyService{
public void doThing(someArgs...);
public void updateThings(someArgs...);
}
#Service
public class MyServiceImpl implements MyService{
#Autowired
private AsynchronousFutureHandlerService futureService;
#Autowired
#Qualifier("myExecutorService")
private ScheduledExecutorService myExecutorService;
#Transactional
#Override
public void doThing(someArgs...){
doThingAsync(someArgs...);
}
private void doThingAsync(someArgs...){
AsynchronousHandler runnable = applicationContext.getBean(
AsynchronousHandler.class, someArgs...);
//as we are executing some treatment in a new Thread, a new transaction is automatically created
Future<?> future = myExecutorService.submit(runnable);
//keep track of thread execution
futureService.addFutures(future);
}
#Override
#Transactional
public void updateThings(someArgs...){
//do udpate stuff
}
}
/**
* very basic solution to improve later to consult thread state
*/
#Service
public class AsynchronousFutureHandlerService {
//TODO : pass to ThreadSafe collection
private List<Future<?>> futures = new ArrayList<>();
public void addTheoreticalApplicationFuture(Future<?> future){
futures.add(future);
this.deleteJobsDone();
}
public boolean isThreadStillRunning(){
boolean stillRunning = false;
for(Future<?> f : futures){
if(!f.isDone()){
stillRunning = true;
break;
}
}
return stillRunning;
}
public void deleteJobsDone(){
this.futures.removeIf(f -> f.isDone());
}
}
#Component
#Scope("prototype")
public class AsynchronousHandler implements Runnable {
#Autowired
private MyService myService;
#Override
public void run() {
myService.updateThings(...); //updates data in DB
...
}
}
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = TestConfiguration.class)
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class, DataSetTestExecutionListener.class,
TransactionalTestExecutionListener.class })
#DataSet(dbType = DBType.H2, locations = { "classpath:dataset.xml" })
public class MyServiceTest{
#Autowired
private MyService myService;
#Autowired
private AsynchronousFutureHandlerService futureService;
#Test
#Transactional
public void test_doThings(){
myService.doThings(someArgs...);
waitUpdateFinish();
Assert.assertEquals(...); //fails here because Thread transaction has been rollbacked
}
private void waitUpdateFinish() throws InterruptedException{
while(futureService.isThreadStillRunning()){
Thread.sleep(500);
}
}
}

Method when() from Mockito do not work correctly

I have problem with mockito method: when(...). When I test:
afterThrowExceptionShouldReturnCorrectHttpStatus()
Ran first, then the second test:
controllerShouldReturnListOfAnns()
It always fails because it throw NotFoundException. When I delete the first test or running second test as first, then everything is correct. This look like method when() from first test override method when() form the second test There is test code and test configuration.
#ActiveProfiles("dev")
#RunWith(SpringRunner.class)
#SpringBootTest
public class AnnTestController {
#Autowired
private AnnounService annSrv;
#Autowired
private AnnounRepo annRepo;
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void contextLoads() {
this.mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
#Test
public void afterThrowExceptionShouldReturnCorrectHttpStatus() throws Exception {
when(this.annRepo.getAnnounList()).thenThrow(NotFoundAnnounException.class);
this.mockMvc.perform(get("/ann/list")).andExpect(status().isNotFound());
}
#Test
public void controllerShouldReturnListOfAnns() throws Exception {
List<Announcement> lst = new ArrayList<>();
lst.add(new Announcement(1, "test", "test"));
when(annRepo.getAnnounList()).thenReturn(lst);
this.mockMvc.perform(get("/ann/list"))
.andExpect(status().isOk())
.andExpect(jsonPath("$[0].id", is(1)));
}}
Config:
#Profile("dev")
#Configuration
public class BeanConfig {
#Bean
public CommentsRepo commentsRepo() {
return mock(CommentsRepo.class);
}}
You can try somthing like that:
#After public void reset_mocks() {
Mockito.reset(this.annRepo);
}

Not able to mock jdbcTemplate in Spring boot Test class

I am using Spring boot and Mockito for testing. I have been able to write test cases for Service layer and they re working fine. But, the test cases for DAO layer do not. The jdbcTemplate object that is mocked and autowired gives null pointer when executing the test case. Below are the details:
My DAOTest class:
#RunWith(SpringRunner.class)
#SpringBootTest(classes = EcommerceApplication.class)
public classEcommerceDaoTest {
#InjectMocks
private IEcommerceDao ecommerceDao = new EcommerceDaoImpl();
#Mock
#Autowired
private JdbcTemplate as400JdbcTemplate;
#Before
public void setUp() throws Exception
{
MockitoAnnotations.initMocks(this);
}
#Test
public void checkOrderExistsTest() throws EcommerceException{
Mockito.when(as400JdbcTemplate.queryForObject(queryForOrder,new Object[]
{"1000"}, int.class)).thenReturn(1);
boolean exists =
ecommerceDao.checkOrderExists("1000");
assertTrue(exists);
}
}
EcommerceDAOImpl.java:
#Override
public boolean checkOrderExists(String orderNo)throws EcommerceException{
boolean doesExist = false;
int count = 0;
try{
count= as400JdbcTemplate.queryForObject(queryForOrder, new Object[]{orderNo}, int.class);
if(count >0){
doesExist = true;
}
}
catch(Exception e){
}
return doesExist;
}
AS400Config.java:
#Bean
#Autowired
public JdbcTemplate as400JdbcTemplate(#Qualifier("as400DataSource")DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
ECommerceApplication.java
#SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,
HibernateJpaAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class })
#EnableTransactionManagement
#Import(As400Configuration.class)
public class EcommerceApplication {
public static void main(String[] args) {
SpringApplication.run(EcommerceApplication.class, args);
}
}
When I am running the test case, I am getting NullPointerException for as400JdbcTemplate. The functionality works fine as is. Its just the test cases for DAO layer that fail because of the inability of the jdbcTemplate to get mocked/autowired.
Please let me know where I am going wrong.
You don't need to use #Mock and #Autowired at the same time. Use only #Mock:
#Mock
private JdbcTemplate as400JdbcTemplate;
Use instead of #RunWith(SpringRunner.class) --> #RunWith(MockitoJUnitRunner.class)
Also to inject mock into DAO you can use ReflectionTestUtils from spring test.
public static void setField(Class targetClass, String name, Object value)
#Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ReflectionTestUtils.setField(ecommerceDao ,"as400JdbcTemplate" ,
as400JdbcTemplate);
}
#Mock
private JdbcTemplate as400JdbcTemplate;

Static field injection in Spring Unit test

I am using JUnit4 in my application where I tried to test a UserService, the current test case is simple:
Create a user named 'admin' and save it to the database.
The other test case will rely on this user.
So I use the BeforeClass to insert the record, however I have to use the UserService to save the user, but spring does not support to inject static fields.
When I tried to create the UserService manually, I found that I have to fill the dependencies myself, I wonder if there is an alternative? Or there is something wrong with how I use JUnit?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:spring/application-config.xml"})
public class UserServiceTest {
#Rule
public final ExpectedException exception = ExpectedException.none();
#Autowired
private UserService userService;
//#Autowired // sprint does not suport this
private static UserService us;
private static User u;
#BeforeClass
public static void before() {
us = new UserServiceImpl(); // this UserService instance can not be used, since I have to fill the dependencies manually
us.clear();
u = new User();
u.setUsername("admin");
u.setPassword("pass");
us.save(u);
}
#AfterClass
public static void after() {
userService.delete(u.getId());
}
#Test
public void testQuery() {
List<User> list = userService.find();
assertEquals(1, list.size());
}
#Test
public void testChangePassword() {
userService.changePassword(u.getId(), u.getPassword(), "newpass");
assertEquals("newpass", userService.findOne(u.getId()).getPassword());
exception.expect(ResourceNotFoundException.class);
userService.changePassword("1", "32", "ss");
exception.expect(ResourceNotFoundException.class);
userService.changePassword(u.getId(), "32", "ss");
}
}

Resources