Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/mocking c #5

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
improve documentation
  • Loading branch information
janeksledziewski committed Oct 15, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 85685d237dc60f930b614a0998b37da173502847
2 changes: 1 addition & 1 deletion website/docs/api/sosl.md
Original file line number Diff line number Diff line change
@@ -594,7 +594,7 @@ Query needs unique id that allows for mocking.
**Signature**

```apex
SOQL mockId(String queryIdentifier)
SOSL mockId(String queryIdentifier)
```

**Example**
13 changes: 10 additions & 3 deletions website/docs/docs/basic-features.md
Original file line number Diff line number Diff line change
@@ -92,8 +92,11 @@ SOSL.of(Account.SObjectType)

## Mocking

Mocking provides a way to substitute records from a Database search with some prepared data. Data can be prepared in form of list of `List<SObject>`, in this format you can set multiple SObject types that will be passed to SOSL class.
Mocked queries won't make any SOSL's and simply return data set in method definition, mock __will ignore all filters and relations__, what is returned depends __solely on data provided to the method__. Mocking is working __only during test execution__. To mock SOSL query, use `.mockId(id)` method to make it identifiable. If you mark more than one query with the same ID, all marked queries will return the same data. Currently you can mock only `search` (`.toSearchList`) queries. Because `.find` (`.toSearchResult`) returns `Search.SearchResult` object which cannot be constructed in apex.
Mocking provides a way to substitute records from a Database search with some prepared data. Data can be prepared in form of list of lists (similar to SOSL return) `List<List<SObject>>`, in this format you can set multiple SObject types that will be passed to SOSL class. Or you can provide fixed ids, as to out of the box SOSL in form of `List<Id>`.

In case of preset results SOSL won't make any SOSL's queries and simply return data set in method definition, mock __will ignore all filters and relations__, what is returned depends __solely on data provided to the method__. Mocking is working __only during test execution__. If fixed Ids are provided to SOSL class it will use standard `Test.setFixedSearchResults` method, to make sure expected results are returned.

To mock SOSL query, use `.mockId(id)` method to make it identifiable. If you mark more than one query with the same ID, all marked queries will return the same data.

```apex
public with sharing class ExampleController {
@@ -111,7 +114,11 @@ public with sharing class ExampleController {
}
```

Then in test simply pass data you want to get from Selector to `SOSL.setMock(id, data)` method. Acceptable format is: `List<List<SObject>>`. Then during execution Selector will return desired data.
If you want to use `search` and don't want to insert any records to database, you can simply set mock data with `SOSL.setMock(String mockId, List<List<SObject>> data)` method.

If you need to use `find` or it doesn't matter if records are in database, you can set fixed results by providing records ids with `SOSL.setMock(String mockId, List<Id> ids)` method.

Then during execution Selector will return desired data.

### List of records

110 changes: 99 additions & 11 deletions website/docs/examples/mocking.md
Original file line number Diff line number Diff line change
@@ -4,18 +4,15 @@ sidebar_position: 13

# MOCKING

Mock SOSL results in Unit Tests.
Currently you can mock only `search` (`.toSearchList`) queries. Because `.find` (`.toSearchResult`) returns `Search.SearchResult` object which cannot be constructed in apex.

You need to mock external objects.
Mock SOSL results in Unit Tests. External objects require mocking.

> In Apex tests, use dynamic SOSL to query external objects. Tests that perform static SOSL queries of external objects fail. ~ Salesforce

## Mock Search results

Firstly, set mocking ID in Query declaration.
## Mock without Database insert
Mocking without inserting records to database is only posible using `search` (`.toSearchList`) method. This is caused by return type for `find` which is returning `Search.SearchResults` which cannot be constructed.

