Transactions controlled by Unit Tests

classic Classic list List threaded Threaded
6 messages Options
Reply | Threaded
Open this post in threaded view
|

Transactions controlled by Unit Tests

Jack Cox
Is there a way in the 'out-of-container' testing paradigm with openejb for the unit test method to control the demarcation of transactions?  

Here's the use case:
1) The unit test is doing entity bean testing, calling various methods on the entity facade to test CRUD operations.
2) The unit test needs to demarcate transactions so that it can insert, delete, then attempt to retrieve deleted entities.  Of course the last retrieval should fail because the entity is deleted.  
3) But, JPA has the interesting behavior that deleted entities continue to be accessible within the deleting transaction.
4) Therefore, the unit test needs to demarcate a transaction around the delete so that the entity is really removed prior to the last retrieval test.

So, how can one demarcate transactions within a junit test case?

Thanks,

Jack
Reply | Threaded
Open this post in threaded view
|

Re: Transactions controlled by Unit Tests

Alexander Saint Croix-2
Jack,

Funny you should ask--I was working on the same thing last week.
http://weblogs.java.net/blog/saintx/archive/2008/02/lambda_function.htmlshows
a good start on how to do this.

The transactions are controlled by a stateless session bean in the base
class, and the method blocks of the test case are broken down into units of
work, then fed into this session bean as Callable objects.  Each callable is
wrapped in its own transaction.  The "getCalls" method is abstract in the
base and gets called by the "test..." method in the base class, so extending
the base requires implementing "getCalls".

Now, the way I do my CRUD tests is slightly different.  They look like this:

public class CRUDTest extends CascadeTestBase {

>
>     public Callable[] getCalls() {
>         return toCallableArray(
>         getCRUD(new Address(), "Address"),
>         getCRUD(new AssociatedAddress(), "AssociatedAddress"),
>         getCRUD(new EmailAddress(), "EmailAddress"),
>         getCRUD(new GeographicAddress(), "GeographicAddress"),
>         getCRUD(new ISOCountryCode(), "ISOCountryCode"),
>         getCRUD(new Locale(), "Locale"),
>         getCRUD(new TelecomAddress(), "TelecomAddress"),
>         getCRUD(new WebPageAddress(), "WebPageAddress"),
>         getCRUD(new AssignedResponsibility(), "AssignedResponsibility"),
>         getCRUD(new Capability(), "Capability"),
>         getCRUD(new PartyRelationship(), "PartyRelationship"),
>         getCRUD(new PartyRelationshipType(), "PartyRelationshipType"),
>         getCRUD(new PartyRole(), "PartyRole"),
>         getCRUD(new PartyRoleType(), "PartyRoleType"),
>         getCRUD(new Responsibility(), "Responsibility"),
>         getCRUD(new Organization(), "Organization"),
>         getCRUD(new OrganizationName(), "OrganizationName"),
>         getCRUD(new Party(), "Party"),
>         getCRUD(new PartySignature(), "PartySignature"),
>         getCRUD(new Person(), "Person"),
>         getCRUD(new PersonName(), "PersonName"),
>         getCRUD(new Binding(), "Binding"),
>         getCRUD(new BindingValue(), "BindingValue"),
>         getCRUD(new BindingType(), "BindingType"));
>     }
>
>     private <T extends Archetype> Callable[] getCRUD(T stem, String
> table){
>         return new Tester<T>(stem, table).getCalls();
>     }
>
>     private Callable[] toCallableArray(Callable[]... array) {
>         final int l = array.length;
>         final int l2 = 3;
>         Callable[] result = new Callable[l * l2];
>
>         for (int i = 0; i < l; i++) {
>             Callable[] subarray = array[i];
>             for (int j = 0; j < l2; j++) {
>                 result[i * l2 + j] = subarray[j];
>             }
>         }
>         return result;
>     }
>
>     private class Tester<T extends Archetype> {
>         private Retriever<T> bean;
>         private T o1, o2;
>         private String table;
>         private long ID;
>
>         public Tester(T o1, String table) {
>             this.table = table;
>             this.o1 = o1;
>         }
>
>         public Callable phase01() {
>             return new Callable() {
>                 public Object call() {
>
>                     // CREATE
>                     assertEquals(0, o1.getID());
>                     mgr.persist(o1);
>                     mgr.flush();
>                     ID = o1.getID();
>                     return null;
>                 }
>             };
>         }
>
>         public Callable phase02() {
>             return new Callable() {
>                 public Object call() {
>
>                     // READ & UPDATE
>                     assertEquals(
>                             "Failed size check for table named \""
>                                     + table + "\"",
>                             1,
>                             mgr.sizeOf(table));
>
>                     bean = new Retriever<T>(table, ID);
>                     o2 = bean.fetch();
>                     Assert.assertNotNull(o2);
>                     o2.setName("name of bean");
>                     o2 = null;
>                     return null;
>                 }
>             };
>         }
>
>         public Callable phase03() {
>             return new Callable() {
>                 public Object call() {
>
>                     // VERIFY UPDATE
>                     assertEquals(1, mgr.sizeOf(table));
>                     bean = new Retriever<T>(table, ID);
>                     o2 = bean.fetch();
>                     assertNotNull(o2);
>                     assertEquals("name of bean", o2.getName());
>
>                     // DESTROY
>                     mgr.remove(o2);
>                     mgr.flush();
>                     assertEquals(0, mgr.sizeOf(table));
>                     return null;
>                 }
>             };
>         }
>
>         /**
>          * Retrieve units of work for this CRUD test block
>          * @return Callable[] units of work
>          */
>         public Callable[] getCalls() {
>             return new Callable[]{
>                     phase01(),
>                     phase02(),
>                     phase03()
>             };
>         }
>     }
>


Hope this helps,
--
Alexander R. Saint Croix




On Feb 12, 2008 8:22 AM, Jack Cox <[hidden email]> wrote:

>
> Is there a way in the 'out-of-container' testing paradigm with openejb for
> the unit test method to control the demarcation of transactions?
>
> Here's the use case:
> 1) The unit test is doing entity bean testing, calling various methods on
> the entity facade to test CRUD operations.
> 2) The unit test needs to demarcate transactions so that it can insert,
> delete, then attempt to retrieve deleted entities.  Of course the last
> retrieval should fail because the entity is deleted.
> 3) But, JPA has the interesting behavior that deleted entities continue to
> be accessible within the deleting transaction.
> 4) Therefore, the unit test needs to demarcate a transaction around the
> delete so that the entity is really removed prior to the last retrieval
> test.
>
> So, how can one demarcate transactions within a junit test case?
>
> Thanks,
>
> Jack
>
> --
> View this message in context:
> http://www.nabble.com/Transactions-controlled-by-Unit-Tests-tp15434029p15434029.html
> Sent from the OpenEJB User mailing list archive at Nabble.com.
>
>
Reply | Threaded
Open this post in threaded view
|

