Unit testing with in-memory databases

857 views
Skip to first unread message

Mark Bigler

unread,
Oct 29, 2012, 8:13:32 AM10/29/12
to orient-...@googlegroups.com
Hi

I'm getting exceptions when running multiple unit tests with custom registered entity classes against an in-memory database. The database is created before each test and dropped afterwards. Executing tests separately everything works fine. But when I running all tests in a suite, the first test succeeds and all of the following test are failing.

OrientDB-Release: 1.2.0

Here is a simplified test case to reproduce the problem. I added some "debug" output - note that the dropped and freshly created database is still using the "old" entity manager.

    private OObjectDatabaseTx db;

   
@Before
   
public void before() throws Exception {
        db
= new OObjectDatabaseTx("memory:test");
       
System.out.println("db.exists():           " + db.exists());
       
if(!db.exists()) {
            db
.create();
           
System.out.println("db.create()");
       
}
       
System.out.println("db:                    " + db.getClass().getName() + "@" + Integer.toHexString(db.hashCode()));
       
System.out.println("db.getEntityManager(): " + db.getEntityManager());

        db
.getEntityManager().registerEntityClass(TestObject.class);
   
}

   
@After
   
public void after() throws Exception {
        db
.drop();
       
System.out.println("db.drop()");
   
}

   
@Test
   
public void testOne() throws Exception {
       
System.out.println("testOne start");
       
System.out.println(db.query(new OSQLSynchQuery<OUser>("select from OUser")));
       
System.out.println(db.query(new OSQLSynchQuery<TestObject>("select from TestObject")));
       
System.out.println("testOne end");
   
}

   
@Test
   
public void testTwo() throws Exception {
       
System.out.println("testTwo start");
       
System.out.println(db.query(new OSQLSynchQuery<OUser>("select from OUser")));
       
System.out.println(db.query(new OSQLSynchQuery<TestObject>("select from TestObject")));
       
System.out.println("testTwo end");
   
}



Output: (running all test-cases)
db.exists():           false
db.create()
db:                    com.orientechnologies.orient.object.db.OObjectDatabaseTx@13b33a0e
db.getEntityManager(): com.orientechnologies.orient.core.entity.OEntityManager@5ba8773c
testOne start
[admin, reader, writer]
[]
testOne end


db.drop()
db.exists():           false
db.create()
db:                    com.orientechnologies.orient.object.db.OObjectDatabaseTx@478e4327
db.getEntityManager(): com.orientechnologies.orient.core.entity.OEntityManager@5ba8773c
testTwo start
[admin, reader, writer]
com.orientechnologies.orient.core.exception.OQueryParsingException: Error on parsing query
Query:  TestObject
------^
    at com.orientechnologies.orient.core.sql.filter.OSQLTarget.<init>(OSQLTarget.java:70)
    at com.orientechnologies.orient.core.sql.OSQLEngine.parseTarget(OSQLEngine.java:283)
    at com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect.parse(OCommandExecutorSQLSelect.java:115)
    at com.orientechnologies.orient.core.sql.OCommandExecutorSQLSelect.parse(OCommandExecutorSQLSelect.java:79)
    at com.orientechnologies.orient.core.sql.OCommandExecutorSQLDelegate.parse(OCommandExecutorSQLDelegate.java:48)
    at com.orientechnologies.orient.core.sql.OCommandExecutorSQLDelegate.parse(OCommandExecutorSQLDelegate.java:33)
    at com.orientechnologies.orient.core.storage.OStorageEmbedded.command(OStorageEmbedded.java:77)
    at com.orientechnologies.orient.core.sql.query.OSQLQuery.run(OSQLQuery.java:69)
    at com.orientechnologies.orient.core.sql.query.OSQLSynchQuery.run(OSQLSynchQuery.java:78)
    at com.orientechnologies.orient.core.query.OQueryAbstract.execute(OQueryAbstract.java:32)
    at com.orientechnologies.orient.core.db.record.ODatabaseRecordAbstract.query(ODatabaseRecordAbstract.java:397)
    at com.orientechnologies.orient.core.db.ODatabaseRecordWrapperAbstract.query(ODatabaseRecordWrapperAbstract.java:178)
    at com.orientechnologies.orient.object.db.ODatabasePojoAbstract.query(ODatabasePojoAbstract.java:218)
    ...