```apex
1. Set Mock Id in Query declaration
```java
public with sharing class ExampleController {

public static List<Account> searchAccountsByName(String accountName) {
@@ -31,9 +28,20 @@ public with sharing class ExampleController {
}
```

Pass list of `List<Sobject>` to SOSL class, and use mock Id to target query to be mocked.
1. Pass list of `List<Sobject>` to SOSL class, and using `setMock(String mockId, List<List<SObject>> records)` method. Set `mockId` to target query to be mocked.
```java
List<SObject> testAccounts = new List<Account>{ new Account(Name = TEST_ACCOUNT_NAME) };
SOSL.setMock('MockingExample', new List<List<SObject>>{ testAccounts });
```


3. During execution Selector will return record that was set by `.setMock` method.
```java
Assert.areEqual(TEST_ACCOUNT_NAME, result.get(0).Name);
```

```apex
4. Full test method:
```java
@IsTest
public class ExampleControllerTest {
private static final String TEST_ACCOUNT_NAME = 'MyAccount 1';
@@ -52,4 +60,84 @@ public class ExampleControllerTest {
}
```

During execution Selector will return record that was set by `.setMock` method.
## Mock with database insert
You can mock both `search` and `find` after inserting records into database.

1. Set Mock Id in Query declaration
```java
public with sharing class ExampleController {

public static List<Account> searchAccountsByName(String accountName) {
return SOSL.find(accountName)
.inAllFields()
.returning(
SOSL.returning(Account.SObjectType)
)
.mockId('MockingExample')
.toSearchList()
.get(0);
}
}
```

2. Inserts records to database and pass `List<Id>` to SOSL class using `setMock(String mockId, List<Id>) records` method. Remember to specify `mockId` to target selected query.
```java
List<Account> testAccounts = new List<Account>{ new Account(Name = SEARCH_TEXT) };
insert testAccounts;

SOSL.setMock('MockingExample', new List<Id>{ testAccounts.get(0).Id });
```

3. During execution Selector will return record that was set by `.setMock` method. It will use standard `Test.setFixedSearchResults` method before return statement.

SOSL Class:
```java
if (mock.hasFixedMock(mockId)) {
Test.setFixedSearchResults(mock.getFixedMock(mockId));
}
```

Return in test:
```java
List<List<SObject>> results = SOSL.find(SEARCH_TEXT)
.inAllFields()
.returning(SOSL.returning(Account.SObjectType).with(Account.Name))
.mockId('MockingExample')
.preview()
.toSearchList();

List<Account> fixedAccounts = [SELECT Name FROM Account];
Assert.isFalse(results.isEmpty(), 'Should return results for at least one SObject');
Assert.isFalse(results.get(0).isEmpty(), 'Accounts list shouldnt be empty');
Assert.isNotNull(results.get(0).get(0), 'Account should be returned');
Assert.areEqual(((Account) fixedAccounts.get(0)).Name, ((Account) results.get(0).get(0)).Name, 'Accounts name should be equal');
```

4. Full test method:
```java
@IsTest
static void mockFixedIds() {
// Test
List<Account> testAccounts = new List<Account>{ new Account(Name = SEARCH_TEXT) };
insert testAccounts;

SOSL.setMock('MockingExample', new List<Id>{ testAccounts.get(0).Id });

List<List<SObject>> results;
Test.startTest();
results = SOSL.find(SEARCH_TEXT)
.inAllFields()
.returning(SOSL.returning(Account.SObjectType).with(Account.Name))
.mockId('MockingExample')
.preview()
.toSearchList();
Test.stopTest();

// Verify
List<Account> fixedAccounts = [SELECT Name FROM Account];
Assert.isFalse(results.isEmpty(), 'Should return results for at least one SObject');
Assert.isFalse(results.get(0).isEmpty(), 'Accounts list shouldnt be empty');
Assert.isNotNull(results.get(0).get(0), 'Account should be returned');
Assert.areEqual(((Account) fixedAccounts.get(0)).Name, ((Account) results.get(0).get(0)).Name, 'Accounts name should be equal');
}
```