Введение в тестирование WCF
ОГЛАВЛЕНИЕ
На рис. 1 показана простая, но вполне представительная ситуация с WCF. Здесь Internet Explorer® выступает в качестве клиентской программы и получает доступ к веб-приложению ASP.NET, которое принимает некоторый текст от пользователя и вычисляет для него криптографический хэш. Для фактического выполнения расчетов для хэширования веб-приложение ASP.NET вызывает «за кадром» службу WCF. В этом конкретном случае служба WCF размещается посредством IIS и используется веб-приложением ASP.NET, но, как я вскоре объясню, помимо варианта с IIS, службы WCF можно размещать несколькими способами, и они могут быть использованы приложением или службой практически любого типа.
Рис. 1 Типичный случай использования WCF
Самый простой тип тестирования службы WCF включает проверку правильности работы функций операций службы. Один из возможных подходов заключается в тестировании службы WCF вручную с помощью пользовательского интерфейса приложения. Но, хотя тестирование вручную является необходимым, использование этого подхода для тестирования основных функций службы WCF требует много времени, приводит к ошибкам, является неэффективным и попросту нудным.
Более приемлемый подход заключается в создании программы автоматизации тестирования, подобной той, этап выполнения которой показан на рис. 2. На снимке экрана показана написанная мной программа тестирования консольного приложения. Она подает входной текст непосредственно внутренней службе WCF, получает от службы сообщение с ответом и определяет результат конкретного тестирования — успешное завершение или сбой. Схема на рис. 3 является упрощенным представлением и сводкой взаимосвязей между программами, приведенными на рис. 1 и 2. Во многих случаях службы WCF извлекают информацию из внутренней базы данных или получают информацию от веб-служб или служб WCF, но эти ситуации не включены в рис. 3.
Рис. 2 Тестирование службы WCF
Рис. 3 Упрощенные взаимосвязи
Затем будет рассмотрена серверная служба WCF, чтобы было совершенно понятно, что именно тестируется. Будет вкратце обсуждено использующее службу WCF веб-приложение ASP.NET, показанное на рис. 1, и будет подробно описана программа тестирования. В заключение будут затронуты некоторые другие случаи тестирования WCF.
Тестируемая система
Тестируемая система состоит из серверной службы WCF и веб-приложения ASP.NET, которое использует службу WCF. Службы WCF обладают выдающейся гибкостью. Одним из важнейших решений при создании службы WCF является выбор для службы механизма размещения. Существует четыре основных варианта: использование IIS, использование Windows® Service, размещение на собственном сервере и использование WAS (Windows Activation Service — служба активирования Windows). Возможно, вы знакомы с использованием IIS и Windows. Размещение на собственном сервере влечет размещение WCF в программе, управляемой Microsoft® .NET Framework, например, консольном приложении. WAS представляет собой новый механизм активирования процесса, имеющийся в Windows Server® 2008 и Windows Vista®. У каждого варианта размещения WCF имеются достоинства и недостатки, зависящие от конкретного случая разработки. В этой статье для примера службы WCF я решил использовать IIS. Такой выбор позволяет использовать преимущества IIS, например встроенные интегрированные управление и текущий контроль, перезапуск процесса, завершение работы при ожидании и активирование на основе сообщений.
Создание службы WCF, размещаемой посредством IIS, является поразительно легкой задачей. Я начинаю с запуска Visual Studio® 2008 в среде Windows Server 2003. Отмечу, что если вы решите разрабатывать службу WCF на машине, работающей под управлением Windows Server 2008 или Windows Vista, в процессе разработки придется иметь дело с проблемами, связанными с их расширенными фукнциями безопасности. Однако объем данной статьи не позволяет их описать.
Далее в меню Visual Studio я выбираю режим «Файл | Создать | Веб-узел». Затем в диалоговом окне создания веб-узла я выбираю шаблон службы WCF (установленный по умолчанию в Visual Studio 2008) и целевую среду .NET Framework 3.5. Для поля Location (Местонахождение) выбираю значение HTTP и указываю localhost/WCF/CryptoHashService. При таком подходе на моей рабочей машине создается полное веб-приложение и виртуальный каталог IIS в папке C:\Inetpub\wwwroot\WCF\CryptoHashService; вместо этого можно было бы выбрать местонахождение в файловой системы моей машины и использовать встроенный в Visual Studio сервер разработки веб-приложений.
Я принял решение использовать в качестве языка реализации C#; однако службы WCF можно реализовать также с помощью Visual Basic® .NET. После того, как в диалоговом окне нажата кнопка «OK», Visual Studio создает полнофункциональную службу WCF с двумя примерами операций, названными GetData и GetDataUsingDataContract. Взглянув на окно обозревателя решений, вы увидите, что Visual Studio генерируре четыре важных файла: IService.cs, Service.cs, Service.svc и web.config. Файл IService.cs содержит определения интерфейса для операций WCF, а файл Service.cs содержит фактические реализации ваших операций. В данном случае я принял решение переименовать эти два файла в ICryptoHashService.cs и CryptoHashService.cs соответственно. Далее я загружаю ICryptoHashService.cs в редактор кода Visual Studio, удаляю код примера интерфейса и заменяю его следующим кодом.
[ServiceContract]
public interface ICryptoHashService
{
[OperationContract]
string GetCryptoHash(string s);
}
Здесь только одна операция, GetCryptoHash, но можно было бы добавить и другие. Обратите внимание на то, что большую часть фактической работы по генерации кода вместо меня выполняют «за кадром» атрибуты [SeviceContract] и [OperationContract]. Далее я редактирую файл реализации CryptoHashService.cs, добавляя ссылку оператора using на пространство имен System.Security.Cryptography, и пишу следующий код.
public class CryptoHashService : ICryptoHashService
{
public string GetCryptoHash(string s)
{
byte[] ba = Encoding.Unicode.GetBytes(s);
MD5CryptoServiceProvider sp = new MD5CryptoServiceProvider();
byte[] hash = sp.ComputeHash(ba);
string result = BitConverter.ToString(hash);
return result;
}
}
Сначала я изменяю имя моего класса CryptoHashService и класса, от которого он произведен, чтобы они соответствовали имени, используемому в моем определении интерфейса. В моем методе GetCryptoHash я просто использую метод GetBytes для преобразования входного аргумента в массив байт, получаю экземпляр класса MD5CryptoServiceProvider и использую метод ComputeHash для преобразования массива байтов в 16-байтовый крипотграфический хэш MD5. Результирующий хэш преобразую из массива байт в понятную строку с помощью статического метода BitConverter.ToString, затем возвращаю эту строку. Чтобы не удлинять пример, я опустил обычную проверку ошибок, которую следует использовать в рабочей среде, например проверку входного аргумента, перехват исключений и т.д.. Далее я редактирую файл Service.svc, чтобы отразить изменения в именах, заменяя «Service» на «CryptoHashService».
<%@ ServiceHost Language="C#" Debug="true" Service="CryptoHashService"
CodeBehind="~/App_Code/CryptoHashService.cs" %>
Редактирование имен завершается обновлением двух ссылок на имена в файле web.config.
<system.serviceModel>
<services>
<service name="CryptoHashService" behaviorConfiguration="Servic Behavior">
<!-- Service Endpoints -->
<endpoint address=""binding="wsHttpBinding" contract="ICryptoHashService">
Обратите внимание на запись binding="wsHttpBinding". Привязка WCF представляет собой коллекцию данных, указывающих на то, каким образом служба WCF осуществляет связь с клиентами, включая транспортный протокол, используемый службой, используемую схему кодировки текста и т.д. Можно использовать встроенные привязки или создавать пользовательские привязки. Привязка wsHttpBinding является предварительно настроенной, используемой по умолчанию при создании размещаемой посредством IIS службы WCF. Visual Studio генерирует еще одну часть файла web.config:
<endpoint address="mex" binding="mexHttpBinding"
contract="IMetadataExchange"/>
Эта запись сообщает моей службе WCF о необходимости предоставлять доступ к метаданным, относящимся собственно к службе, чтобы клиентские программы могли проверять ее для определения способа взаимодействия с ней. Теперь можно с успехом строить службу WCF, выбрав в главном меню пункт «Построить | Построить решение». Можно было бы также воспользоваться клавишей F5, чтобы как построить службу WCF, так и получить рекомендации по созданию клиентской программы. Поскольку размещение моей службы выполняется посредством IIS, мне не требуется запускать службу явным образом; служба WCF будет готова к приему входящих сообщений WCF всегда, когда работает IIS.
Теперь приступим к разбору процедуры создания веб-приложения ASP.NET, показанного на рис. 1. Я запускаю новый экземпляр Visual Studio и выдаю команду «Файл | Создать | Веб-узел». В диалоговом окне создания веб-узла я выбираю шаблон веб-узла ASP.NET и в раскрывающемся списке целевых платформ выбираю .NET Framework 3.5. Я использую Location HTTP и выбираю C# в качестве языка. В поле местонахождения ввожу строку localhost/WCF/UtilitiesAndTools для неявного присвоения имени моему веб-приложению. Можно было бы указать местоположение файловой системы на моей рабочей машине и вместо IIS использовать веб-сервер. Далее я добавляю в мое веб-приложение некоторый минимальный код UI.
Код на рис. 4 представляет собой основной UI, без таких деталей форматирования, как стиль шрифта и теги <hr/>. Точный код UI наряду с кодом, приведенном в этой статье, можно получить из материалов для загрузки, прилагаемых к статье.
Рис. 4 Код UI веб-приложения ASP.NET
<body>
<form id="form1" runat="server">
<div>
<asp:Label runat="server" ID="Label"
Text="Demo Utilities Featuring WCF Services" />
<asp:Label runat="server" ID="Label2"
Text="Enter text here:" />
<asp:TextBox runat="server" ID="TextBox1"
Height="100px" Width="320px" />
<asp:Button runat="server" ID="Button1"
Text="Get MD5 Crypto-Hash" onclick="Button1_Click"
Width="150px" />
<asp:Button runat="server" ID="Button2"
Text="Get SHA1 Crypto-Hash"
Width="150px" />
<asp:Label runat="server" ID="Label3"
Text="Crypto-Hash of your text (computed by WCF Service) is:" />
<asp:TextBox runat="server" ID="TextBox2"
Width="320px" />
</div>
</form>
</body>
В данный момент мое веб-приложение ASP.NET ничего не знает о моей службе WCF; однако технологии WCF и ASP.NET разработаны для беспрепятственной совместной работы. В окне обозревателя решений моего проекта веб-приложения я щелкаю правой кнопкой мыши имя проекта и выбираю «Добавить ссылку на службу». Обратите внимание на то, что это новая возможность, дополняющая старые режимы «Добавить ссылку» (обычно использовался для библиотек DLL и пространств имен .NET) и «Добавить веб-ссылку» (обычно использовался для веб-служб ASP.NET).
В результирующем диалоговом окне «Добавить ссылку на службу» я ввожу строку localhost/WCF/CryptoHashService/Service.svc и нажимаю кнопку «Начать». Инструментальное средство добавления ссылки на службу проверяет указанное местоположение на наличие служб и отображает обнаруженные службы; в данном случае службу CryptoHashService WCF. В диалоговом окне, в поле «Пространство имен» я выбираю простое, но удобное описательное имя по умолчанию, ServiceReference1, затем нажимаю кнопку «OK». Visual Studio генерирует весь код прокси, требуемый моему приложению для связи со службой WCF. В частности, я получаю класс с именем CryptoHashServiceClient (т.е, к имени службы WCF добавлено слово «Client»), который дает возможность устанавливать связь со службой CryptoHashService.
В окне конструктора дважды щелкаю элемент элемент управления Button1 для передачи Visual Studio инструкции относительно необходимости зарегистрировать обработчик событий элемента управления. Затем добавляю этот код в метод Button1_Click.
try
{
string s = TextBox1.Text;
ServiceReference1.CryptoHashServiceClient c =
new ServiceReference1.CryptoHasahServiceClient();
string h = c.GetCryptoHash(s);
TextBox2.Text = h;
}
catch(Exception ex)
{
TextBox2.Text = ex.Message;
}
Это даже слишком легко! Извлекаю текст из поля TextBox1, образую экземпляр автоматически сгенерированного класса CryptoHashServiceClient, вызываю метод GetCryptoHash объекта и отображаю результирующий крипотографический хэш MD5 (Message Digest version 5) в поле TextBox2. Пользовательский интерфейс веб-приложения на рис. 1 демонстрирует элемент управления кнопки, осуществляющий вычисление криптографического хэша SHA-1 (Secure Hash Algorithm 1), но я не реализовывал эти функции для имитации разрабатываемой системы. После привязки веб-приложения пользователь может запустить Internet Explorer и отыскать приложение, ввести некоторый текст и получить криптографический хэш MD5 текста, вычисленный серверной службой WCF, как показано на рис. 1.
Программа тестирования
Теперь обратимся к коду для простой программы тестирования, показанному на рис. 2. По существу программа тестирования представляет собой просто клиент WCF, который отправляет входное сообщение тестируемой службе WCF и проверяет правильность возвращаемого сообщения. Начну с запуска нового экземпляра Visual Studio и создания программы консольного приложения на C# с названием TestHarness. В примере, показанном на рис. 2, обратите внимание на то, что местонахождение моей программы я задаю в виде подкаталога C:\Inetpub\wwwroot именно для того, чтобы держать код программы поблизости от тестируемой системы. Однако можно было бы поместить программу в любое другое место, включая изолированный узел для тестирования. На рис. 5 показана общая структура программы тестирования.
Рис. 5 Структура программы тестирования
using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;namespace TestHarness
{
class Program
{
static void Main(string[] args)
{
try
{
// display startup messages
// set up test case data collection, testCases
// set up WCF binding object, wsb
// set up WCF address object, epaCryptoHashServiceClient c =
new CryptoHashServiceClient(wsb, epa);foreach (TestCaseData tcd in testCases)
{
// echo case ID, input, expected values
// call GetCryptoHash method, fetch return
// compare actual return with expected
// print pass/fail result
}
Console.WriteLine("\nDone");
}
catch (Exception ex)
{
Console.WriteLine("Fatal error: " + ex.Message);
}
}
}class TestCaseData
{
public readonly string caseID;
public readonly string input;
public readonly string expected;
public TestCaseData(string caseID, string input, string expected)
{
this.caseID = caseID; this.input = input; this.expected =
expected;
}
}
}
Кодирование программы тестирования я начинаю с добавления оператора using, указывающего на пространство имен System.ServiceModel. В этом пространстве имен содержатся основные функции службы WCF. Далее выполняю настройку данных для теста. На рис. 5 видно, что создается простой класс TestCaseData, предназначенный для хранения идентификатора теста, входного значения и ожидаемого значения. Как я вскоре объясню, в рабочей ситуации можно было бы добавить также и другие поля, включая сведения о привязке службы WCF. Теперь данные для теста я встраиваю непосредственно в программу, создавая общий объект List и наполняя его объектами TestCaseData.
List<TestCaseData> testCases = new List<TestCaseData>();
testCases.Add(new TestCaseData("001", "Hello world",
"E6-76-0D-55-5C-32-F6-6F-5E-15-93-31-DB-20-FD-8E"));
testCases.Add(new TestCaseData("002", "Goodbye world",
"1A-05-3C-C0-4A-18-13-06-0E-AC-EA-BA-46-EC-CF-B1"));
testCases.Add(new TestCaseData("003", "",
"D4-1D-8C-D9-8F-00-B2-04-E9-80-09-98-EC-F8-42-7E"));
Данные для теста можно было бы настроить во внешнем хранилище, например файле XML или таблице SQL. Здесь я использую три варианта тестовых данных, но в реальной ситуации можно было бы использовать тысячи. Наиболее сложной частью тестирования программного обеспечения является определение подходящих входных тестовых данных, заставляющих работать тестируемую систему в полную силу, и определение ожидаемых результатов.
Теперь, хотя можно написать код клиента WCF с нуля, гораздо проще для генерации кода прокси и файла конфигурации WCF использовать программу командной строки svcutil.exe, поставляемую с Visual Studio 2008. В данном случае я запускаю командную оболочку Visual Studio (которой известно местоположение svcutil.exe) и ввожу команду.
> svcutil.exe http://localhost/WCF/CryptoHashService/Service.svc
Svcutil.exe, запускаемый на выполнение с местоположением службы WCF и без каких-либо дополнительных аргументов, считывает метаданные WCF из службы и генерирует два файла. Первый файл — CryptoHashService.cs (имя целевой службы WCF, наращенное расширением .cs), содержащий код прокси на C#, который позволит отправлять сообщения и получать сообщения от моей службы WCF. Второй файл — output.config, содержащий сведения о привязке WCF (транспортный протокол, значение времени ожидания, кодировку текста и т.д.), используемые целевой службой WCF. После генерирования этих двух файлов я щелкаю правой кнопкой мыши проект программы тестирования и добавляю к нему файл CryptoHashService.cs. Вместо этого можно было бы скопировать код и вставить его непосредственно в программу тестирования.
Теперь существуют два способа использовать сведения о привязке из файла output.config. Один способ заключается в переименовании файла в файл app.config и последующем добавлении этого файла в проект клиента. Второй подход – в том, чтобы исследовать данные из файла output.config и затем написать код, программным образом назначающий объекту привязки значения, показанные в файле output.config. При написании обычной клиентской программы WCF подход с использованием app.config обычно является более предпочтительным вариантом, чем программный подход. Поскольку файл app.config представляет собой обычный текст и читается клиентской программой во время выполнения, если изменяются сведения о привязке соответствующей службы WCF, можно изменить соответствующие сведения о привязке клиента. Это делается просто внесением изменений в файл app.config и не требует перекомпилирования клиента. Однако в конкретном случае клиентской программы тестирования WCF иногда правильнее указать сведения о привязке программным способом. Программный подход позволяет легко передавать сведения о привязке в виде части входных данных для тестирования. Здесь я использую программный подход. Я создаю экземпляр объекта WSHttpBinding.
WSHttpBinding wsb = new WSHttpBinding();
Теперь присваиваю значения свойству Name и разным свойствам, относящимся ко времени, объекта привязки.
wsb.Name = "WSHttpBinding_ICryptoHashService";
wsb.CloseTimeout = TimeSpan.Parse("00:01:00");
wsb.OpenTimeout = TimeSpan.Parse("00:01:00");
wsb.ReceiveTimeout = TimeSpan.Parse("00:10:00");
wsb.SendTimeout = TimeSpan.Parse("00:01:00");
Я определяю эти значения, визуально исследуя атрибуты записи о привязке в файле output.config file, который был сгенерирован программой svcutil.exe. В данном случае я использую все значения по умолчанию, сгенерированные Visual Studio, когда создавалась служба WCF. Оставшимся свойствам привязки значения присваиваются подобным же образом.
wsb.BypassProxyOnLocal = false;
wsb.TransactionFlow = false;
wsb.HostNameComparisonMode =
System.ServiceModel.HostNameComparisonMode.StrongWildcard;
wsb.MaxBufferPoolSize = 524288;
wsb.MaxReceivedMessageSize = 65536;
wsb.MessageEncoding =
System.ServiceModel.WSMessageEncoding.Text;
wsb.TextEncoding = System.Text.Encoding.UTF8;
wsb.UseDefaultWebProxy = true;
wsb.AllowCookies = false;
Теперь можно настраивать объект прокси.
string uri =
"http://vte014.vte.local/WCF/CryptoHashService/Service.svc";
EndpointAddress epa = new EndpointAddress(uri);
CryptoHashServiceClient c =
new CryptoHashServiceClient(wsb, epa);
Класс CryptoHashServiceClient определяется в автоматически сгенерированном файле CryptoHashService.cs, созданном с помощью svcutil.exe. Этот конструктор классов принимает объект привязки (только что настроенный программно) и объект EndPointAddress, который указывает на службу WCF. Теперь экземпляр объекта клиента создан, и можно перебирать все варианты тестов и работать с тестируемой службой WCF.
На рис. 6 просто отправляется на консоль сообщение об успехе или сбое каждого из вариантов теста. В рабочей среде потребовалось бы проделать больший объем работы — отслеживать общее число успешных и сбойных случаев, возможно, отправлять сообщения по электронной почте, если один или несколько тестов закончились сбоем, сохранять результаты во внешнем хранилище данных и т.д. Если используется Team Foundation Server, данной программой тестирования можно управлять с помощью методик из моей статьи о тестировании «Пользовательская автоматизация тестирования при помощи Team System» из номера «Выпуск» за 2008 г. (см. msdn.microsoft.com/magazine/cc164248).
Рис. 6 Отображение состояния для каждого случая тестирования
foreach (TestCaseData tcd in testCases)
{
Console.WriteLine("Case ID = " + tcd.caseID);
Console.WriteLine("Input = " + tcd.input);
Console.WriteLine("Expected = " + tcd.expected);string actual = c.GetCryptoHash(tcd.input);
Console.WriteLine("Actual = " + actual);
if (actual == tcd.expected)
Console.WriteLine("* Pass *");
else
Console.WriteLine("** FAIL **");
}
Дополнительные соображения
Методики, представленные в этом месяце, обеспечивают прочную основу для начала работы с основными задачами тестирования служб WCF. Однако существует много других аспектов тестирования WCF, которые будут обсуждаться в будущих статьях. Большая часть этих дополнительных тем, посвященных тестированию, стала возможной благодаря замечательной гибкости WCF. Например, WCF дает системам возможность использовать безопасность на транспортном уровне (с помощью HTTPS, например), а также на более низких уровнях. Хотя службы WCF могут использовать HTTP, WCF дает системам возможность устанавливать связь с помощью нескольких других механизмов, включая TCP и именованные каналы. Как было показано, службы WCF можно размещать в IIS, но их можно размещать и другими способами, включая использование служб Windows и самостоятельных управляемых приложений. Службы WCF могут поддерживать несколько конечных точек, имеющих разные адреса, привязки и контракты. WCF поддерживает обмен сообщениями типа запрос-ответ и обмен сообщениями дуплексного типа. Все эти возможности WCF и многие другие приводят к любопытным последствиям для всестороннего тестирования.
Случай основного тестирования функций WCF, представленный в данной статье, является только одной частью всестороннего тестирования WCF. В силу простоты моей фиктивной службы WCF криптографического хэша вся логика содержится в единственном методе GetCryptoHash. В более реалистичных случаях может появиться код, инкапсулирующий бизнес-логику и отдельный код, инкапсулирующий функциональные возможности службы. Данный подход позволяет тестировать бизнес-логику и службы по отдельности, что упрощает задачу тестирования.
При создании служб WCF с помощью Visual Studio Team System, можно воспользоваться преимуществом встроенной поддержки тестирования, если используется идеология разработки, ориентированная на тестирование. Можно также использовать служебную программу тестирования клиента WcfTestClient.exe, поставляемую с Visual Studio 2008, чтобы выполнять тестирование служб WCF вручную — в дополнение к автоматизированному тестированию, представленному в этой статье (см. статью, написанную моим коллегой по MSDN® Magazine Джувелом Лоуи (Juval Lowy) на веб-странице по адресу msdn.microsoft.com/magazine/cc163289). В добавление к чисто функциональному тестированию может также потребоваться выполнение тестирования нагрузки с помощью встроенных в Visual Studio инструментов тестирования нагрузки.
Автор: Джеймс МакКэффри (James McCaffrey)
Источник: http://msdn.microsoft.com/ru-ru/magazine/