У меня есть класс с методом вызова @future, и этот метод будущего планирует экземпляр другого класса, который также делает вызов. По сути, если первая выноска прошла успешно, позже планируется еще одна выноска. Все это запускается простым обновлением поля в объекте Self_Serve_Trial_Request__c.

При написании тестов я был приятно удивлен, увидев, что обновление поля запускает не только первоначальный вызов будущего, но и последующий запланированный вызов. Я ясно видел это в журнале отладки и предположил, что они оба запускались синхронно. Здорово.

Однако затем я понял, что любой код после Test.stopTest() НЕ видел изменений, созданных обоими выносками, а вместо этого видел только изменения, созданные первым. По сути, Test.stopTest() НЕ делает обе выноски синхронно, она делает синхронно только первую. Таким образом, я могу получить доступ к контексту только после первой выноски, и у меня нет возможности получить доступ к контексту после второй выноски.

Как тогда можно написать полный сквозной тест и получить доступ к контексту, существующему после обоих вызовов?

 Test.startTest();

     Test.setMock(HttpCalloutMock.class, new SelfServeTrialMocks.CreateMachineMock());

     Self_Serve_Trial_Request__c SSTR = [Select ID FROM Self_Serve_Trial_Request__c LIMIT 1];                                           
            SSTR.Decision__c = 'Approved';
     update SSTR;

 Test.stopTest();

 //>>>>> the initial future method runs here, so I can query the record to see those changes... 

     Self_Serve_Trial_Request__c SSTR = [SELECT Environment_ID__c
                                         FROM Self_Serve_Trial_Request__c WHERE ID = :SSTRs.ID];
     system.assert(whatever = whatever);

 //>>>>>>BUT NOW my scheduled class runs here, after any other code that I write. I can see it working clearly in the debug logs, but cannot access it. Is there no way to do so?
1
Sebastian Kessel 5 Июн 2020 в 00:34
Возможно, вы получаете незафиксированную работу, ожидающую исключения, когда выполняете DML между вызовами?
 – 
Raul
5 Июн 2020 в 00:37
Нет, не исключение, я вижу, что обе выноски успешно выполняются в журналах.
 – 
number41
5 Июн 2020 в 01:00

1 ответ

Лучший ответ

Это классическая проблема при тестировании многоуровневого асинхронного Apex. Хитрость заключается в том, что Test.startTest() и Test.stopTest() вызывают синхронное выполнение только первого уровня — в данном случае будущего метода — асинхронного кода.

Любая асинхронная функциональность, поставленная в очередь этим уровнем, в конечном итоге будет выполняться и фактически может быть видна в ваших журналах отладки, но не будет выполняться синхронно, чтобы ваш модульный тест мог подтвердить свои результаты.

Как тогда можно написать полный сквозной тест и получить доступ к контексту, существующему после обоих вызовов?

Вы действительно не можете.

Чтобы эффективно протестировать многоуровневый асинхронный код, вы должны разложить его на части и протестировать каждую единицу по отдельности и/или применить внедрение зависимостей для оценки взаимодействия между ними. Здесь вы должны написать один модульный тест для будущего метода и можете написать запросы к CronTrigger, чтобы проверить, что он поставил запланированный класс в очередь, как и ожидалось.

Затем напишите дополнительные модульные тесты для функциональности запланированного класса и в этих модульных тестах создайте правильные условия для его запуска, вставив данные напрямую (не вызывайте метод будущего) перед явной постановкой запланированного в очередь.

4
David Reed 5 Июн 2020 в 01:05
Спасибо, забавно, что это не объясняется ни в одной документации, которую я видел. В связи с этим я подумал о запросе CronTrigger и обнаружил кое-что еще — когда вы запрашиваете объект CronTrigger в тестовом классе, он возвращает все фактические запланированные задания в вашей организации! Не только те, которые вы создали в своем тесте! Это на самом деле хуже, чем проблема с асинхронностью — с какой стати Salesforce делает все остальное специфичным для тестов в тестовых классах, но возвращает фактические данные для заданий Cron? Это делает почти невозможным точное определение того, какие задания вы запрашиваете во время выполнения.
 – 
number41
5 Июн 2020 в 08:01