apex-enterprise-patterns / fflib-apex-common

Common Apex Library supporting Apex Enterprise Patterns and much more!
BSD 3-Clause "New" or "Revised" License
907 stars 515 forks source link

testing 'without DML' #139

Closed marktroupe closed 7 years ago

marktroupe commented 7 years ago

I don't know if this is a legacy issue that's since been overcome, but I'm working on an org that's partially implementing fflib and noticed an issue when doing 'without DML' tests. I am relatively new to fflib.

eg in this example from the df12 repo:

@IsTest
    private static void testInsertValidationSuccessWithoutDML()
    {
        Opportunity opp = new Opportunity ( Name = 'Test' );
        SObjectDomain.Test.Database.onInsert(new Opportunity[] { opp } );       
        SObjectDomain.triggerHandler(Opportunities.class);
        System.assertEquals(SObjectDomain.Errors.getAll().size(), 0);       
    }

We have implemented some test classes this way, but if we stick with the above example, we are noticing an error would occur when the Opportunities domain class contains any After Update or After Insert actions which use their own Unit of Work. Let's say, for example it updates the Opportunitiy's parent Account in the After Update, which in turn triggers the Accounts domain class and an update on Account. Because the 'state' has already been set as 'Insert' and the sObject we are inserting is an 'Opportunity', the Account domain class will run in this state, essentially passing the 'opp' record into the Account domain class and doing an insert action, which results in a runtime cast error.

Appreciate if I can get any help on this issue.

thanks Mark

afawcett commented 7 years ago

Yeah so i think this is a bug / limitation in the early mocking framework for domain classes. Basically the SObjectDomain.Test facility mocks an immediate call to SObjectDomain.triggerHandler only (avoiding an immediate DML to fire the trigger). It seems you have further invocation of DML and thus later a nested Account domain class call.

We have two options here i guess..

Strategically the second option is the one to go with, but i also appreciate you don't have all of fflib or likely for that matter ApexMocks present. There is also the fact that ApexMocks has quite a steep learning and adoption curve. Once Spring'17 goes live the Apex Stub API will be GA and one of the biggest inhibitors to ApexMocks around using the ApexMocks Mock code generator will go away, so the barry to entry to mocking will be much lower. That said, if you just want to mock the UOW, you may not actually need to generate any code. Have a read through the blog above and let me know your thoughts, happy to help with pointers to help you migrate your test to ApexMocks if you want to share the method.

marktroupe commented 7 years ago

thanks for the reply and you confirmed my suspicions. Yep ApexMocks is a bit out of reach for us at this stage, but in the back of our minds.