When can I use (SeeAllData=true)?

205    Asked by Aashishchaursiya in Salesforce , Asked on Apr 18, 2023

At this point I think we're painfully aware that using SeeAllData=true is one of the 7 Deadly Sins of Salesforce (assuming there's just 7). I know, for example, why we shouldn't use it:

  • Can slow tests down massively if you're accessing a lot of data

  • Querying for a specific record today might be gone tomorrow

  • Could cause tests to pass in sandboxes but fail in production

  • Etc...

I feel at this point I'm regurgitating the same old response to SeeAllData=true:

Using SeeAllData is almost always unnecessary except in the rarest of circumstances.

I realised though I don't actually know when it's useful to use it. So when might it be appropriate to use it? I've never come across a situation yet personally and things like Profiles persist in test classes.

The only reason I think where I could be useful is that we have an object called Source__c which stores a bunch of records there. The name might be Website, Mobile App, CompanyWe'reAPartnerWith1. It's basically interacted with minimally (this was made before the introduction of Custom Metadata Types), but even then I'd just do something like:

Source__c src = new Source__c( Name = 'Website' ); INSERT src;

I read that querying data is more efficient than performing DML in this way (hence @testSetup), so maybe I'm doing that "wrong"?

In most cases, you can work around the SeeAllData true limitation by using the Force.com Enterprise Apex Selector pattern and ApexMocks.

here's an example for Dashboard

Your MyClass.doSomething() method uses code such as this:
.... Dashboard[] dashboards = DashBoardsSelector.newInstance() .selectByDeveloperName(new Set {'Foo'});

The selector follows the Enterprise pattern and looks like this (excerpted):

public virtual class DashboardsSelector implements IDashboardsSelector{ public List getSObjectFieldList(){ return new List { Dashboard.Description, Dashboard.DeveloperName, ... }; } /* Factory to provide the caller with a new Selector. Enables selectors to be mocked */ public static IDashboardsSelector newInstance() { return (IDashboardsSelector)Application.Selector.newInstance(Dashboard.SObjectType); } /* getSObjectType : Used to construct queries. Required. */ public Schema.SObjectType getSObjectType() {return Dashboard.SObjectType;} /* selectById : default selector, returns all matching SObjects for fields defined by getSObjectFieldList */ public virtual List selectById(Set ids) { return ids.isEmpty() ? new List () : (Dashboard[]) selectSObjectsById(ids); } /* selectByDeveloperName : fetches dashboard for a devname set */ public virtual List selectByDeveloperName(Set devNames) { if (devNames.isEmpty()) return new List (); fflib_QueryFactory dbQF = newQueryFactory() .setCondition('DeveloperName IN :grinning:evNames'); return Database.query(dbQF.toSOQL()); } }

The interface for factory and mocking is:

public interface IDashboardsSelector { List selectByDeveloperName(Set devNames); }
and your Application class has an entry for the selector (not shown here)
Now, your testmethods don't need to use SeeAllData=true, they merely need to mock the selector and the selector's method's return:

fflib_ApexMocks mocks = new fflib_ApexMocks(); // Given mockDashboards (using Json serialize/deserialize technique - here with Sobject Fabricator github lib) Dashboard[] mockDashboards = new List (); mockDashboards.add((Dashboard) new sfab_FabricatedSObject(Dashboard.class) .setField(Dashboard.Id,fflib_IdGenerator.generate(Dashboard.SObjectType)) .setField(Dashboard.DeveloperName,'FooDash') .toSObject()); // Given a mockSelector DashboardsSelector mockDashboardsSelector = (DashboardsSelector) mocks.mock(DashboardsSelector.class); mocks.startStubbing(); mocks.when(mockDashboardsSelector.SObjectType()).thenReturn(Dashboard.SObjectType); for (Integer i = 0; i < mockDashboards> {mockDashboards[i].DeveloperName})) .thenReturn(new List{mockDashBoards[i]}); } mocks.stopStubbing(); Application.Selector.setMock(mockDashboardsSelector); // when codeUnderTest method invoked new MyClass.doSomething(...); // then verify whatever results you care about

Now this pattern is super useful and allows you to create mock results from SOQl queries against SObjects you can't otherwise insert in testmethods. The ApexMocks library gives you flexibility with selector, domain, service, and unit of work layers and can avoid creating DML actual sobjects in most of your unit tests.

But what about testing the actual selector class itself? How do you know that the method selectByDeveloperName actually does the right thing?

Here, seeAllData=true is required but you have localised it to just the selector class itself and the rest of your unit tests need never depend on org data

  private class DashboardsSelectorTest { @IsTest(SeeAllData=true) static void testSelectors() { Dashboard[] orgDashboards = [SELECT ID, DeveloperName From Dashboard Limit 1]; if (orgDashboards.isEmpty()) {return;} // can't test, no org dashboards Dashboard[] dbs = DashboardsSelector.newInstance() .selectByDeveloperName(new Set {orgDashboards[0].DeveloperName}); System.assertEquals(1,dbs.size(),'should find the real org Dashboard'); } }


Your Answer

Interviews

Parent Categories