You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Quick introduction: My name is Cristian and I'm a passionate developer. I try to keep up with technology, programming languages that I use and best practices. I've been developing using Apex for a few years now and have known about fflib for 2-3 yrs.
Since I really started looking into fflib and SoC I've been planning on having our codebase at work, refactored to AEP. Now the time has come and we are actually in the process of doing so.
As it was expected, I had doubts and questions and stumbled upon a thing or two. But by reading everything that I could find related to the implementation of fflib, all the discussions here, as well as sample code, I think I managed to get a fair understanding of it and how to utilize it.
What pushed me to want to use it is the nice separation of concerns, the cleaner and decoupled code that you can write with it and of course, the big benefit of writting tests without having to interact with the database (uhh what a relief 😉 ). At the moment we have thousands of test methods that take about 2 hours to validate each time we try to deploy. From what I hear that's not the longest time but I suppose nor the smallest one and I for one, would prefer faster validations and deployments. We have about a year since we've been using Github actions to automate our deployments and while we are a lot faster and consistent than in the past, the pestering ~2 hrs of validation time is killing me.
Back to the subject of the discussion, while the domain layer testing has been quite easy to implement and the Service Layer testing will do well with mocking, the Selector Layer for me was a given: I need to insert data in order to test the selector methods. That thought, confirmed by the test classes I've seen in the sample code: ProductsSelectorTest.cls, which also insert records, almost convinced me that there's no other way and I would have to deal with that 😞.
However, thinking about it for longer than a few days, it kept coming to me: "Well, if testing a selector means I need to insert data so that it is actually queried back to me, in my 👀 that means I'm actually also testing the functionality of the Database query engine, which I would rather trust that it's working correctly otherwise I would really question our usage of Salesforce."
So, without this part, what else is there to possibly be tested for selector methods? Then I realized it is quite obvious: the actual QUERY. (sorry guys, maybe for all of you this was a given but for me it was quite a moment).
Since we established that we need to trust that the query engine will correctly return the requested data, then the only thing to test is that we pass it the correct query. Hence for me, that's the only thing that I would like to test in my Selector unit tests.
Once I established that, I looked for ways to do so and I got sad... my "brilliant" idea was not really easy to achieve. At least not in a clean and elegant way. So, I did what I do all the time: research and research. I look for other articles where people have discussed about that and I found one that had code that looked like this:
@IsTest( isParallel=true )
private with sharing classSEL_CampaignMembers_TEST{
@IsTestprivatestaticvoidselectActiveByTypeAndUsers(){
SEL_CampaignMembers.newInstance().selectActiveByTypeAndUsers( null, newSet<Id>() );
fflib_QueryFactoryresult=fflib_QueryFactory.lastQueryFactory;
// Validate selected fieldsSet<String> selectedFields=result.getSelectedFields();
System.assert( selectedFields.contains( 'CampaignId' ), 'Expected standard field CampaignId from getSObjectFieldList() to be part of query' );
System.assert( selectedFields.contains( 'Campaign.Description' ), 'Expected relationship field Campaign.Description from getRelatedFieldSet() to be part of query' );
// Validate conditionsStringcondition=result.getCondition();
System.assert( condition.contains( 'Campaign.Type = :type' ), 'Expected Brand condition on parent Campaign record' );
System.assert( condition.contains( 'Campaign.IsActive = true' ), 'Expected parent campaign to be required active' );
System.assert( condition.contains( 'ContactId IN ( SELECT ContactId FROM User WHERE Id IN :userIds )' ), 'Expected a subselect condition to allow getting CMs by UserId' );
}
}
For a moment I was in the Eureka moment...until I checked the fflib_QueryFactory class only to realize, there's no such thing as lastQueryFactory property or method to return that. However, that put me on the path to think about it and realize that it would be quite a great addition to the class, to have a method that could return the last queryFactory that was generated so we can analyze it and test for its contents.
In order to "confirm" my theory, I added a private property and a public getLastQuery method to my own selector class and wrote the test to see if everything is working. And it did! Second step was to take these ones and add them to the fflib_SObjectSelector.cls where I also had to modify the call to the newQueryFactory(). I removed them from my own Selector class and run the tests again. It still passed. So, to me at least at this point this is a perfectly valid option to write unit tests for the selector class without having to interact with the database, same as all the other layers.
In the end, my questions to the maintainers of the library:
Was this something that was considered in the past but never implemented? If yes, may I know the explanation for that?
If it wasn't considered in the past, would you consider adding it to the class for this purpose?
Am I wrong in my assumption that it would help writting database free unit testing for the selectors?
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
-
Hi everyone!
Quick introduction: My name is Cristian and I'm a passionate developer. I try to keep up with technology, programming languages that I use and best practices. I've been developing using Apex for a few years now and have known about
fflib
for 2-3 yrs.Since I really started looking into fflib and SoC I've been planning on having our codebase at work, refactored to AEP. Now the time has come and we are actually in the process of doing so.
As it was expected, I had doubts and questions and stumbled upon a thing or two. But by reading everything that I could find related to the implementation of fflib, all the discussions here, as well as sample code, I think I managed to get a fair understanding of it and how to utilize it.
What pushed me to want to use it is the nice separation of concerns, the cleaner and decoupled code that you can write with it and of course, the big benefit of writting tests without having to interact with the database (uhh what a relief 😉 ). At the moment we have thousands of test methods that take about 2 hours to validate each time we try to deploy. From what I hear that's not the longest time but I suppose nor the smallest one and I for one, would prefer faster validations and deployments. We have about a year since we've been using Github actions to automate our deployments and while we are a lot faster and consistent than in the past, the pestering ~2 hrs of validation time is killing me.
Back to the subject of the discussion, while the domain layer testing has been quite easy to implement and the Service Layer testing will do well with mocking, the Selector Layer for me was a given: I need to insert data in order to test the selector methods. That thought, confirmed by the test classes I've seen in the sample code: ProductsSelectorTest.cls, which also insert records, almost convinced me that there's no other way and I would have to deal with that 😞.
However, thinking about it for longer than a few days, it kept coming to me: "Well, if testing a selector means I need to insert data so that it is actually queried back to me, in my 👀 that means I'm actually also testing the functionality of the Database query engine, which I would rather trust that it's working correctly otherwise I would really question our usage of Salesforce."
So, without this part, what else is there to possibly be tested for selector methods? Then I realized it is quite obvious: the actual QUERY. (sorry guys, maybe for all of you this was a given but for me it was quite a moment).
Since we established that we need to trust that the query engine will correctly return the requested data, then the only thing to test is that we pass it the correct query. Hence for me, that's the only thing that I would like to test in my Selector unit tests.
Once I established that, I looked for ways to do so and I got sad... my "brilliant" idea was not really easy to achieve. At least not in a clean and elegant way. So, I did what I do all the time: research and research. I look for other articles where people have discussed about that and I found one that had code that looked like this:
For a moment I was in the Eureka moment...until I checked the
fflib_QueryFactory
class only to realize, there's no such thing aslastQueryFactory
property or method to return that. However, that put me on the path to think about it and realize that it would be quite a great addition to the class, to have a method that could return the last queryFactory that was generated so we can analyze it and test for its contents.In order to "confirm" my theory, I added a private property and a public
getLastQuery
method to my own selector class and wrote the test to see if everything is working. And it did! Second step was to take these ones and add them to the fflib_SObjectSelector.cls where I also had to modify the call to the newQueryFactory(). I removed them from my own Selector class and run the tests again. It still passed. So, to me at least at this point this is a perfectly valid option to write unit tests for the selector class without having to interact with the database, same as all the other layers.In the end, my questions to the maintainers of the library:
Beta Was this translation helpful? Give feedback.
All reactions