Caused by: com.orientechnologies.orient.core.exception.OCommandExecutionException: Class 'TESTOBJECT' was not found in current database
    at com.orientechnologies.orient.core.sql.filter.OSQLTarget.extractTargets(OSQLTarget.java:150)
    at com.orientechnologies.orient.core.sql.filter.OSQLTarget.<init>(OSQLTarget.java:61)
    ... 38 more
db.drop()



@OrientDB users/developers: is anyone else extensively writing unit tests with in-memory databases? What about unit testing classes (e.g. webservice servlets) using a OObjectDatabasePool? Please share your experience so we can create a new wiki page about writing unit tests.

Greetings
Mark

Mark Bigler

unread,
Oct 29, 2012, 9:42:31 AM10/29/12
to orient-...@googlegroups.com
It looks like the entity manager is shared for every OObjectDatabaseTx with the same url...

  // OObjectDatabaseTx:680
 
protected void init() {
    entityManager
= OEntityManager.getEntityManagerByDatabaseURL(getURL());
   
...
 
}

... and the entity class handler is used for all instances running in the same JVM.
 // OObjectDatabaseTx:680
 
protected void init() {
   
...
    entityManager
.setClassHandler(OObjectEntityClassHandler.getInstance());
   
...
 
}

I'm not sure if this probably caused the problem, but it leads to a confusing situation: two different databases, two different entity managers but all the registered entity classes for database "A" are already registered for database "B" too.

OObjectDatabaseTx db = new OObjectDatabaseTx("memory:A");

System.out.println("db:                             " + db.getClass().getName() + "@" + Integer.toHexString(db.hashCode()));
System.out.println("db.getURL():                    " + db.getURL());
System.out.println("db.getEntityManager():          " + db.getEntityManager());
System.out.println("before registerEntityClass():   " + db.getEntityManager().getEntityClass(TestObject.class.getSimpleName()));
db
.getEntityManager().registerEntityClass(TestObject.class);
System.out.println("after registerEntityClass():    " + db.getEntityManager().getEntityClass(TestObject.class.getSimpleName()));

db:                             com.orientechnologies.orient.object.db.OObjectDatabaseTx@299209ea
db.getURL():                    memory:A
db.getEntityManager():          com.orientechnologies.orient.core.entity.OEntityManager@32c8f6f8
before registerEntityClass():   null
after registerEntityClass():    class ...$TestObject


OObjectDatabaseTx db2 = new OObjectDatabaseTx("memory:B");
System.out.println("db:                             " + db2.getClass().getName() + "@" + Integer.toHexString(db2.hashCode()));
System.out.println("db.getURL():                    " + db2.getURL());
System.out.println("db.getEntityManager():          " + db2.getEntityManager());
System.out.println("before registerEntityClass():   " + db2.getEntityManager().getEntityClass(TestObject.class.getSimpleName()));
db2
.getEntityManager().registerEntityClass(TestObject.class);
System.out.println("after registerEntityClass():    " + db2.getEntityManager().getEntityClass(TestObject.class.getSimpleName()));

db:                             com.orientechnologies.orient.object.db.OObjectDatabaseTx@43ef9157
db.getURL():                    memory:B
db.getEntityManager():          com.orientechnologies.orient.core.entity.OEntityManager@252f0999
before registerEntityClass():   ...$TestObject
after registerEntityClass():    ...$TestObject



Mark Bigler

unread,
Oct 29, 2012, 10:57:39 AM10/29/12
to orient-...@googlegroups.com
This could also be helpful - registered entity class "TestObject" is missing in a new/different database:

