I am new to JUnit and trying to test a spring web service which uses JPA DAOs. I need to test a service method similar to below.
Service method is annotated with #Transactional(propagation=Propagation.REQUIRED) and ServiceObjectRepository.update() method calls a native sql query to update the db.
#Transactional(propagation=Propagation.REQUIRED)
public void serviceMethod(){
//Read DB for ServiceObject to update
//Call ServiceObjectRepository.update() method to update DB
}
ServiceObjectRepository
public interface ServiceObjectRepository extends JpaRepository<ServiceObject, Integer> {
#Query(value ="UPDATE serviceobjcet AS c SET c.objectstatus= :os WHERE c.objid = :oi", nativeQuery = true)
public Integer update(#Param("os")short objStatus,#Param("oi")int objId);
}
TestClass
#TransactionConfiguration(defaultRollback=true)
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(
locations = "classpath:service-test.xml")
#Transactional
public class ServiceHandlerTest {
#Test
public void testServiceMethod() {
//Create ServiceObject and save to DB
//Call serviceMethod()
//Read DB for updatedServiceObject
assertEquals("Test: Object should be in updated state", new Short(3), updatedServiceObject.getObjectstatus(), 0);
}
}
My test runs and rollback the db transactions. But the problem is when I read the updatedServiceObject after calling the serviceMethod it does not return the updated object. So my test fails with a NullPointerException at the assertEquals. Any idea to overcome this issue?
Finally I came up with a solution, Rather than creating ServiceObject and saving to DB in the test method, I have done it before the test method and deletion of the created objects done after the transaction. So that my test class looks like,
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath:service-test.xml")
public class ServiceHandlerTest {
#Before
public void setup() {
//Create ServiceObject and save to DB
}
#Test
public void testServiceMethod() {
//Call serviceMethod()
//Read DB for updatedServiceObject
assertEquals("Test: Object should be in updated state", new Short(3), updatedServiceObject.getObjectstatus(), 0);
}
#After
public void teardown() {
//Delete created ServiceObject from DB
}
}
And I found that test method or test class does not necessary to be transactional in such a case.
Related
When the JUnit Test bellow executes an INSERT or DELETE, the table gets locked and the service being tested cannot read the table. It's also not possible to read any row in the table outside Eclipse, from SSMS, for example.
One thing to know is, all test classes extend from a BaseTest class, from which I autowire the repositories, which are the same classes used in the service being tested. Not sure it is a problem.
class AlterarComiteTest extends BaseTest {
[...]
}
#ActiveProfiles("des")
#Slf4j
#SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT)
public class BaseTest {
[...]
#Autowired
protected ComiteRepository comiteRep;
[...]
}
This is how the controller method being tested looks like:
[...]
#PutMapping("/{sqComite}")
#Transactional(value = TxType.REQUIRES_NEW, rollbackOn = Throwable.class)
public ResponseEntity<ComiteDto> atualizarComite(HttpServletRequest request, #PathVariable Long sqComite, #RequestBody #Valid ComiteForm form) throws ApplicationException {
}
[...]
Try #1
Result: After cadastrarComite(true), which executes an INSERT on the same entity of the controller, the table gets locked. When the service method tries to SELECT the entity, it finds the lock and remain there, waiting.
Note: excluirComite(comiteCenario) DELETEs the entity.
#Test
#Transactional
void alterar_DadosInvalidos_SqPeriodicidadeReuniaoNaoExiste() {
[...]
Comite comiteCenario = cadastrarComite(true);
[...]
[calls the service method being tested]
[...]
excluirComite(comiteCenario);
}
Try #2
Result: Same as Try #1, but the lock starts in the #BeforeEach
class AlterarComiteTest extends BaseTest {
private Comite comiteAtivo;
#BeforeEach
void setUpBeforeEach() {
[...]
if (comiteAtivo == null) {
comiteAtivo = cadastrarComite(true);
}
[...]
}
#Test
#Transactional
void alterar_DadosInvalidos_SqPeriodicidadeReuniaoNaoExiste() {
[...]
Comite comiteCenario = comiteAtivo;
[...]
[calls the service method being tested]
[...]
excluirComite(comiteCenario);
}
}
Try #3
Result: If all tests in the test class are executed, then they all execute successfully, but the #AfterAll fails, since it (for some reason) does not have any transaction related to it. It causes created entities to not be DELETED, dirtying the database.
Caused by: javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call
BUT, if only any single #Test from the test class is executed, then it works all the way (maybe the test transaction is used in the #AfterAll, don't know why).
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
class AlterarComiteTest extends BaseTest {
private Comite comiteAtivo;
#BeforeAll
void setUpBeforeClass() {
comiteAtivo = cadastrarComite(true);
}
#AfterAll
void tearDownAfterClass() {
excluirComite(comiteAtivo);
}
#Test
#Transactional
void alterar_DadosInvalidos_SqPeriodicidadeReuniaoNaoExiste() {
[...]
Comite comiteCenario = comiteAtivo;
[...]
[calls the service method being tested]
}
}
Try #4
Result: Removed #Transactional from all test methods. All tests got the same error.
Caused by: javax.persistence.TransactionRequiredException: No EntityManager with actual transaction available for current thread - cannot reliably process 'remove' call
#BeforeEach
void setUpBeforeEach() {
comiteAtivo = cadastrarComite(true);
comiteInativo = cadastrarComite(false);
}
#AfterEach
void tearDownAfterEach() {
excluirComite(comiteAtivo);
excluirComite(comiteInativo);
}
#Test
void alterar_DadosInvalidos_SqPeriodicidadeReuniaoNaoExiste() {
[...]
Comite comiteCenario = comiteAtivo;
[...]
[calls the service method being tested]
}
Last note: I tried inserting #Transactional on cadastrarComite and excluirComite as well. Currently they do not have it. It did not made any difference.
I have a sql file which looks like this:
--Testdata
INSERT into cms_patch.configuration_main(document_key, config)
values('TEST/PhoenixTest', '{"b" : "c"}');
I am trying to run a test and want to seed this data to the table before that test, but the data is getting rolled back before the test happens.
Test class:
#Sql({"/sql/cms_dao_data.sql"})
public class CmsDaoImplTest extends AbstractPhoenixDao {
#Autowired
private CmsDao cmsDao;
#BeforeMethod(alwaysRun = true)
public void beforeMethod() {
this.cmsDao = new CmsDaoImpl();
}
#Test
#Sql("/sql/cms_dao_data.sql")
public void containsDocumentKey() {
Boolean result = cmsDao.containsDocumentKey("TEST/PhoenixTest");
assertTrue(result);
}
}
abstract class:
#Transactional
#TestPropertySource({"classpath:application-test.properties"})
#ContextConfiguration(classes = {RepositoryContext.class}, loader = AnnotationConfigContextLoader.class)
public abstract class AbstractPhoenixDao extends AbstractTransactionalTestNGSpringContextTests {
public AbstractPhoenixDao() {
}
}
Can someone please help me to know why the data is getting rolled back before the test happens.
the data is getting created because if I keep 2 values in insert with same primary key the sql run at start gives an error.
we are using postgres.
I have two different test classes, one testing a module that I wrote and the other testing a user-defined function that I developed. These two tests instantiate a Neo4j for testing purposes differently. Module test does it like this:
class ModuleTest
{
GraphDatabaseService database;
#Before
public void setUp()
{
String confFile = this.getClass().getClassLoader().getResource("neo4j-module.conf").getPath();
database = new TestGraphDatabaseFactory()
.newImpermanentDatabaseBuilder()
.loadPropertiesFromFile(confFile)
.newGraphDatabase();
}
}
While the UDF test class instantiates its embedded database this way:
public class UdfTest
{
#Rule
public Neo4jRule neo4j = new Neo4jRule()
.withFunction(Udf.class);
#Test
public void someTest() throws Throwable
{
try (Driver driver = GraphDatabase.driver(neo4j.boltURI() , Config.build().withEncryptionLevel(Config.EncryptionLevel.NONE).toConfig())) {
Session session = driver.session();
//...
}
}
}
The problem here is that in the first form the UDFs are not registered and in the second the module. My question is; how can I start an embedded Neo4j database for my tests in which both my module and UDF is loaded?
Take a look at how APOC Procedures loads procedures and functions within their test classes. They call a utility method during setUp():
public static void registerProcedure(GraphDatabaseService db, Class<?>...procedures) throws KernelException {
Procedures proceduresService = ((GraphDatabaseAPI) db).getDependencyResolver().resolveDependency(Procedures.class);
for (Class<?> procedure : procedures) {
proceduresService.registerProcedure(procedure);
proceduresService.registerFunction(procedure);
}
}
Just pass the GraphDatabaseService and the Class with the procedures/functions to register, and this should set everything up for your test class.
I'm working on a JavaEE application with EJB and JPA.
I'm doing some tests and entities are persisting. Although, I'd like to remove entities between those tests. I tried to use the method EntityManager.clear() but this doesn't work. When I try to consult one of the old entities, it is still on the EntityManager.
How can I solve this problem?
Invoking EntityManager.clear() will not remove the entities, because they are persisted in the database.
You could close your entity manager factory and open again:
#Before
public void beforeTest() {
entitManagerFactory.close()
entitManagerFactory = // new EntityManagerFactory
}
The problem with this approach is that create an EntityManager may slow down the tests process.
What you could do instead is:
1) You could delete all entities before the test:
#Before
public void beforeTest() {
// execute a query like delete from Person p
}
2) You could use a new ID/values for every test:
#Test
public void test1() {
Person person = PersonTestUtil.createPerson("11111");
}
#Test
public void test2() {
Person person = PersonTestUtil.createPerson("22222");
}
I am making Test of My classes so I am inserting so many data for to test my code.
So I am thinking to make some mechanism of savepoint and rollback in DB.
I am using postgresql as DB sever.
Following is my code for test :
#RunWith(SpringJUnit4ClassRunner.class)
#WebAppConfiguration
#ContextConfiguration("file:src/main/webapp/WEB-INF/ls-dispatcher-servlet.xml")
public class AddBindingProcessorTest extends IntegrationTestBase {
#Autowired
private AddBindingProcessor processor;
public AddBindingProcessorTest(){
}
#Before
public void setUp() throws Exception {
}
#After
public void tearDown() throws Exception {
}
#Test
public void add() throws Exception {
AddBinding command;
command = new AddBinding();
command.setId(50l);
command.setBindingName("bindingtest1");
command.setBindingPrice((double)253);
BindingTypeResponse response = (BindingTypeResponse)processRequest(command);
System.out.println("from addbindingprocessor test "+response.getBindingName());
}
}
Here I am setting value through command object and passing to ProcessRequest() Method that will store data inside DB using hibernate.
Still I have to write assert in my testProcess() method that will check data is correct or not ?
So my question is that I when this transaction starts in setUp() method one savepoint should be created and then testProcess() method will be executed and assert check for the data that they are correct or not and then in tearDown() method I want to rollback to savepoint that is set in setUp() method.
So how to do so ? If Anyone can just guide me that what I ll have use and how to move forward then I ll learn that thing and go by myself.
I just want guidance about it that what I ll have to use and where ?
Thank You All.
If I get you right, you can just use the
#TransactionConfiguration(defaultRollback = true)
annotation below your #ContextConfiguration annotation.
This will rollback the changes in your tests after every run.
user3145373 pointed out, that the attribute transactionManager="context bean transaction manager" in #TransactionConfiguration needed to be set also.
It is part of the spring-test lib.