(495) 925-0049, ITShop интернет-магазин 229-0436, Учебный Центр 925-0049
  Главная страница Карта сайта Контакты
Поиск
Вход
Регистрация
Рассылки сайта
 
 
 
 
 

Модульное тестирование OLAP-кубов SQL Server с применением C#

Марк Нейделсон

Исходный код можно скачать по ссылке

Продукты и технологии:

SQL Server, C#, Online Analytical Processing (OLAP)

В статье рассматриваются:

  • характеристики куба;
  • пример куба для анализа объемов продаж;
  • создание версии куба для модульного тестирования;
  • проверка поведения куба;
  • тестирование куба.

Я чувствую себя немного Томасом Джефферсоном, когда говорю: "Мы почитаем само собой разумеющимися следующие истины, что весь код имеет определенные неотчуждаемые права и что среди этих прав - возможность тщательного модульного тестирования простым и выразительным способом, чтобы любые дефекты могли быть легко выявлены и чтобы отключения производственных сред были сведены к минимуму". Да, слегка возвышенно, но точно передает мою точку зрения.

Конечно, большинство разработчиков считает, что их код должен подвергаться модульному тестированию, но что происходит, когда определение "кода" расплывчато? Я сам недавно попал в такую ситуацию, когда мне предложили сложную проблему и поставили задачу найти решение. Группа разработчиков занималась написанием сложного OLAP-куба, используя SQL Server Analysis Services (SSAS). В этом кубе было множество измерений, и все они были связаны с крайне сложной таблицей фактов (fact table). Поскольку разработчики были весьма искушенными в создании кубов, они сумели собрать куб, но проверка результатов их MDX-запросов (Multidimensional Expressions) оказалась устрашающей задачей. Трудности усугублялись рядом факторов, в том числе объемом данных в измерениях (dimensions) и таблицей фактов, а также временем и вычислительными ресурсами, необходимыми для сборки куба. После создания куба результаты (получаемые с помощью MDX-запросов) отправлялись пользователям. Если бы пользователи нашли какую-то проблему с данными, на локализацию источника проблемы потребовалось бы очень много времени. Кроме того, после обнаружения и устранения нижележащей проблемы куб пришлось бы регенерировать. Еще одно осложнение: если добавлялись размерности, нижележащая таблица фактов обновлялась или куб собирался с использованием других агрегаций (aggregations) - никакого способа определить полное влияние этих изменений не было. Кажущееся невинным изменение могло вызвать далеко идущие и каскадные последствия для запросов к кубу.

Решение парадоксальной ситуации с кубом - создать среду и процесс, где можно разместить измерения куба и факты, используя ограниченный объем данных, и выполнять запросы к кубу с применением производственной версии схемы куба. В идеале, тестовую версию куба следовало бы создавать с нуля всякий раз, когда запускаются модульные тесты, что исключает шансы на проявление побочных эффектов, возникающих из-за существующих артефактов. Другие требования к инфраструктуре модульного тестирования (на самом деле они распространяются на большинство инфраструктур модульного тестирования независимо от целевого приложения) - обеспечить повторяемость оценок качества по результатам тестов (test validations) и, если происходит сбой, легко и быстро выявлять причину провала теста (когда тестовый сценарий сообщает о провале из-за несовпадения данных, это далеко от идеала).

В этой статье я представлю инфраструктуру, которая позволяет создавать набор модульных тестов для проверки MDX-вывода OLAP-куба. Описываемая архитектура обеспечивает создание куба по существующей схеме внутри своей базы данных модульного тестирования (unit-test database) и его воссоздание при каждом запуске набора тестов. Она также позволяет предоставлять MDX-запросы, выполняемые применительно к только что сформированной тестовой версии куба. Более того, она сверяет результаты с существующим шаблоном и представляет их в простом HTML-формате. Такой вариант проверки тестового сценария по шаблону можно расширить до инфраструктур модульного тестирования, в которых результаты данных велики и сложны.

