задний план
Чтобы уменьшить давление регрессии тестов и улучшить качество разработки проекта, наши команды разработчиков и тестировщиков сотрудничают для выполнения автоматизированного тестирования в некоторых проектах.
Чтобы лучше понять нашу потребность в моках, давайте сначала посмотрим на объяснение автоматического тестирования в вики.
В тестировании программного обеспечения автоматизированное тестирование относится к процессу использования другого программного обеспечения, независимого от тестируемого программного обеспечения, для автоматического выполнения тестов, сравнения фактических результатов с ожиданиями и создания отчетов о тестировании.
Наши автоматические тесты выполняются в соответствии с нашим предустановленным процессом, и мы не хотим, чтобы на нас влияли сторонние сервисы (вверх-вниз, интерфейс возвращает неверные данные), поэтому при вызове стороннего интерфейса мы будем использовать mock и вернуть то, что мы ожидаем данные.
В автоматизированном тесте мы издевались над тремя интерфейсами http, dubbo и mq message, В этой статье объясняется наше решение для имитации интерфейса dubbo.
В настоящее время Dubbo предлагает решения
Во-первых, давайте взглянем на фиктивные функции, предоставляемые самим фреймворком dubbo.
Основной код функции dubbo mock выглядит следующим образом.
//from MockClusterInvoker
public Result invoke(Invocation invocation) throws RpcException {
Result result = null;
//获取方法级别mock配置
String value = directory.getUrl().getMethodParameter(invocation.getMethodName(), Constants.MOCK_KEY, Boolean.FALSE.toString()).trim();
//没有配置 或者 =false
if (value.length() == 0 || value.equalsIgnoreCase("false")) {
//no mock
result = this.invoker.invoke(invocation);
} else if (value.startsWith("force")) {
// force 开头 强制进行mock
if (logger.isWarnEnabled()) {
logger.warn("force-mock: " + invocation.getMethodName() + " force-mock enabled , url : " + directory.getUrl());
}
//force:direct mock
result = doMockInvoke(invocation, null);
} else {
//不是force的话 是失败了再进行mock
//fail-mock
try {
result = this.invoker.invoke(invocation);
} catch (RpcException e) {
//如果是业务异常不进行mock
if (e.isBiz()) {
throw e;
}
if (logger.isWarnEnabled()) {
logger.warn("fail-mock: " + invocation.getMethodName() + " fail-mock enabled , url : " + directory.getUrl(), e);
}
result = doMockInvoke(invocation, e);
}
}
return result;
}
В соответствии с разницей соответствующего значения key=mock в URL-адресе существует три вида логики соответственно.
- value = null не издевайтесь
- value = force xxx принудительно использовать фиктивную логику
- value = xxx пройти фиктивную логику после того, как служба не будет вызвана
Глядя на третью логику, чувствуете ли вы, что это на самом деле даунгрейд, отказ от даунгрейда, а вторая логика называется принудительным даунгрейдом.
Официальная документация, предоставленная dubbo, также определяет этот макет как даунгрейд.
Если вы хотите узнать, как настроить выражения Dubbo Mock, вы можете увидетьАнализ исходного кода Dubbo с пониженной версией Mock
Соответствует ли это нашим потребностям
Наши потребности
- Независимо от того, находится ли третья сторона в сети или нет, это не влияет на наши макеты.
- Гибкая и простая конфигурация
После тестирования, после установки check=false и настройки mock=force:return null для интерфейса, если провайдер не в сети, будет выброшено исключение no provider, что не соответствует требованию 1.
Метод тестирования: добавьте следующую конфигурацию в официальную демонстрацию dubbo.
//from DemoServiceComponent
@Reference(mock = "force:return null",check = false)
private DemoService demoService;
Для требования 2 также существуют следующие проблемы
- Мы не можем переместить конфигурацию dubbo в исходный проект, поэтому мы можем вызвать принудительный макет, только добавив конфигурацию переопределения в реестр dubbo, что неудобно для использования.
- Из пункта 1 также видно, что фиктивная функция зависит от реестра.Наша фиктивная среда и тестовая среда используют один и тот же реестр, что невозможно.
- Документ фиктивного значения недостаточно детализирован, а построение трудоемко для возврата сложных типов.
Таким образом, вывод состоит в том, что ни реализация, ни использование не могут удовлетворить наши потребности.Функция mock Dubbo по-прежнему ориентирована на требования понижения уровня производства.Нам необходимо разработать mock-решение, которое нам будет удобно использовать.
Решения по расширению, разработанные нами
Конструктивные точки макетной схемы, которую мы разработали для фреймворка dubbo, следующие:
- То же использование класса-оболочки точки расширения Cluster для имплантации фиктивной логики, чтобы гарантировать, что автономный сервис не повлияет на наш автоматический запуск теста.
- Используйте файл конфигурации свойств для управления фиктивным переключателем интерфейса, конфигурация может быть размещена в apollo, без вторжения кода.
- Перенаправьте запрос на наш сервер EsayMock и настройте данные Json типа возврата интерфейса.
Могу ли я использовать фильтр для этого?
Я видел подобное решение в Интернете до того, как реализовал его с помощью Filter.На самом деле, наша первая версия также использует для этого Filter, но есть проблема.Провайдер нашего mock-интерфейса должен быть в сети.
Ниже объясняется, почему эта проблема возникает с точки зрения исходного кода.
При условии использования zookeeper в качестве центра регистрации и check=false
Логика фильтра внедряется через класс-оболочку протокола, ProtocolFilterWrapper.ProtocolFilterWrapper внедряет логику цепочки вызовов фильтра для других протоколов, кроме RegistryProtocol.
//from ProtocolFilterWrapper
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
return protocol.refer(type, url);
}
return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
}
Проблема кроется в RegistryProtocol.RegistryProtocol косвенно зависит от DubboProtocl через модули Cluster и Directory.В модуле Directory, то есть RegistryDirectory, будет проверяться количество провайдеров.Если будет 0, то будет брошено исключение . Все это происходит до вызова вызывающей программы, сгенерированной DubboProtocl.
Инициатор, сгенерированный DubboProtocol, инкапсулирует логику фильтрации и вызывает логику к удаленным службам.
Invoekr, созданный RegistryProtcol, инкапсулирует функции управления услугами, такие как вызов кластера и балансировка нагрузки на основе DubboProtocol.
//from RegistryDirectory
private void refreshInvoker(List<URL> invokerUrls) {
Assert.notNull(invokerUrls, "invokerUrls should not be null");
//这边为什么是一个,针对没有提供者目录,dubbo框架会自动返回一个empty的url
if (invokerUrls.size() == 1
&& invokerUrls.get(0) != null
&& Constants.EMPTY_PROTOCOL.equals(invokerUrls.get(0).getProtocol())) {
this.forbidden = true; // Forbid to access
this.invokers = Collections.emptyList();
routerChain.setInvokers(this.invokers);
destroyAllInvokers(); // Close all invokers
}
//...
}
public List<Invoker<T>> doList(Invocation invocation) {
if (forbidden) {
// 1. No service provider 2. Service providers are disabled
throw new RpcException(RpcException.FORBIDDEN_EXCEPTION, "No provider available from registry " +
getUrl().getAddress() + " for service " + getConsumerUrl().getServiceKey() + " on consumer " +
NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() +
", please check status of providers(disabled, not registered or in blacklist).");
}
//...
}
Друзья, которые не читали исходный код dubbo, могут его не понять, вы можете попробовать его после прочтения принципа dubbo refer
недостаточный
Интерфейсы json и dubbo на мок-сервере не сильно связаны, но это не большая проблема, мы все запускаем предустановленный процесс.
проект с открытым исходным кодом
Так много было сказано, все это принципиальное содержание, и ссылка прикреплена ниже, и каждый может использовать ее и вносить предложения.