I have a JUnit test where I am using Spring Test DBUnit and Spring declarative transaction management. If I tag a test method with #Transactional (which will utilize the #BeforeTransactional to verify database state) and also with #DatabaseSetup (to set the database state to what I want), which will have precedence? Will the database first be set up and then #BeforeTransactional will check it, or will the check happen after setup? Here's some code:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = PersonServiceConfiguration.class)
#TestExecutionListeners({ DependencyInjectionTestExecutionListener.class,
DirtiesContextTestExecutionListener.class,
TransactionalTestExecutionListener.class })
public class PersonServiceTransactionalIT {
#Autowired
private PersonManagementService pms;
#BeforeTransaction
public void beforeTransaction() {
checkDatabaseState();
}
#AfterTransaction
public void afterTransaction() {
checkDatabaseState();
}
#Test
#Transactional
#DatabaseSetup("/test_data_start.xml")
public void testCreateValid() {
Person expected = new Person(6l, "six", "created");
Person actual = pms.save(expected);
assertEquals("Person objects not equal", expected, actual);
}
private void checkDatabaseState() {
List<Person> pl = pms.findAll();
assertEquals("Size of database not as expected", 5, pl.size());
}
}
Related
I cannot write data to db in #beforeEach as lifecycle methods are not transactional. How can I force data to commit? Data is stored in a transaction, but it is executed after the tearDown() method. By the way, I use MariaDB test container.
#SpringBootTest
#AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
#ContextConfiguration(initializers = TestConfigurations.Initializer.class,
classes = {Application.class, TestConfigurations.class})
#Transactional(transactionManager = "transactionManager")
public class SomeTest {
#Autowired
private SomeRepository someRepository;
#Nested
class SomeNestedClass {
#BeforeEach
void setUp() {
someRepository.saveAll(Fixtures.getSomeEntities());
}
#AfterEach
public void tearDown() {
someRepository.deleteAll();
}
...
Your test methods annotated with #Transactional will be rollback by default by Spring Test, so you can just initialize your data at the beginning of your test.
try using #BeforeTransaction
/ #AfterTransaction
I create a Spring 2.3 application using Spring Data REST, Hibernate, Mysql.
I created my tests, I've around 450 tests splitted in about 70 files. Because the persistence layer leans on a multi tenant approach (single db per tenant) using a Hikari connection pool, I've the need to avoid the pool is initializated for each test file but at the same time I need to use #MockBean because I need to mock up some repositories in the entire Spring test contest.
I create a custom annotation for all test in my suite:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#SpringBootTest
#TestExecutionListeners(value = TestExecutionListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
#Transactional
#ActiveProfiles("test")
public #interface TestConfig {
}
Reading many posts and the doc, I know if I use #MockBean inside a test, the Spring context is reloaded and therefore a new pool connection is created in my case.
My idea is to create a #MockBean and share it with all tests in my suite so the context is not reloaded every time.
I tried several approaches:
#Log4j2
public class TestExecutionListener extends AbstractTestExecutionListener implements Ordered {
#Override
public void beforeTestMethod(TestContext testContext) throws Exception {
try {
TestDbUtils testDbUtils = (TestDbUtils) testContext.getApplicationContext().getBean(TestDbUtils.class);
testDbUtils.truncateDB();
TenantRepository tenantRepository = mock(TenantRepository.class);
testContext.setAttribute("tenantRepository", tenantRepository);
TenantContext.setCurrentTenantId("test");
when(tenantRepository.findByTenantId("test")).thenReturn(testDbUtils.fakeTenant());
} catch (Exception e) {
}
}
#Override
public int getOrder() {
return Integer.MAX_VALUE;
}
}
All my tests are annotated like this:
#TestConfig
#Log4j2
public class InvoiceTests {
#Test
public void test1(){
}
}
Unfortunately my tenantRepository.findByTenantId() is not mocked up. I also tried to create an abstract superclass:
#SpringBootTest
#TestPropertySource(locations = "classpath:application-test.properties")
#TestExecutionListeners(value = TestExecutionListener.class, mergeMode = TestExecutionListeners.MergeMode.MERGE_WITH_DEFAULTS)
#Transactional
#ActiveProfiles("test")
public abstract class AbstractIntegrationTest {
#MockBean
protected TenantRepository tenantRepository;
#MockBean
protected SubscriptionRepository subscriptionRepository;
#Autowired
protected TestDbUtils testDbUtils;
#BeforeAll
public void beforeAll() {
when(tenantRepository.findByTenantId("test")).thenReturn(testDbUtils.fakeTenant());
}
#BeforeEach
public void setup() {
testDbUtils.truncateDB();
TenantContext.setCurrentTenantId("test");
}
}
Even if my tests extended this superclass, during the run all of them were skipped (not sure why).
Is there any way to accomplish what I described?
For tests I use:
Spring Test 3.2.3.RELEASE
JUnit 4.12
Mockito 1.10.19
The following test code should save some entity into the database, however, this does not occur:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {ControllerTestConfig.class})
public class ControllerTest {
#Autowired
SomeMapper someMapper;
#Test
public void shouldCreateSomeEntity() {
SomeEntity someEntity = new SomeEntity();
someEntity.setSomeProperty("property");
...
someMapper.createSomeEntity(someEntity);
}
...
}
I use a simulated implementation of the mapper:
#Configuration
public class ControllerTestConfig {
#Bean
public SomeMapper SomeMapper() {
return Mockito.mock(SomeMapper.class);
}
...
}
Because the implementation is simulated, the method call is intercepted in the class org.mockito.internal.creation.cglib.MethodInterceptorFilter.
The mapper is an interface:
public interface SomeMapper {
#Insert("Insert into some_table (id, some_entity_id, type, full_name) values (#{id}, #{someEntityId}, #{type}, #{fullName})")
#SelectKey(statement="select nextval('seqid_static_data');", keyProperty="id", before=true, resultType=long.class)
void createSomeEntity(SomeEntity someEntity);
...
}
Thus, it is not possible to create an instance of this mapper. For example, by this way:
#Bean
public SomeMapper SomeMapper() {
return new SomeMapper();
}
...
How to use MyBatis mappers in Spring's JUnit tests?
Did you try to emulate method call by doAnswer or doThrow, they should works with void methods. For example:
#Test
public void shouldCreateSomeEntity() {
SomeEntity someEntity = new SomeEntity();
someEntity.setSomeProperty("property");
Mockito.doAnswer(invocation -> {
invocation.getArgument(0).setSomeProperty("changed_property")
}).when(someMapper).createSomeEntity(Mockito.eq(someEntity));
someMapper.createSomeEntity(someEntity);
Assert.assertEquals("changed_property", someEntity.getSomeProperty());
}
I have a Spring 3.1 MVC + Hibernate 3.6 project with its junit4 test suit. My problem is that there is no transaction starting in my test cases, even thought I added a #Transactional.
My test case calls a controller and a dao. In the controller, a transaction is started anyway, so it does not complain. In the dao, I added a #Transactional(propagation = Propagation.MANDATORY) to be sure it will take the test's transaction. And currently it raises an IllegalTransactionStateException, which I guess it means there is no current transaction.
I tried to create programmaticaly an transaction and it does work, which means the AOP proxy to get the dao service is not the cause of the problem. However I want to create a transaction with the #Transactional annotation.
here's my dao:
// ...imports...
#Repository("taskDao")
#Transactional(propagation = Propagation.MANDATORY)
public class TaskHome implements TaskDao {
private static final Log log = LogFactory.getLog(TaskHome.class);
private SessionFactory sessionFactory;
#Autowired
public TaskHome(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Task findById(int id) {
log.debug("getting Task instance with id: " + id);
try {
Task instance = (Task) this.sessionFactory.getCurrentSession().get(
Task.class, id); // exception raised here!
if (instance == null) {
log.debug("get successful, no instance found");
} else {
log.debug("get successful, instance found");
}
return instance;
} catch (RuntimeException re) {
log.error("get failed", re);
throw re;
}
}
...
}
Here's my test case:
// ...imports...
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class TestTaskController {
private static ClassPathXmlApplicationContext context;
private static TaskDao taskDao;
#BeforeClass
public static void initHibernate() throws Exception {
context = new ClassPathXmlApplicationContext("applicationContext.xml");
taskDao = context.getBean("taskDao", TaskDao.class);
}
#Test
public void testOnSubmit() {
// expects an existing default transaction here
Task task = taskDao.findById(1); // fails already here
// ... calls the controller and does some tests.
}
}
I searched in all Spring's documentation and googled it in any way I could imagine, but I don't see why the transaction is not started.
Any help is very welcome.
When using #RunWith(SpringJUnit4ClassRunner.class) you should obtain beans from the application context created by SpringJUnit4ClassRunner rather than from your own one.
In your case things go wrong because #Transactional on the unit test creates a transaction in the application context managed by SpringJUnit4ClassRunner, but you call methods on the beans obtained from the application context created manually.
So, remove your #BeforeClass method and obtain TaskDao via autowiring:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration({ "/test-config.xml", "/applicationContext.xml" })
#TransactionConfiguration(defaultRollback = true)
#Transactional
public class TestTaskController {
#Autowired
private TaskDao taskDao;
...
}
See also:
9.3.5 Spring TestContext Framework
I am using JUNIT4 + Spring and wrote a test case. I wired in a JDBC Template and did manual set on it. But that turns out be null and the test is throwing null pointer exception when i use that injected variable. What's wrong here?
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:applicationContextTest.xml" })
#TransactionConfiguration(defaultRollback = true)
#Configurable
public class WriterTest {
private JdbcTemplate utilityJdbcTemplate;
public void setUtilityJdbcTemplate(JdbcTemplate utilityJdbcTemplate) {
this.utilityJdbcTemplate = utilityJdbcTemplate;
}
#Test
#Transactional
#Rollback(true)
public void testHappyPath() {
Assert.assertNotNull(utilityJdbcTemplate);
}
}
Here the test fails because utilityJdbcTemplate being null. why?
"gotta autowire":
#Autowired
private JdbcTemplate utilityJdbcTemplate;