Не бросайтесь тапками: я только учусь Перерываю гайды: много информации о том, КАК сделать, но никак не могу найти, ЗАЧЕМ ЭТО. То есть почему мы не можем подрубить юнит-тест напрямую к коллауту? Что он нам дает? Еще возникла такая интересная фишка: я сейчас тестирую POST, GET, PUT и DELETE коллауты. Прописала мок на токен, если у нас в эндпоинте services/oauth2/token, а также сам объект в стринге, если задан иной эндпоинт:
@isTest
global class CalloutMock implements HttpCalloutMock {
global HTTPResponse respond(HTTPRequest request) {
HttpResponse response = new HttpResponse();
response.setHeader('Content-Type', 'application/json');
String serialized;
if(request.getEndpoint().contains('services/oauth2/token')) {
serialized = '{"webtoken": "fake-token"}';
} else {
serialized = '{"Name":"LooksLikeName"}';
}
response.setBody(serialized);
response.setStatusCode(200);
return response;
}
}
Что теперь происходит. Я использую этот мок во всех методах юнит-теста, и каким-то образом код вообще не приходится менять, только методы разные подтягиваю из основного класса в тест:
@isTest
static void testPostCallout() {
//Это стринга с данными токена (один из методов теста):
String request = accessTokenBody();
TestObj__c testObj = new TestObj__c(Name = 'LooksLikeName');
insert testObj;Test.startTest();
Test.setMock(HttpCalloutMock.class, new CalloutMock());
HttpResponse response = OurClassWithCallouts.postCallout(testObj.Id);
String actualValue = response.getBody();
String expectedValue = '{"Name":"LooksLikeName"}';
System.assertEquals(actualValue, expectedValue);
System.assertEquals(200, response.getStatusCode());
Test.stopTest();
}@isTest
static void testPostCallout() {
String request = accessTokenBody();
TestObj__c testObj = new TestObj__c(Name = 'LooksLikeName');
insert testObj;Test.startTest();
Test.setMock(HttpCalloutMock.class, new CalloutMock());
HttpResponse response = OurClassWithCallouts.deleteCallout(testObj.Id);
String actualValue = response.getBody();
String expectedValue = '{"Name":"LooksLikeName"}';
System.assertEquals(actualValue, expectedValue);
System.assertEquals(200, response.getStatusCode());
Test.stopTest();
}///...и так далее со всеми методами (GET и PUT).
Меня это настораживает. Ну то есть то, что покрытие почти 100%, все правильно ассёртится, а код в самом тесте практически не меняется. Я делаю что-то неправильно? Не могли бы вы объяснить суть моков в связке с обычными юнит-тестами? А то до меня как до жирафа вся эта интеграция :D
Не бросайтесь тапками: я только учусь :) Перерываю гайды: много информации о том, КАК сделать, но никак не могу найти, ЗАЧЕМ ЭТО. То есть почему мы не можем подрубить юнит-тест напрямую к коллауту? Что он нам дает? Еще возникла такая интересная фишка: я сейчас тестирую POST, GET, PUT и DELETE коллауты. Прописала мок на токен, если у нас в эндпоинте [b]services/oauth2/token[/b], а также сам объект в стринге, если задан иной эндпоинт: [code] @isTest global class CalloutMock implements HttpCalloutMock { global HTTPResponse respond(HTTPRequest request) { HttpResponse response = new HttpResponse(); response.setHeader('Content-Type', 'application/json'); String serialized; if(request.getEndpoint().contains('services/oauth2/token')) { serialized = '{"webtoken": "fake-token"}'; } else { serialized = '{"Name":"LooksLikeName"}'; } response.setBody(serialized); response.setStatusCode(200); return response; } } [/code] Что теперь происходит. Я использую этот мок во всех методах юнит-теста, и каким-то образом код вообще не приходится менять, только методы разные подтягиваю из основного класса в тест: [code] @isTest static void testPostCallout() { //Это стринга с данными токена (один из методов теста): String request = accessTokenBody(); TestObj__c testObj = new TestObj__c(Name = 'LooksLikeName'); insert testObj; Test.startTest(); Test.setMock(HttpCalloutMock.class, new CalloutMock()); HttpResponse response = OurClassWithCallouts.postCallout(testObj.Id); String actualValue = response.getBody(); String expectedValue = '{"Name":"LooksLikeName"}'; System.assertEquals(actualValue, expectedValue); System.assertEquals(200, response.getStatusCode()); Test.stopTest(); } @isTest static void testPostCallout() { String request = accessTokenBody(); TestObj__c testObj = new TestObj__c(Name = 'LooksLikeName'); insert testObj; Test.startTest(); Test.setMock(HttpCalloutMock.class, new CalloutMock()); HttpResponse response = OurClassWithCallouts.deleteCallout(testObj.Id); String actualValue = response.getBody(); String expectedValue = '{"Name":"LooksLikeName"}'; System.assertEquals(actualValue, expectedValue); System.assertEquals(200, response.getStatusCode()); Test.stopTest(); } ///...и так далее со всеми методами (GET и PUT). [/code] Меня это настораживает. Ну то есть то, что покрытие почти 100%, все правильно ассёртится, а код в самом тесте практически не меняется. Я делаю что-то неправильно? Не могли бы вы объяснить суть моков в связке с обычными юнит-тестами? А то до меня как до жирафа вся эта интеграция :D
во время теста не происходит настоящего колаута, и чтобы тестируемый код на этой строчке не упал (а также чтобы вернуть в Респонс что полезное, что будет работать ниже по-коду - так как с респонсом обычно что-то делают), для этого и создают этот Мок-респонс. Это то, что заменит настоящий респонс во время теста. Можно сделать веб серсис колаут класс и по-детски, используя Test.isRunningTest() не делать колаута во время теста, а тут же подменять его чем-нибудь, чтобы код дальше прошел и покрылся тестом. Но лучше делать все с HttpCalloutMock классом
во время теста не происходит настоящего колаута, и чтобы тестируемый код на этой строчке не упал (а также чтобы вернуть в Респонс что полезное, что будет работать ниже по-коду - так как с респонсом обычно что-то делают), для этого и создают этот Мок-респонс. Это то, что заменит настоящий респонс во время теста. Можно сделать веб серсис колаут класс и по-детски, используя Test.isRunningTest() не делать колаута во время теста, а тут же подменять его чем-нибудь, чтобы код дальше прошел и покрылся тестом. Но лучше делать все с HttpCalloutMock классом
Спасибо. Поняла. Ну в принципе Мок же может быть универсальным для всех методов, как в примере? Тест отрабатывает в принципе, и код покрывает.
[quote="Den Brown"]во время теста не происходит настоящего колаута, и чтобы тестируемый код на этой строчке не упал (а также чтобы вернуть в Респонс что полезное, что будет работать ниже по-коду - так как с респонсом обычно что-то делают), для этого и создают этот Мок-респонс. Это то, что заменит настоящий респонс во время теста. Можно сделать веб серсис колаут класс и по-детски, используя Test.isRunningTest() не делать колаута во время теста, а тут же подменять его чем-нибудь, чтобы код дальше прошел и покрылся тестом. Но лучше делать все с HttpCalloutMock классом[/quote] Спасибо. Поняла. Ну в принципе Мок же может быть универсальным для всех методов, как в примере? Тест отрабатывает в принципе, и код покрывает.
При граммотном написании callout, mock можно и не делать.
При граммотном написании callout, mock можно и не делать.
Это как? Я знаю, что есть возможность тестировать Моками и Статик Ресурсами. Пытаюсь сейчас разобраться с Testing multiple HTTP callouts, но вообще, в интеграции путаюсь пока что.
[quote="wilder"]При граммотном написании callout, mock можно и не делать.[/quote] Это как? Я знаю, что есть возможность тестировать Моками и Статик Ресурсами. Пытаюсь сейчас разобраться с Testing multiple HTTP callouts, но вообще, в интеграции путаюсь пока что.
Если сделать все каллауты из одного места. То в этом месте можно сделать проверку на текущий контекст. И если понятно что мы в тесте то из статик ресурса или из другого места подгрузать ответ соответствующий запросу
Если сделать все каллауты из одного места. То в этом месте можно сделать проверку на текущий контекст. И если понятно что мы в тесте то из статик ресурса или из другого места подгрузать ответ соответствующий запросу
Вот wilder про это хочет сказать
HttpRequest req = new HttpRequest();
HttpResponse res = new HttpResponse();
Http http = new Http();
req.setEndpoint('some_url');
req.setMethod('GET');
if (!test.isRunningTest()) {
res = http.send(req);
} else {
res = new HttpResponse();
res.setHeader('Content-Type', 'application/json');
res.setBody('???');
res.setStatusCode(200);
}
Вот wilder про это хочет сказать [code] HttpRequest req = new HttpRequest(); HttpResponse res = new HttpResponse(); Http http = new Http(); req.setEndpoint('some_url'); req.setMethod('GET'); if (!test.isRunningTest()) { res = http.send(req); } else { res = new HttpResponse(); res.setHeader('Content-Type', 'application/json'); res.setBody('???'); res.setStatusCode(200); } [/code]
Конечно правильнее использовать Mock
но не всегда Mock работают и приходится этот вариант использовать.
Вот к примеру интересный случай для Mock.
Как протестировать метод который выполняет не один а несколько callouts.
Можно конечно разбить на несколько методов и тестить каждый отдельно. Но вот если нельзя, то что?
Конечно правильнее использовать Mock но не всегда Mock работают и приходится этот вариант использовать. Вот к примеру интересный случай для Mock. Как протестировать метод который выполняет не один а несколько callouts. Можно конечно разбить на несколько методов и тестить каждый отдельно. Но вот если нельзя, то что?
Stub фреймворк решает все проблемы)
Stub фреймворк решает все проблемы)
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_stub_api.htm
Фигасе. Я даже не слышал про такое 0_о
Кто использует в реальных проектах? Какие еще преимущества он дает?
[quote="Gres"]Stub фреймворк[/quote] https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_testing_stub_api.htm Фигасе. Я даже не слышал про такое 0_о Кто использует в реальных проектах? Какие еще преимущества он дает?
Я использую уже давно, написал над ним свое удобное API, очень нравится мапить результат выполнения метода на какой-то стаб.
[quote="Dmitry Shnyrev"]Кто использует в реальных проектах? Какие еще преимущества он дает? [/quote] Я использую уже давно, написал над ним свое удобное API, очень нравится мапить результат выполнения метода на какой-то стаб.
Угу. Так точно написан свой фреймворк для стабов. И он значительно проще чем по ссылке.
А стабы нужны для симуляции еще не готовых вебсервисов. Очень удобно через сеттингс включать/выключать.
Угу. Так точно написан свой фреймворк для стабов. И он значительно проще чем по ссылке. А стабы нужны для симуляции еще не готовых вебсервисов. Очень удобно через сеттингс включать/выключать.
Возвращать разный мок для разных эндпоинтов: https://salesforce.stackexchange.com/questions/139235/how-to-create-mock-class-for-multiple-callouts-in-single-class
[quote="Dmitry Shnyrev"] Как протестировать метод который выполняет не один а несколько callouts. [/quote] Возвращать разный мок для разных эндпоинтов: https://salesforce.stackexchange.com/questions/139235/how-to-create-mock-class-for-multiple-callouts-in-single-class
О блин! Оказывается все так просто.
Я даже чет и не додумался проверять endpoint запроса чтобы организовать универсальный Mock
private class Mock implements HttpCalloutMock {
public HTTPResponse respond(HTTPRequest req) {
if (req.getEndpoint().endsWith('abc')) {
HTTPResponse res = new HTTPResponse();
res.setBody('{}');
res.setStatusCode(200);
return res;
} else if (req.getEndpoint().endsWith('xyz')) {
...
} else {
System.assert(false, 'unexpected endpoint ' + req.getEndpoint());
}
}
}
О блин! Оказывается все так просто. Я даже чет и не додумался проверять endpoint запроса чтобы организовать универсальный Mock [code] private class Mock implements HttpCalloutMock { public HTTPResponse respond(HTTPRequest req) { if (req.getEndpoint().endsWith('abc')) { HTTPResponse res = new HTTPResponse(); res.setBody('{}'); res.setStatusCode(200); return res; } else if (req.getEndpoint().endsWith('xyz')) { ... } else { System.assert(false, 'unexpected endpoint ' + req.getEndpoint()); } } } [/code]