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
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'); } }