Toplink in Memory

 

 

 

 

 

 

Toplink in memory will let you run your tests faster, so you can run them ALL before you check in. Continuous integration without having to wait for cruise control or a colleague to yell at you. Fewer failing tests due to test interaction, Toplink in Memory automatically deletes all the objects you created during the test – but leaves the fixtures in the database untouched.

 

How does it do this? Well, it stubs out the database. When in production, ToplinkInMemory calls Oracles Toplink normally – which uses the database. During testing all the objects are created in memory and only in memory. NOTHING is written to a disk. And none of those pesky transactions (expensive) are used to save even more time during test runs.

 

This library was created out of need. We are currently running 4700 tests which take multiple hours to run against the database. Our in memory frame work allow us to run them in as little as five minutes. We hope it will be of use to you.

 

Download

 

http://sourceforge.net/projects/toplink-in-mem/

 

Quick Start Guide

 

Well I assume you’ve downloaded the source code for both the examples and the library and imported it into an eclipse project. The configuration information is at the end of the quick example. We’ll take you through the following steps:

 

  1. Creating an Object
  2. Finding an Object by Id
  3. Finding an Object by Expression
  4. Creating a simple test
  5. Wrapping the test in an InMemoryDecorator
  6. Wrapping the test in an DatabaseDecorator
  7. Configuration

 

Creating an object

 

The basic steps to create an object are the same as in toplink. However, the object must implement the Identifiable interface which ensures the object responds to the messages setId(Long) and getId().  This example is taken from the ToplinkInMemoryExample package.

 

 

public static Long createTestObjectWithName(final String expectedName) throws Exception {

        Transaction txn = new Transaction() {

            public Object executeTransaction() throws Exception {

                TestObject testObject = (TestObject) TransactionManagerImplementation.getInstance().newInstance(

                        TestObject.class);

                testObject.setName(expectedName);

                return testObject.getId();

            }

        };

   

        final Long id = (Long) TransactionHandler.getInstance().runTransaction(txn);

        return id;

    }

 

 

Finding an Object by Id

 

This example is taken from the ToplinkInMemoryExample package.

 

 

  

Public static String findTestObjectNameById(final Long id) throws Exception {

        Transaction txn2 = new Transaction() {

            public Object executeTransaction() throws Exception {

                TestObject testObject = (TestObject) TransactionManagerImplementation.getInstance().findById(

                        TestObject.class, id);

                return testObject.getName();

            }

        };

   

        String actualName = (String) TransactionHandler.getInstance().runTransaction(txn2);

        return actualName;

    }


Finding an Object using an Expression

This example is taken from the SfToplinkInMemory package. There are many more tests in this package that test different expressions.

 

In this test we don’t use a transaction – because it runs in memory only. We can ignore the Unit of work and test the inner workings of our classes without having to worry about them being fully formed.

 

 

    public void testFindAllInstancesWhere_NoInstances() throws Exception {

        TestObject expectedTestObject = createAnonymousTestObject();

        TestObject notFoundTestObject = createAnonymousTestObject();

        ExpressionBuilder testObject = new ExpressionBuilder();

 

        Expression whereNameEquals = whereNameEqualsExpression("This is not the name");

 

        // execute

        List actualTestObjects = TransactionManagerImplementation.getInstance().findAllInstancesWhere(whereNameEquals,

                TestObject.class);

 

        // assert

        assertTrue("TestObject not found", actualTestObjects.isEmpty());

    }

 

private Expression whereNameEqualsExpression(String name) {

    ExpressionBuilder testObject = new ExpressionBuilder();

    Expression whereNameEquals = testObject.get("name").equal(name);

    return whereNameEquals;

}

 

A simple test

Returning to our previous example we now examine a simple test. In this case we check to make sure we can create and then read back an object from the database.

 

   

public void test_example_create_and_find() throws Exception {

        // setup

        String expectedName = "Bob";

        Long id = TestObjectFacade.createTestObjectWithName(expectedName);

 

        // execute

        String actualName = TestObjectFacade.findTestObjectNameById(id);

       

        // verify

        assertEquals("Expected Same name", expectedName, actualName);

    }

 

Running it In Memory

 

Let us run that test in memory.

The InMemoryTestDecorator configures the test to run in memory using the JUnit test decorator pattern.

Note the automatic rapidCleanup().

 

public class InMemoryTestDecorator extends TestSetup {

 

    public InMemoryTestDecorator(Test arg0) {

        super(arg0);

    }

   

 

    protected void setup() throws Exception {

        TransactionManagerImplementation.getInstance().useInMemoryTransactionManager();

        TransactionManagerImplementation.getInstance().setDatabaseManager(DatabaseManager.getInstance());

    }

 

    protected void tearDown() throws Exception {

        TransactionManagerImplementation.getInstance().rapidCleanup();

    }

}

 