OObjectDatabaseTx db = new OObjectDatabaseTx("memory:A");
db
.create();
db
.getEntityManager().registerEntityClass(TestObject.class);
System.out.println("DB - METADATA - SCHEMA - CLASSES: " + db.getURL());
for(OClass oClass : db.getMetadata().getSchema().getClasses()) {
   
System.out.println(oClass);
}
//db.drop();


OObjectDatabaseTx db2 = new OObjectDatabaseTx("memory:B");

db2
.create();
System.out.println("DB - METADATA - SCHEMA - CLASSES: " + db2.getURL());
db2
.getEntityManager().registerEntityClass(TestObject.class);
for(OClass oClass : db2.getMetadata().getSchema().getClasses()) {
   
System.out.println(oClass);
}

DB - METADATA - SCHEMA - CLASSES: memory:A
ORestricted
TestObject
ORIDs
OIdentity
ODocumentWrapper
OFunction
OUser
ORole

DB - METADATA - SCHEMA - CLASSES: memory:B
ORestricted
ORIDs
ODocumentWrapper
OIdentity
OUser
OFunction
ORole

Tim Barrett

unread,
Dec 5, 2012, 10:07:24 AM12/5/12
to orient-...@googlegroups.com
Hello,

Is there a workaround or even better a solution for this issue? In our project we have situations where users will potentially be working with one database, closing it, then opening up or creating a second database. I am then finding, exactly as Mark identified below, that sql queries for classes of types that I had registered in the second database are throwing OQueryParsingExceptions. The exception blurb is then telling me that the class I am querying for was not found in current database, which of course it should be because just as in Mark's succinct example below the classes were registered via the entity manager in the new database. This is affecting all of the queries, these are being used in situations where an index is unnecessary.

Working with a local OObjectDatabaseTx, version 1.2.0 of Orientdb.

Cheers,

Tim 

Luca Garulli

unread,
Dec 5, 2012, 7:27:57 PM12/5/12
to orient-database
Hi,
as recap what is the problem? Mark pointed us that the entity manager is the same for different databases? This sounds like a bug. Have you tried against latest 1.3.0-SNAPSHOT?

Lvc@



--
 
 
 

Tim Barrett

unread,
Dec 6, 2012, 1:52:34 AM12/6/12
to orient-...@googlegroups.com
Hi Luca,

Thanks for the response.

I haven't tried against 1.3 snapshot I will do that today.

I was struggling with the following, all steps carried out within the same JVM

1) Create/open database1.
2) Register entity classes A,B,C in database 1.
3) Work with these entity classes, saving, loading, and querying them.
4) Close database 1.
5) Create/open database2.
6) Register entity classes A,B,C in database 2. (these being exactly the same classes that were previously registered in database1.
7) Any queries (select from A) in database2 fail with a query parse exception with an exception message telling me that the class I was querying (A) does not exist in the database. All other operations in database2 for these classes succeed - saving, loading, retrieving by ORID. And once an object has been saved, queries *do* work, they just don't work when there are no instances of the class in the database. The workaround is to look for instance count > 0 before exceuting a query but ideally that shouldn't be necessary.

I didn't want to report this until I had either found errors in my own code or found the issue reported in this group. This case is easily reproducable. When searching through this group for a similar problem I came across Mark's description which I think describes the same issue and he went further by identifying the probable cause. This thread was focusing on the issue surfacing when several junit tests are run by maven and a solution was given for maven which essentially (I think) causes a new JVM to be used for each test - which is actually more of a workaround I think and left the original problem unsolved for those who would be needing this ability to use different databases in a live situation. In my case it is absolutely normal for users to be working with different databases within one single session and one single JVM as they are building navigable indexes of different sets of documents.

As I said in my earlier post this is all occurring when using the object database.

Cheers,

Tim

Luca Garulli

unread,
Dec 6, 2012, 3:21:44 AM12/6/12
to orient-database
Hi Tim,
classes are auto-created once you save the first object. Probably in the first database you first create then query while in the second case you make the opposite?

