I've come up with a solution to my problems with data store integration tests.  
I don't know if this is a good solution; feedback is welcome.

The problem, as much as my feeble brain can grasp, is that when Spring provides 
you with a PersistenceManager, you need to use transactions so that, at the 
appropriate time, the PersistenceManager's close() is called (by Spring).  I'm 
assuming that in the previous, when I say transactions, I mean Spring's 
transactions; in my case Spring's @Transactional annotation.  When I say 
"Spring provides you with a PersistenceManager", in my case this means my DAO 
is subclassing JdoDaoSupport.

If you don't have any transactions in your test code (including your DAO) I 
don't know when or if the PersistenceManager's close() is called.  But you'll 
get errors about objects being managed by a different PersistenceManager so 
this doesn't seem to be an option.  You'll also get errors with objects that 
had detachCopy() applied to them before they were returned.

Some people use transactions in their DAOs.  My understanding is that the 
transactions should be in the coarse grained methods (the service layer) and 
not in the fine grained methods (the DAO layer).  If this doesn't sound right, 
have a look at the pdf available from

  http://apress.com/book/downloadfile/2625

Whether or not you could have your DAOs using transactions as well as your 
service layer classes, I don't know.

If you use Spring's @Transactional annotation on your unit test's methods, that 
may work, but it won't if you're using a @Before method to add stuff to the 
data store to test with, which is what was biting me.

At first I started thinking that I'd have to give up testing my DAOs and only 
test my service layer classes since they use @Transactional.  But then it 
occurred to me that I could sort of achieve the same thing by wrapping all of 
my DAO methods in transactions *but only during testing*.  So far this seems to 
work.

My test class starts out as follows:

@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({
    DependencyInjectionTestExecutionListener.class
})
@ContextConfiguration(locations = {
        "classpath:spring/applicationContext.xml",
        "classpath:spring/jdo-gae-context.xml",
        "classpath:jdo-tx-test-context.xml"
})
public class FacilityDaoTest extends LocalDatastoreTestCase {
    private final transient Logger log = LoggerFactory.getLogger(getClass());

    private final String facilityName = "abc";

    @Autowired
    @Qualifier("jdo")
    private IFacilityDao facilityDao;

    final Facility[] facilities = new Facility[] {
            new Facility("Abc Facility", this.facilityName, false),
            new Facility("Def Facility", this.facilityName + "-2", true),
            new Facility("ghi Facility", this.facilityName + "-3", true),
    };

    @Before
    public void insertFacilities() throws Exception {
        Assert.assertEquals(0, DatastoreServiceFactory
                .getDatastoreService()
                .prepare(new Query(Facility.class.getSimpleName()))
                .countEntities());

        for (final Facility eachFacility : this.facilities) {
            this.facilityDao.makePersistent(eachFacility);

            this.log.debug("facility: {}", eachFacility);
        }

        Assert.assertEquals(this.facilities.length, DatastoreServiceFactory
                .getDatastoreService()
                .prepare(new Query(Facility.class.getSimpleName()))
                .countEntities());
    }

    @Test
    public void findByName() {
        final Facility facility = 
this.facilityDao.findByName(this.facilityName);

        Assert.assertNotNull("facility should not be null", facility);
    }

applicationContext.xml contains

    <context:annotation-config />

    <context:component-scan
         base-package="com.objecteffects.waitlist"
    />

jdo-gae-context.xml contains

    <tx:annotation-driven
        transaction-manager="transactionManager"
    />

    <beans:bean
        id="dataNucleusJdoDialect"
        class="org.datanucleus.springframework.DataNucleusJdoDialect"
    />

    <beans:bean id="transactionManager" 
class="org.springframework.orm.jdo.JdoTransactionManager">
        <beans:property
            name="persistenceManagerFactory"
            ref="persistenceManagerFactory"
        />

        <beans:property
            name="jdoDialect"
            ref="dataNucleusJdoDialect"
        />
    </beans:bean>

    <beans:bean id="persistenceManagerFactory" 
class="org.springframework.orm.jdo.LocalPersistenceManagerFactoryBean">
        <beans:property
            name="persistenceManagerFactoryName"
            value="transactions-optional"
        />
    </beans:bean>

And the "trick" is jdo-tx-test-context.xml, which creates an aop proxy to wrap 
the dao methods, and it contains

    <tx:advice id="transactionAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method
                name="find*"
                read-only="true"
            />

            <tx:method
                name="*"
            />
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut
            id="waitlistServiceOperation"
            expression="execution(* 
com.objecteffects.waitlist.db.impl.dao.jdo.*.*(..))"
        />

        <aop:advisor
            advice-ref="transactionAdvice"
            pointcut-ref="waitlistServiceOperation"
        />
    </aop:config>

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Google App Engine for Java" group.
To post to this group, send email to google-appengine-java@googlegroups.com
To unsubscribe from this group, send email to 
google-appengine-java+unsubscr...@googlegroups.com
For more options, visit this group at 
http://groups.google.com/group/google-appengine-java?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to