Re: Transactions controlled by Unit Tests

Alexander Saint Croix-2
In case it needs stating again, thanks to David Blevins for showing me how
to do this--I'm working on my app again, and making forward progress after
one tough month of test hangups.

--
Alex
Reply | Threaded
Open this post in threaded view
|

Re: Transactions controlled by Unit Tests

Jack Cox
In reply to this post by Jack Cox
I'll break etiquette and reply to my own post.

I was mistaken (i.e. I'm stupid) regarding the delete operation.  The entity does disappear from the JPA context.  

That not withstanding, I still have need to control the transaction demarcation from the junit test case.

Jack

Jack Cox wrote
Is there a way in the 'out-of-container' testing paradigm with openejb for the unit test method to control the demarcation of transactions?  

Here's the use case:
1) The unit test is doing entity bean testing, calling various methods on the entity facade to test CRUD operations.
2) The unit test needs to demarcate transactions so that it can insert, delete, then attempt to retrieve deleted entities.  Of course the last retrieval should fail because the entity is deleted.  
3) But, JPA has the interesting behavior that deleted entities continue to be accessible within the deleting transaction.
4) Therefore, the unit test needs to demarcate a transaction around the delete so that the entity is really removed prior to the last retrieval test.

So, how can one demarcate transactions within a junit test case?

Thanks,

Jack
Reply | Threaded
Open this post in threaded view
|

Re: Transactions controlled by Unit Tests

dblevins
Administrator
Here,  http://cwiki.apache.org/OPENEJBx30/unit-testing-transactions.html

I wrote up a doc for you better describing the pattern I created for  
Alex.  It shows usage for transactions, but the same pattern can be  
used for security as well.

That approach works great and still keeps you free of any OpenEJB-
specific code in your test cases.

