File this one under working theory at the moment.
I have an embedded test framework that strives at the expense of performance
to achieve as much isolation as possible.
To that end, here is the workflow:
1. Maven surefire forks a VM and instantiates a JUnit test class.
Standard Maven stuff; I have <forkMode> set to "always", which does exactly
this.
2. For each method in the test class:
1. I connect to a named H2 database in memory set to not shut down
until the VM exits. The first such connection will create the (empty)
database if it isn't there already.
2. Liquibase (www.liquibase.org) creates the schemas and tables if
they don't already exist. This will effectively only happen
once; Liquibase
knows what it has run already.
3. DbUnit (www.dbunit.org) issues a CLEAN_INSERT statement (
http://www.dbunit.org/components.html#cleanInsert) and loads in test
data into the tables. The "clean" part means that it
effectively truncates
the test tables involved (DELETE FROM table). The "insert" part reads an
XML file and inserts those rows into the relevant tables. The
net effect is
that for each test method I have a clean, known, in-memory database
containing only data that I put there--no more, no less. The
database looks
exactly the same at the beginning of each test method. I can
prove that no
test method impacts the data view of another test method.
4. A new embedded Glassfish instance is created and started. A new
one. Did I mention this is a new one?
5. The EJB app under test is deployed. (There are many reasons why
javax.ejb.EJBContainer doesn't work here.)
6. The EJB under test is retrieved via a JNDI lookup in the
java:global space.
7. The EJB is installed into the test (during its setUp()) method. It
usually has an EntityManager injected in it by Glassfish that it
makes heavy
use of.
8. The test does whatever the test does. Usually this involves
updating and reading values in the H2 database.
9. Glassfish is stopped and destroyed. Note: destroyed. Gone.
Finis. Killed. Eliminated. Erased.
1. (As a teaser, I assume this means that EclipseLink-as-bundled by
Glassfish is destroyed as well.)
3. After all methods in the test case have run like this, we're done.
What I am seeing is that the effects of one test method's operation on the
database are visible--through EclipseLink-supplied EntityManagers only, not
in the database--by other test methods. Specifically, if I ask H2 what the
version column values are at the beginning of a test method, H2 responds
"0", as I would expect (their initial value as set up by DbUnit). If I
find() the relevant entity and ask him about his @Version property, however,
his version is often 3 or 4, indicating that either I'm going mad (possible)
or that the entity in question is being sourced from a cache that should
have died with the destruction of Glassfish in step 9.
Again, there is a new Glassfish instance launched and destroyed for each
test method. And yet it appears that some values are still coming from the
EclipseLink session cache, even though Glassfish has died and risen again in
between test methods.
Something smells "static" here in a world of "instances".
Before I (lose money and :-)) spend time writing a test case for this, is
there something inherent (hi, Bhavani) in the embedded Glassfish design as
it interacts with EclipseLink that would result in the EclipseLink session
cache being leaked in this manner? Is this kind of method-by-method,
JUnit-based isolation impossible with embedded Glassfish?
See you all at JavaOne!
Best,
Laird
--
http://about.me/lairdnelson