from spring doc:
One common issue in tests that access a real database is their effect
on the state of the persistence store. Even when you use a development
database, changes to the state may affect future tests. Also, many
operations — such as inserting or modifying persistent data — cannot
be performed (or verified) outside of a transaction.
The TestContext framework addresses this issue. By default, the
framework creates and rolls back a transaction for each test. You can
write code that can assume the existence of a transaction. If you call
transactionally proxied objects in your tests, they behave correctly,
according to their configured transactional semantics. In addition, if
a test method deletes the contents of selected tables while running
within the transaction managed for the test, the transaction rolls
back by default, and the database returns to its state prior to
execution of the test. Transactional support is provided to a test by
using a PlatformTransactionManager bean defined in the test’s
application context.
If you want a transaction to commit (unusual, but occasionally useful
when you want a particular test to populate or modify the database),
you can tell the TestContext framework to cause the transaction to
commit instead of roll back by using the #Commit annotation.
How can we be certain that a transactional test was successful if the transaction is rolled back after it? Perhaps the test will be failed as a result of the transaction failing upon commit, for instance because of a violation of a SQL constraint (in relational databases transactions). or, am I missing something?
As per my understanding, by default transaction got roll-back so that state of database remains unchanged.
For cases like unique constraint violation, ideally you should be verifying exception message/code which your application is throwing other than verifying state of transaction in unit tests.
Please note, you don't require to verify if rollback actually rolling back transaction or not, but you need to verify if error is thrown from your application after constraint violation occurred.
So your success criteria in this case is to check error is thrown after trying to insert duplicate record, and to check if error message which you have thrown from your method is correct.
For cases like update/insert to table, you can mark test case with explicit commit & verify it by executing select query within test; that will be your success criteria.
Related
I've been working with Spring and Hibernate for about two years. Recently I have also been working on testing. Now I'm not quite sure if I understood everything correctly. Do I understand correctly that the following methods exist? **If I make wrong assumptions, please correct me!
Method 1:
Situation: The test class is annotated with #Transactional. The test data is created manually in an #BeforeEach method and stored in a repository.
Advantages: Through #Transactional annotation, all (BeforeEach, Test-Method, AfterEach) methods are executed in one transaction, which can be undone directly by rollback and therefore no emptying of the database is necessary.
Disadvantages: Since everything is carried out in one transaction and canceled directly by rollback, the data never ends up correctly in the database? Perhaps errors would occur during a commit? This means that the test does not reflect a real situation.
Method 2:
Situation: The test class has no #Transactional annotation. The test data is created and stored in an #BeforeEach method.
Advantages: Since the #Transactional annotation is missing, all calls of the service or controller are executed in a separate transaction, reflecting a real situation.
Disadvantages: Since everything is executed in separate transactions, the database must be completely emptied manually after each test (disable constraints and empty each table).
I have another question, but it's more subjective Do you like the initialization of test data using the #BeforeEach method and manual creation of objects and saving via repository or SQL scripts in #Sql annotation better? Initializing via SQL scripts feels faster in my opinion.
I am trying to figure out why #Transactional does not rollback data in envers audit table after each test and how to fix it.
How can I make that happen in spring integration tests?
I have tried with #DirtiesContext and that makes it work but it's rather workaround and makes tests run much longer which I don't like.
Does any of you have any idea how to make it work?
Hibernate Envers acts as a transaction-commit-time audit solution. What that ultimately means is that all the persistence changes that happen during a transaction are examined and a set of work unit operations are cached in memory. In short, the only time Envers flushes operations to the audit schema is immediately before the commit of a successful transaction.
So how does this all work from a Hibernate point of view?
Hibernate Envers registers 2 very critical callback operations with Hibernate ORM when a transaction that operates on an audited entity is detected, a before and after transaction completion callback. The before callback is what actually performs the flushing of the audit changes to the audit tables and the after is responsible for cleaning up any resource allocations that are related to the transaction.
The only time that the before callback actually happens is when the hibernate transaction coordinator is asked to commit the transaction. If the transaction has been marked for rollback when the commit is requested, then the before callback is skipped.
The after callback always happens no matter the transaction status.
It would seem whats likely happening is #Transactional is creating the transaction boundary and the method is then invoked to perform its operations and when the method exits, the annotation forces a transaction commit leading to the observed behavior.
I can see a few options for you:
Override transaction behavior for tests to be read-only.
Design tests to clean-up their test data after validating the test use case.
Design tests to work even if existing test data exists in the tables.
Disable Envers via configuration if it isn't needed as a part of that integration test.
We identified a transaction nesting issues after seeing UnexpectedRollbackExceptions in a production logs. This was because we handle specific database errors fairly close to the #Transactional persistence code, but further up at the service level was another #Transactional annotation that errored because the transaction was set to rollback only (as explained here: http://codetreasury.com/2014/01/25/springs-transactional-important-points/)
Our tests were initially too low level, so I added some further up the stack to confirm our fix, however the transaction exception thrown is different:
In live we see: UnexpectedRollbackException
In JUnit/Spring integration tests we see: TransactionSystemException
I'm concerned our tests are inconsistent with live code - can you explain why the exceptions are different?
I would like to create some data in #BeforeClass and use them in my #Test method.
After all tests are finished (failed or succeeded), I would like to rollback the data.
Can this be achieved using the annotations #BeforeClass, #Transactional, without having to clean up the data explicitly?
We had a similar problem here. Our solution was to give each test class an embedded H2 database (just takes a couple of seconds to setup the first one; after that, it's not really noticeable anymore).
That allowed us to load any kind of test data into the database, no need to clean that up after all tests have run.
Each test still gets its own transaction, so after each individual test, the per-class database is rolled back to the original state.
To debug tests, we would annotate the individual test with #Transactional(rollback=false), so we could look at the database with an SQL tool.
Another test would examine all tests classes, looking for this annotation to make sure no one accidentally commits it.
I have a very interesting situation. I am slightly new to JBoss and Oracle, having worked mostly with Weblogic on DB2. That said, what I am trying to do is pretty simple.
I have a local-tx-datasource to an Oracle database. From my Java I code, I invoke datasource.getConnection() after retrieving the datasource using the appropriate JNDI name. The local-tx-datasource declaration in my -ds.xml file does not have any explicit reference to autocommit behaviour.
After getting the connection, I execute a create/update query and I get back the correct update count. Subsequently, for a short duration, I am even able to retrieve this record. However, after that the database pretends it never got the record in the first place, and there is nothing at all.
My experience with connections suggests that this happens when the connection does not commit its work, and so only that connection itself will be able to see the data in its transaction. From what I read, JBoss too follows the specification that the Connection returned is an autocommit one. I even verified this from my Java code, and it states the autocommit behaviour is set to true. However, if that was the case, why are my records not getting created / updated?
Following this, I set the Connection's autocommit behaviour to false (again from Java code), and then did the commit explicitly. Since then, there has been no issue.
What could possibly be going wrong? Is my understanding of autocommit here incorrect or does JBoss have some other interpretation of it. Please note, I do not have any transactions at all. These are very simple single record insert queries.
Please note, I do not have any transactions at all.
Wrong assumption. The local-tx-datasource starts a JTA transaction in your behalf. I'm not sure how the autocommit works in this scenario, but I suppose that autocommit applies only when you are using exclusively JDBC transactions, not JTA transactions.
In JTA, if you don't commit a transaction[*], it will be rolled back after the timeout. This explains the scenario that you are experiencing. So, I'd try to either change the local-tx-datasource to no-tx-datasource or to manually commit the transaction.
Note, however, that not managing your transactions is a bad thing. Autocommit should always be avoided. There's no better party to determine when to commit than your application. Leaving this responsibility to the driver/container is, IMO, not very responsible :-)
[*] One exception is for operations inside EJBs, whose business methods are "automatically" wrapped in a JTA transaction. So, you don't need to explicitly commit the transaction.