If you don't have any issues with your test cases extending an OpenEJB  
specific TestCase implementation then I'm pretty sure we could find a  
way to support @TransactionAttribute and @RunAs on the test methods  
themselves (as well as dependency injection).  If that sounds  
attractive to you, I'll create a JIRA for it.  We'd probably implement  
it as syntactic sugar on the above approach.

-David

On Feb 12, 2008, at 8:39 AM, Jack Cox wrote:

>
> I'll break etiquette and reply to my own post.
>
> I was mistaken (i.e. I'm stupid) regarding the delete operation.  
> The entity
> does disappear from the JPA context.
>
> That not withstanding, I still have need to control the transaction
> demarcation from the junit test case.
>
> Jack
>
>
> Jack Cox wrote:
>>
>> Is there a way in the 'out-of-container' testing paradigm with  
>> openejb for
>> the unit test method to control the demarcation of transactions?
>>
>> Here's the use case:
>> 1) The unit test is doing entity bean testing, calling various  
>> methods on
>> the entity facade to test CRUD operations.
>> 2) The unit test needs to demarcate transactions so that it can  
>> insert,
>> delete, then attempt to retrieve deleted entities.  Of course the  
>> last
>> retrieval should fail because the entity is deleted.
>> 3) But, JPA has the interesting behavior that deleted entities  
>> continue to
>> be accessible within the deleting transaction.
>> 4) Therefore, the unit test needs to demarcate a transaction around  
>> the
>> delete so that the entity is really removed prior to the last  
>> retrieval
>> test.
>>
>> So, how can one demarcate transactions within a junit test case?
>>
>> Thanks,
>>
>> Jack
>>
>>
>
> --
> View this message in context: http://www.nabble.com/Transactions-controlled-by-Unit-Tests-tp15434029p15434676.html
> Sent from the OpenEJB User mailing list archive at Nabble.com.
>
>

Reply | Threaded
Open this post in threaded view
|

Re: Transactions controlled by Unit Tests

Jack Cox
David,

Thanks for the clear example, it now makes sense.  I like the approach since it doesn't require modifying the bean under test.

-Jack

David Blevins wrote
Here,  http://cwiki.apache.org/OPENEJBx30/unit-testing-transactions.html

I wrote up a doc for you better describing the pattern I created for  
Alex.  It shows usage for transactions, but the same pattern can be  
used for security as well.

That approach works great and still keeps you free of any OpenEJB-
specific code in your test cases.

If you don't have any issues with your test cases extending an OpenEJB  
specific TestCase implementation then I'm pretty sure we could find a  
way to support @TransactionAttribute and @RunAs on the test methods  
themselves (as well as dependency injection).  If that sounds  
attractive to you, I'll create a JIRA for it.  We'd probably implement  
it as syntactic sugar on the above approach.

-David

On Feb 12, 2008, at 8:39 AM, Jack Cox wrote:

>
> I'll break etiquette and reply to my own post.
>
> I was mistaken (i.e. I'm stupid) regarding the delete operation.  
> The entity
> does disappear from the JPA context.
>
> That not withstanding, I still have need to control the transaction
> demarcation from the junit test case.
>
> Jack
>
>
> Jack Cox wrote:
>>
>> Is there a way in the 'out-of-container' testing paradigm with  
>> openejb for
>> the unit test method to control the demarcation of transactions?
>>
>> Here's the use case:
>> 1) The unit test is doing entity bean testing, calling various  
>> methods on
>> the entity facade to test CRUD operations.
>> 2) The unit test needs to demarcate transactions so that it can  
>> insert,
>> delete, then attempt to retrieve deleted entities.  Of course the  
>> last
>> retrieval should fail because the entity is deleted.
>> 3) But, JPA has the interesting behavior that deleted entities  
>> continue to
>> be accessible within the deleting transaction.
>> 4) Therefore, the unit test needs to demarcate a transaction around  
>> the
>> delete so that the entity is really removed prior to the last  
>> retrieval
>> test.
>>
>> So, how can one demarcate transactions within a junit test case?
>>
>> Thanks,
>>
>> Jack
>>
>>
>
> --
> View this message in context: http://www.nabble.com/Transactions-controlled-by-Unit-Tests-tp15434029p15434676.html
> Sent from the OpenEJB User mailing list archive at Nabble.com.
>
>