So query can't find the class.

Lvc@


On 6 December 2012 07:52, Tim Barrett <comc...@gmail.com> wrote:
when

Tim Barrett

unread,
Dec 6, 2012, 6:30:53 AM12/6/12
to orient-...@googlegroups.com
Hi Luca,

Problem still there with 1.3.0-SNAPSHOT.

Cheers,

Tim

Tim Barrett

unread,
Dec 10, 2012, 4:38:31 AM12/10/12
to orient-...@googlegroups.com
Hi,

I'm pretty sure this is a bug, it can be reproduced with the code supplied by Mark.

In my case I get the problem running through exactly the same set of logic just for 2 different databases, so no question of doing things differently between 1 and 2. 

- Create a database
- register classes and indexes
- whilst creating a large number of instances of various classes check to ensure instances don't already exist using an sql query
- close database
- Create a second database
- register classes and indexes
 whilst creating a large number of instances of various classes check to ensure instances don't already exist using an sql query - it is the execution of these queries that causes the problem, parsing query raised. class I am querying against unknown to database.

It's quite a serious problem in my use case as each database is essentially a user created index of various documents they wish to search, so database 1 might be a collection fo all the works of Dickens and database 2 might be a collection of photos. Or database 2 might even be a completely new recreation of database 1. I can workaround it in various ways but I would prefer to use what seems like a more conventional root of indexing the classes I need to index and then querying for existence prior to saving in order to determine whether to save a new one or update an existing one.

The problem occurs under 1.3.0 as well, checked that last week.

Cheers,

Tim

Simone Gianni

unread,
Aug 26, 2013, 6:08:44 AM8/26/13
to orient-...@googlegroups.com, comc...@gmail.com
Hi all, ciao Luca,
I'm hitting this same error on 1.5.0 . I've tried dropping the database from test to test, using always a new db ("memory:test" + System.nanoTime()) , registering the entity package only once and registering it always, but all tests after the first one executed fail with :

com.orientechnologies.orient.core.exception.OQueryParsingException: Error on parsing query at position #8: Error on parsing query
[...]
Caused by: com.orientechnologies.orient.core.exception.OCommandExecutionException: Class 'MSGUSER' was not found in current database
at com.orientechnologies.orient.core.sql.filter.OSQLTarget.extractTargets(OSQLTarget.java:161)
at com.orientechnologies.orient.core.sql.filter.OSQLTarget.<init>(OSQLTarget.java:61)
... 38 more

The OEntityManager is new for each database, but the OObjectEntityClassHandler is reused. It seem to contain (in its entityClasses Map) the right classes, but cannot find it again.

I haven't digged too much in the code to understand why.

Tests run fine if started one by one.

Any solution? Am I doing something wrong?

Simone

Simone Gianni

unread,
Aug 26, 2013, 6:32:32 AM8/26/13
to orient-...@googlegroups.com
Hi again,
what I've noticed is that OObjectEntitySerializer has a static Map of classes. During the first test, on the first DB, my domain classes are correctly registered here, and correctly created on the database schema.

The second time, on the second DB, classes are already in this map, and are not created in the new database schema.

Could it be?

Simone

Luca Garulli

unread,
Aug 27, 2013, 3:27:06 AM8/27/13
to orient-database, Luca Molino
Hi Simone,
@lucamolino, please could you take at this issue?

Lvc@



--
 
---
You received this message because you are subscribed to the Google Groups "OrientDB" group.
To unsubscribe from this group and stop receiving emails from it, send an email to orient-databa...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Luca Molino

unread,
Aug 30, 2013, 5:43:26 AM8/30/13
to orient-database, Luca Garulli
Hi Simone,

in version 1.6-SNAPSHOT we added a synchronizeSchema API

https://github.com/orientechnologies/orientdb/wiki/Object-Database#schema-synchronizing



2013/8/27 Luca Garulli <l.ga...@gmail.com>



--
Luca Molino
Reply all
Reply to author
Forward
0 new messages