Next we create an In Memory test suite to wrap our current test suite in the InMemoryTestDecorator.

 

public class AllTestsInMemory extends TestCase {

   

    public static Test suite() {

       return new InMemoryTestDecorator(AllTests.suite());

    }

}

 

Now you can run AllTestsInMemory and all will work.

 

Running it In Database

Let us run that test Against the database.

The DatabaseTestDecorator configures the test to run in memory using the JUnit test decorator pattern.

The DatabaseManager implements the DatabaseManagerInterface (Basically a factory for getting the toplink session).

A TestCleanupListener watches all the objects created by Toplink so it can delete them in the teardown of the test.

 

public class DatabaseTestDecorator extends TestSetup {

 

    private TestCleanupListener testCleanupListener;

 

    public DatabaseTestDecorator(Test arg0) {

        super(arg0);

    }

 

    protected void setUp() throws Exception {

        try {

            DatabaseManager.getInstance().initialize();

            TransactionManagerImplementation.getInstance().useDatabaseTransactionManager();

            TransactionManagerImplementation.getInstance().setDatabaseManager(DatabaseManager.getInstance());

 

            testCleanupListener = new TestCleanupListener();

            TransactionManagerImplementation.getInstance().addNewInstanceListener(testCleanupListener);

        } catch (Exception e) {

            e.printStackTrace();

            throw e;

        }

    }

 

    protected void tearDown() throws Exception {

        TransactionManagerImplementation.getInstance().removeNewInstanceListener(testCleanupListener);

        testCleanupListener.deleteNewInstances(getTest().toString());

    }

}

 

Next we create a Database test suite to wrap our current test suite in the DatabaseTestDecorator.

 

public class AllTestsAgainstDatabase extends TestCase {

   

    public static Test suite() {

       return new DatabaseTestDecorator(AllTests.suite());

    }

}

 

Now you can run AllTestsAgainstDatabase and all will work provided you’ve created the database and configured the DatabaseManager.

 

Configuring the Database Manager

 

Here’s an example. More to come later

 

public class DatabaseManager implements DatabaseManagerInterface {

    private static final String DRIVER_CLASS_NAME = "oracle.jdbc.driver.OracleDriver.class";

 

    private static final String USER_PWD = "test";

 

    private static final String USER_NAME = "test";

 

    private static final String JDBC_URL = "jdbc:oracle:thin:@192.168.1.101:1521:MYDB";

 

    private ServerSession serverSession;

 

    private Project project;

 

    protected static DatabaseManager instance = new DatabaseManager();

 

    public static DatabaseManager getInstance() {

        return instance;

    }

 

    private DatabaseManager() {

        super();

    }

 

 

    public void initialize() {

        project = new TestToplinkProject();

 

        DatabaseLogin login = new DatabaseLogin();

        // use your own Sequence table

        login.usePlatform(new oracle.toplink.internal.databaseaccess.OraclePlatform());

 

        login.setUsesNativeSequencing(false);

        login.setSequencePreallocationSize(50);

        login.setSequenceTableName("SEQUENCES");

        login.setSequenceNameFieldName("SEQ_NAME");

        login.setSequenceCounterFieldName("SEQ_COUNTER");

 

        // How to connect

        login.setConnectionString(JDBC_URL);

        login.setUserName(USER_NAME);

        login.setPassword(USER_PWD);

        login.setDriverClass(oracle.jdbc.driver.OracleDriver.class);

//        login.setDriverClassName(DRIVER_CLASS_NAME);

       

        // Configuration properties.

//        login.setShouldBindAllParameters(true);

//        login.setShouldCacheAllStatements(true);

//        login.setUsesByteArrayBinding(true);

//        login.setUsesStringBinding(false);

//        if (login.shouldUseByteArrayBinding()) { // Can only be used with binding.

//            login.setUsesStreamsForBinding(false);

//        }

//        login.setShouldForceFieldNamesToUpperCase(false);

//        login.setShouldOptimizeDataConversion(true);

//        login.setShouldTrimStrings(true);

//        login.setUsesBatchWriting(false);

//        if (login.shouldUseBatchWriting()) { // Can only be used with batch writing.

//            login.setUsesJDBCBatchWriting(true);

//        }

//        login.setUsesExternalConnectionPooling(false);

//        login.setUsesExternalTransactionController(false);

        project.setLogin(login);

 

        serverSession = (ServerSession) project.createServerSession(1, 10);

        serverSession.logMessages();

        serverSession.login();

    }

 

    public UnitOfWork acquireUnitOfWork() {

        return serverSession.acquireClientSession().acquireUnitOfWork();

    }

 

    public ServerSession getServerSession() {

        return serverSession;

    }

 

    public Session getSession() {

        return serverSession;

    }

 

    public Project getProject() {

        return project;

    }

 

}