Обзор кубов

Прежде чем углубиться в решение проблемы тестирования куба, я вкратце обрисую концепции и компоненты, лежащие в основе куба. Кубы - это средства быстрого доступа к данным, содержащимся в большом хранилище данных (data warehouse). Они организуют и суммируют данные в многомерную структуру. Кубы являются основным компонентом технологии OLAP и предоставляют простой в использовании механизм для запроса данных с малым и предсказуемым временем ответа. Куб состоит из данных измерения (dimension data) и мер (measures) (числовых фактов). Центральная таблица в кубе известна как таблица фактов и является источником мер куба (cube"s measures). Она ссылается на таблицы измерений (dimension tables), которые содержат иерархические уровни информации, которые можно запрашивать. Иерархия измерений дает возможность пользователям задавать вопросы на высоком уровне. Затем, применяя иерархию измерения, пользователи могут углубляться в детали.

Кубы содержатся в базе данных. Вот что представляют собой объекты, которые образуют структуру куба для конкретной базы данных.

  • Источники данных Источники информации, загружаемые в куб.
  • Меры Числовые значения, представленные в кубе. Это могут быть даты, но обычно они являются числовым с разными уровнями агрегации (например, Sum, Max, Min и Count).
  • Измерения Атрибуты, сопоставляемые с мерами. Распространенные примеры измерений - бизнес-данные, имена клиентов и географические регионы.
  • Разделы Раздел (partition) определяет часть данных фактов, загружаемую в группу мер. За счет создания множества разделов куб можно обрабатывать параллельно, а также раздельно хранить и запрашивать, что повышает производительность. Кроме того, вы можете заново обрабатывать индивидуальные разделы, не влияя на остальные.
  • Роли куба В каждом кубе должна быть минимум одна роль, которая разрешает доступ конечным пользователям. Роли могут разрешать доступ ко всем данным или только к подмножеству данных, хранящихся в кубе, на основе идентификатора индивидуального пользователя или его членстве в какой-либо группе Active Directory.

Кубы являются основным компонентом технологии OLAP и предоставляют простой в использовании механизм для запроса данных с малым и предсказуемым временем ответа.

Определение схемы куба можно извлечь из SSAS в виде XML for Analysis (XMLA). XMLA - это XML-протокол на базе SOAP, который обеспечивает доступ к кубу по HTTP. XMLA-определение содержит все детали для каждого из пяти объектов куба, описанных ранее. XMLA позволяет легко и быстро воссоздавать куб в разных базах данных или на разных серверах. Это краеугольный камень, вокруг которого создается модульно тестируемый куб.

Как только куб создан и обработан, можно запрашивать меры данных, всячески комбинируя множество измерений, задействованных при создании куба. Кубы запрашиваются с помощью ранее упомянутого синтаксиса MDX. Как и SQL-запрос, MDX-запрос содержит запрос данных (через выражение SELECT), точку данных (data point) (через выражение FROM) и необязательный фильтр данных (через блок WHERE). Вот базовый пример:

SELECT {[<axis specification using Measures>]...} ON AXIS(0), {[<axis specification using a dimension hierarchy>]...} ON AXIS(1) FROM [<cube>] WHERE (<filter specification>)

Пример куба продаж

Я создал пример куба, позволяющего быстро запрашивать данные продаж для разных магазинов в разные периоды по разным клиентам (рис. 1).

Схема базы данных для примера куба продаж
Рис. 1. Схема базы данных для примера куба продаж

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

Создание модульно тестируемой версии куба

Как упоминалось, определение куба можно извлечь из SSAS в форме XMLA-документа. Этот XMLA-документ используется для создания модульно тестируемой версии куба. Применение производственного определения куба гарантирует, что тесты будут точно проверять все средства этого куба. Вы взаимодействуете с механизмом SSAS с помощью Microsoft.AnalysisServices.dll, которая находится в сборках SQL Server SDK.

Для генерации куба используется объект XMLAtoCube. Его конструктор принимает каталог базовой конфигурации, в котором хранится XMLA. Выбранная мной структура каталогов для размещения XMLA-куба такова: < базовый каталог >\< имя производственного сервера >\< имя производственной базы данных >.

{Для верстки: не выделять полужирным ссылки на листинги, их нет в статье - они в сопутствующем коде}

XMLAtoCube содержит один открытый метод, CreateCubeFromXMLA, который принимает следующие параметры (код этого метода см. в листинге 1 в файле Listings.zip в сопутствующем коде).

  • SourceServerName Имя производственного сервера, на котором размещен куб.
  • TargetServerName Имя сервера, на котором размещена модульно тестируемая версия куба.
  • SourceDatabaseName Имя производственной базы данных, содержащей куб.
  • TargetDatabaseName Имя базы данных, в которой будет размещена модульно тестируемая версия куба.
  • DataSourceProviderDefinition URL строки подключения, которая указывает модульно тестируемой версии куба местонахождение измерений его источника и таблицы фактов. Это будет источник данных меньшего масштаба для модульного теста.

CreateCubeFromXMLA сначала устанавливает соединение с сервером аналитики для модульного тестирования и удаляет базу данных модульно тестируемой версии куба, если таковая уже есть. Удаление базы данных очень важно, потому что это гарантирует выполнение тестов в чистой среде без остаточных артефактов, которые могли бы исказить результаты. Соединение с сервером аналитики осуществляется в методе ConnectToServer с помощью экземпляра Microsoft.AnalysisServices.Server. Вызов Server.Connect(< строка подключения >), в котором используется переданное имя сервера модульного тестирования, устанавливает соединение (листинг 2 в сопутствующем коде). Как только соединение с сервером успешно установлено, база данных модульно тестируемой версии куба удаляется, если она существует. Это удаление выполняется методом DropDatabase, которому передается экземпляр подключенного сервера и имя базы данных, подлежащей удалению (TargetServerName). DropDatabase убеждается, что экземпляр сервера подключен, ищет Microsoft.AnalysisServices.Database по переданному имени базы данных и, если она существует (экземпляр базы данных не равен null), удаляет ее (листинг 3 в сопутствующем коде).

Следующий шаг - получить XMLA-определение исходного куба и сгенерировать его версию для модульного тестирования. Эта версия содержит те же измерения, иерархии измерений, меры, разделы, роли и все остальное, что и в исходном кубе. Отличие в том, что она генерируется в новой базе данных, указывающей другое местонахождение ее исходных данных. Метод AddCubeToDatabase создает тестовый куб (листинг 4 в сопутствующем коде). AddCubeToDatabase считывает XMLA-определение из файловой системы, используя ранее упомянутое соглашение по именованию. Имя XMLA-файла передается экземпляру XmlTextReader при конструировании. XMLA считывается методом Microsoft.AnalysisServices.Utils.Deserialize, а затем передается XmlTextReader и только что созданному экземпляру Microsoft.AnalysisServices.Database. Экземпляр объекта Database теперь содержит полное определение куба, но это определение по-прежнему имеет имя базы данных и источник данных исходного куба. Чтобы указать на базу данных модульного тестирования, достаточно задать свойства Name и ID параметра "Database to unit-test database name" (targetDatabaseName). Затем эта база данных может быть добавлена к экземпляру сервера аналитики модульного тестирования (unit-test analytics server) вызовом Server.Databases.Add(< база данных модульного тестирования >) и последующим вызовом метода Database.Update.

После создания базы данных вы должны обновить источник данных куба внешним набором данных для модульного тестирования. У экземпляра Database есть список экземпляров DataSource (обычно с кубом сопоставляется только один источник данных), а строка подключения в XMLA-определении заменяется строкой подключения к модульному тесту. После замены строки подключения вызов метода DataSource.Update обновляет ее в сервере SSAS. К этому моменту настройка XMLA-определения завершается, а остальные части куба (DataSourceView, Dimension и Cube) обновляются и обрабатываются.

По завершении вызова AddCubeToDatabase создается модульно тестируемая версия куба с заданным сервером и используется выбранная база данных. Она указывает на пользовательский набор исходных данных. Хотя куб создан, он пока пуст. Чтобы заполнить измерения исходными данными, эти измерения нужно обработать. Поэтому вызывается метод ProcessDimensions с передачей экземпляра Database. Все Dimension в Database обрабатываются с помощью метода Dimension.Process(ProcessType.ProcessFull). После успешной обработки измерений обрабатываются кубы в базе данных модульного тестирования с применением метода ProcessCube. Как и ProcessDimensions, ProcessCube принимает экземпляр Database, перебирает в цикле все кубы в Database и вызывает Cube.Process(ProcessType.ProcessFull) (листинг 5 в сопутствующем коде). К этому моменту модульно тестируемая версия куба создается и заполняется целевыми тестовыми данными. Следующий шаг - запуск тестов применительно к этому кубу, чтобы убедиться, что поведение куба соответствует ожидаемому.

Проверка поведения куба

Хотя решение для проверки (validation) ориентировано на куб, используемый проектировочный шаблон применим и к другим инфраструктурам модульного тестирования. В процессе тестирования используется проверка шаблона (template validation). Проще говоря, при запуске теста структура данных, содержащая результаты, создается и сверяется с ранее сохраненной версией структуры данных, которая была признана правильной. Поскольку проверять двоичную структуру данных трудно, тестеру, который осуществляет модульное тестирование, передается HTML-представление этой структуры. Тестер использует HTML-представление для проверки начальных результатов модульного теста (чтобы убедиться в том, что результаты соответствуют ожидаемым) и изучает, что привело к провалу этого теста при последующих прогонах. В случае провала HTML отображает исходную структуру данных и то, какая часть структуры не прошла проверку и почему. Это крайне важно в отладке.

Лучший способ тестирования в большинстве сценариев - использование методологии черного ящика. Тест по этой методологии передает входные данные и проверяет выходные. Поскольку для куба выходные данные - это результат запроса, входными данными является запрос. Для запроса куба используются MDX-выражения. После того как куб интерпретирует MDX-запрос, он возвращает результат. Этот результат выдается в виде строк и полей, которые являются представлением измерений, выбранных для выполнения MDX-запроса. Результат MDX-запроса может быть весьма сложным, так как запрос может охватывать целый ряд измерений, а это дает в выводе множество строк и полей. Структура данных, выбранная для хранения данных куба - словарь экземпляров CubeRow, ключом которых служит имя строки куба. Класс CubeRow содержит две альтернативные структуры данных, одна из которых используется в зависимости от ситуации. Это словарь экземпляров CubeTuple, ключом которых служит имя поля. CubeTuple - это просто объект, содержащий имя поля куба и значение данного поля. Словарь объектов CubeTuble используется, когда в данном поле куба есть значение. Вторая структура данных является другим словарем, сопоставляющим имя строки с экземпляром CubeRow. Поскольку MDX-запрос может иметь много уровней измерений строк, CubeRow содержит собственный словарь с именем строки и соответствующими экземплярами CubeRow.

Результаты куба хранятся не только в экземпляре Dictionary<String, CubeRow>, они хранятся и в HTML-строке. HTML-строка дает тестеру визуальное представление результатов куба. HTML-таблица содержит несколько уровней заголовков полей и строк, а в ячейках этой таблицы находятся MDX-значения. В табл. 1 показана разметка HTML-представления результата MDX-запроса.

Табл. 1. Разметка HTML-представления результата MDX-запроса

   

Измерение поля

Описание 1 (значение 1)

Измерение поля

Описание 1 (значение 1)

Измерение поля

Описание 1 (значение 2)

Измерение поля

Описание 1 (значение 2)

   

Измерение поля

Описание 2 (значение 1)

Измерение поля

Описание 2 (значение 2)

Измерение поля

Описание 2 (значение 1)

Измерение поля

Описание 2 (значение 2)

Измерение строки

Описание 1 (значение 1)

Измерение строки

Описание 2 (значение 1)

MDX-значение результата MDX-значение результата MDX-значение результата MDX-значение результата

Измерение строки

Описание 1 (значение 1)

Измерение строки

Описание 2 (значение 2)

MDX-значение результата MDX-значение результата MDX-значение результата MDX-значение результата

Измерение строки

Описание 1 (значение 2)

Измерение строки

Описание 2 (значение 1)

MDX-значение результата MDX-значение результата MDX-значение результата MDX-значение результата

Измерение строки

Описание 1 (значение 2)

Измерение строки

Описание 2 (значение 2)

MDX-значение результата MDX-значение результата MDX-значение результата MDX-значение результата

BaseMDXTest содержит код для выполнения MDX-запроса и создает структуру данных MDX-результата, а также репрезентативный XML. BaseMDXTest использует Microsoft.AnalysisServices.AdomdClient.dll, которая находится в сборках SQL Server SDK, для подключения к SSAS-кубу и выполнения MDX-запросов. BuildTemplate - это метод, который выполняет MDX-запрос и формирует как словарь MDX-результата, так и HTML-представление. Сначала устанавливается соединение с кубом. Для установления соединения и его открытия в экземпляр MicrosoftAnalysisServices.AdomdClient.AdomdConnection передается строка подключения. Затем в только что созданном экземпляре соединения вызывается метод Open, и экземпляр соединения возвращается вызвавшему коду. После создания соединения создается экземпляр MicrosoftAnalysisServices.AdomdClient.AdomdCommand и передается в строку MDX-запроса и экземпляр AdomdConnection. Вызов метода AdomdCommand.ExecuteCellSet выполняет MDX-запрос применительно к модульно тестируемой версии куба и возвращает экземпляр MicrosoftAnalysisServices.AdomdClient.CellSet (листинг 6 в сопутствующем коде).

После получения экземпляра CellSet выполняется проверка, чтобы удостовериться, что в наборе результатов присутствуют две оси. Ось 0 содержит столбцы (поля), а ось 1 - строки. Каждая ось содержит набор объектов Position. Position представляет tuple в данной оси и включает один или более объектов Member. Member представляет заголовки полей или строк для данного Position (листинг 7 в сопутствующем коде).

Затем вычисляется количество строк и полей, возвращенных MDX-запросом. Для этого берется число строк (счетчик объектов Position в Axis 1), складывается с количеством измерений поля (CellSet.Axes[0].Positions[0].Members.Count), затем к строкам добавляется число полей, поскольку при представлении MDX-результатов в виде двухмерной таблицы эти поля включаются в набор строк. Аналогично вычисляется количество полей (листинг 8 в сопутствующем коде).

Зная число строк и полей, можно сгенерировать словарь объектов CubeRow и HTML-представление MDX-вывода. На листинге 9 в сопутствующем коде вы найдете код для прохода по MDX-набору результатов, создания HTML и сохранения результатов в CubeRow Dictionary. Сначала в цикле перебираются строки. Если номер строки больше, чем количество измерений поля, значит, в MDX-результатах доступна новая строка. Иначе говоря, после прохода по строкам заголовков полей становятся доступными MDX-данные. В этот момент создается новый объект CubeRow.

Следующий шаг - перебор в цикле каждого поля в строке. Если номер текущей строки меньше, чем количество измерений поля, значит, текущая строка на самом деле является строкой заголовков полей. В словаре описания заголовков (header captions) объединяются для местоположения каждого поля и отделяются двоеточием. То есть, если заголовок состоит из нескольких измерений, каждое поле будет объединено с данным измерением в порядке убывания разрешения (descending order of resolution). Генерация HTML для заголовка поля осуществляется проще. Заголовок измерения просто окружается HTML-тегами заголовка таблицы (<th></th>). В каждом случае заголовок текущего измерения извлекается из оси заголовка (CellSet.Axis[0]), позиции поля (счетчик текущего поля минус счетчик измерения текущей строки) и текущего Member в этой позиции (счетчик текущей строки).

Если номер текущей строки больше, чем количество измерений поля, заголовки полей больше не обрабатываются. Вместо них в очередь на обработку ставятся кортежи строк (row tuples) MDX-набора результатов. По аналогии с полями, которые имеют заголовки, в строках MDX-набора результатов тоже могут быть заголовки. Если номер обрабатываемого поля меньше, чем количество измерений строки, то обрабатывается заголовок строки. Для каждого заголовка строки создается новый Dictionary<string, CubeRow>, добавляется к текущему словарю и задается как словарь текущего MDX-результата. Это означает, что для каждого заголовка поля существует словарь строк, содержащий более детализированные данные MDX-результата.

Извлечение описания заголовка строки аналогично таковому для описания заголовка поля. Описание строки (row caption) извлекается из объекта Member в позиции текущего поля (из Position) текущей строки (CellSet.Axis[1]). Описание строки является ключом к словарю объектов CubeRow, и, если текущий CubeRow Dictionary не имеет извлекаемого описания строки, объект CubeRow добавляется в словарь с ключом, в качестве которого используется описание строки. Если же описание строки имеется в текущем CubeRow Dictionary, то объект CubeRow извлекается и задается как currentCubeRow.

После того как члены описания строки исчерпываются (т. е. текущий счетчик полей больше количества измерений строки) и строки заголовков полей пройдены (т. е. текущий счетчик строк больше количества измерений поля), наступает пора добавить значения MDX-ячейки в текущий объект CubeRow. Каждая комбинация заголовка и значения поля образует один экземпляр CubeTuple. Каждый CubeRow содержит словарь объектов CubeTuple, в качестве ключа которых используется заголовок поля. Заголовок поля извлекается из ранее сконструированного массива заголовков полей (вспомните, что заголовок поля - это строка из всех описаний полей, разделенных двоеточиями). Индекс заголовка поля равен текущему счетчику полей за вычетом количества измерений строки (общий счетчик полей включает измерения строки). Текущее MDX-значение CellSet формируется обращением к соответствующей двухмерной точке (поле, строка). Она основана на текущем счетчике полей (минус количество измерений строки) и текущем счетчике строк (минус количество измерений поля). Это значение добавляется в объект CubeRow с применением метода AddTuple, которому передаются заголовок и значение поля. В то же время HTML-представление обновляется добавлением MDX-значения ячейки между HTML-тегами измерения таблицы (<td></td>). Графическое представление Dictionary<string, CubeRow> показано на рис. 2.

Графическое представление CubeRow Dictionary
Рис. 2. Графическое представление CubeRow Dictionary

Row Dimension Caption Описание измерения строки
Column Dimension Caption Описание измерения поля
Column Dimension Value Описание измерения значения

Как Dictionary, так и HTML-представление шаблона сохраняются в ранее определенном местоположении в файловой системе вызовом метода BaseMDXTest.PersistTemplate. Поскольку шаблон должен вручную проверяться разработчиком модульных тестов, тест считается проваленным, и вот почему метод BaseMDXTest.TestMDXQuery возвращает false при успехе.

После создания шаблона последующие прогоны того же теста проверяются относительно ранее сохраненного шаблона. При вызове метода TestMDXQuery сначала выполняется проверка на наличие теста с заданным именем. Если он есть и создание нового шаблона не запрашивается (запрос на повторное создание шаблона возможен, если текущий шаблон некорректен), то шаблон результатов теста загружается в память. Шаблон включает и объектное, и HTML-представление MDX-набора результатов. BaseMDXTest.RunComparison выполняет MDX-запрос и сверяет результаты с сохраненным шаблоном. Результаты MDX-запроса проходят по тому же пути, что и при создании шаблона. Основное различие между созданием исходного шаблона и проверкой относительно шаблона заключается в том, что вместо создания Dictionary<string, CubeRow> выполняются операции поиска в словаре шаблона, чтобы узнать, существуют ли те же результаты MDX-запроса. Когда в цикле перебираются строки и поля, HTML-таблица создается так же, как и при создании шаблона, - с тем исключением, что теперь ячейки в HTML-таблице окрашиваются. Зеленая ячейка указывает на совпадение с соответствующей ячейкой в исходном шаблоне, а красная - на расхождение. Раскраска ячеек и их представление в HTML-таблице позволяет тестеру сразу же понять, почему тестовый сценарий прошел успешно или неудачно. Всякий раз, когда обнаруживается несовпадение, булев флаг (testPass) устанавливается в false, указывая, что тестовый сценарий завершился неудачей.

Хотя это не так просто и прямолинейно, но даже OLAP-куб можно подвергнуть модульному тестированию, используя C#.

При проходе по результатам MDX-запроса и их проверке относительно словаря шаблона, каждый найденный CubeTuple (объект, содержащий измерение поля, сцепленные имена и значение поля) удаляется из текущего CubeRow-словаря объектов CubeTuple. Следовательно, после прохода всех результатов MDX-запроса словарь исходного шаблона должен иметь объекты CubeRow с пустым словарем объектов CubeTuple, если было полное совпадение MDX-результатов. В ином случае в результатах нового MDX-запроса отсутствуют данные, которые содержались в исходном результате. Метод BaseMDXTest.CheckForExtraDataInTemplate рекурсивно проверяет словарь шаблона на оставшиеся объекты CubeTuple и возвращает true, если остались объекты CubeTuple. Булев флаг testPass в методе RunComparison устанавливается в false, если найдены дополнительные данные, и тестовый сценарий считается проваленным.

По окончании прохода MDX-результатов и проверки по словарю шаблона возвращается экземпляр объекта CubeComparisonResult. Он конструируется с булевым флагом testPass и HTML-таблицей, показывающей результат. Метод BaseMDXTest.TestMDXQuery использует CubeComparisonResult для формирования HTML-страницы, на которой отображаются HTML-таблица результатов исходного MDX-запроса и HTML-таблица сравнения. HTML сохраняется в файловой системе вызовом метода BaseMDXTest.PersistTestReport, который создает суммарную веб-страницу TestReport.html, где перечисляются все прогоны тестов и ссылки на их HTML-страницы результатов, а также сводное количество пройденных и проваленный тестовых сценариев.

Тестирование куба Purchase

Используя оба компонента в инфраструктуре тестирования куба - код создания куба (XMLAtoCube) и шаблон результатов MDX-запроса (BaseMDXTest), - вы можете создавать сценарии модульных тестов, проверяющих куб. Хотя код инфраструктуры весьма обширен, создание тестовых сценариев осуществляется достаточно просто и прямолинейно. На листинге 10 в сопутствующем коде содержится пример модульных тестов для проверки куба Purchase. Эти тестовые сценарии используют инфраструктуру тестирования от Microsoft, но годится и любая другая инфраструктура тестирования.

Объект модульного теста (PurchaseCubeTest в этом примере) наследует от BaseMDXTest. Конструктор по умолчанию в PurchaseCubeTest конструирует BaseMDXTest с использованием URL сервера SSAS, где расположен куб, и базового каталога, в котором следует хранить шаблон результатов MDX-запроса и результаты последующих тестов.

Метод [TestInitialize] служит для создания модульно тестируемой версии куба Purchase. Он принимает XMLA-определение исходного куба и создает его на сервере SSAS модульного тестирования (targetServerName), используя базу данных модульного тестирования (targetDatabaseName). Кроме того, он задает URL исходных данных так, чтобы он указывал на местоположение измерения теста и данные фактов. [TestInitialize] выполняется только раз для данного [TestClass], что гарантирует: куб создается лишь в начале тестирования.

Сами тестовые сценарии выполняются в методах, аннотированных [TestMethod]. Каждый тестовый сценарий прост. В нем определяется MDX-запрос, а затем выполняется, используя наследуемый метод BaseMDXTest.TestMDXQuery. TestMDXQuery возвращает true, если тест проходит успешно, или false, если он заканчивается неудачей, а метод Assert.IsTrue применяется для того, чтобы модульный тест проходил или проваливался. После выполнения всех тестов можно открыть конечный HTML-документ тестов и изучить проваленные тестовые сценарии. На рис. 3 дан пример HTML-вывода одного из тестов.

HTML-вывод примера теста
Рис. 3. HTML-вывод примера теста

Должным образом тестируемый код

Хотя это не так просто и прямолинейно, но даже OLAP-куб можно подвергнуть модульному тестированию, используя C#. Включение файлов Microsoft.AnalysisServices.dll и Microsoft.AnalysisServicesAdomdClient.dll в сборки SQL Server предоставляет вам API как для создания, так и для запроса SSAS-кубов. Представленная архитектура позволяет добавлять богатый набор модульных тестов, которые дают вывод в читаемом человеком формате; благодаря этому можно быстро выявлять провалы тестовых сценариев. Метод проверки тестового сценария по шаблону можно применять в других архитектурах модульного тестирования, где проверочный вывод далеко не прямолинеен. Примеры простираются от приложений, опирающихся на сохранение информации в традиционных реляционных базах данных, где шаблон содержит данные, которые должны сохраняться в базе данных после выполнения приложения, до программ с UI, сохраняющих состояние UI в структуре данных и отображающих этот UI в HTML-форме.

Я не совсем уверен, что Томас Джефферсон или наши отцы-основатели стали бы сравнивать независимость нации с правами кода на должное тестирование, но совершенно убежден, что вашим пользователям и руководителям было бы приятно знать о корректном и тщательном тестировании предлагаемого вами приложения.

Марк Нейделсон  (Mark Nadelson) - профессиональный разработчик с 22-летним опытом в телекоммуникациях, интернет-технологиях и финансовой области. На протяжении всей карьеры использовал многие языки программирования, включая ассемблер, C, C++, Java и C#. Также является автором двух книг и ряда технических статей.

Выражаю благодарность за рецензирование статьи экспертам Дэвиду Нейлеру (David Neigler) и Джо Сэмпино (Joe Sampino).



 Распечатать »
 Правила публикации »
  Написать редактору 
 Рекомендовать » Дата публикации: 25.08.2015 
 

Магазин программного обеспечения   WWW.ITSHOP.RU
Microsoft 365 Business Basic (corporate)
Microsoft Office 365 Персональный 32-bit/x64. 1 ПК/MAC + 1 Планшет + 1 Телефон. Все языки. Подписка на 1 год.
Microsoft Office 365 Бизнес. Подписка на 1 рабочее место на 1 год
Microsoft Office 365 Профессиональный Плюс. Подписка на 1 рабочее место на 1 год
Microsoft Office для дома и учебы 2019 (лицензия ESD)
 
Другие предложения...
 
Курсы обучения   WWW.ITSHOP.RU
 
Другие предложения...
 
Магазин сертификационных экзаменов   WWW.ITSHOP.RU
 
Другие предложения...
 
3D Принтеры | 3D Печать   WWW.ITSHOP.RU
 
Другие предложения...
 
Новости по теме
 
Рассылки Subscribe.ru
Информационные технологии: CASE, RAD, ERP, OLAP
Безопасность компьютерных сетей и защита информации
Новости ITShop.ru - ПО, книги, документация, курсы обучения
CASE-технологии
Программирование на Microsoft Access
Реестр Windows. Секреты работы на компьютере
Проект mic-hard - все об XP - новости, статьи, советы
 
Статьи по теме
 
Новинки каталога Download
 
Исходники
 
Документация
 
 



    
rambler's top100 Rambler's Top100