IIS размещал службу WCF: тесты интеграции и охват кода
Для проекта я запрограммировал библиотеку сервиса wcf. Он может быть размещен в IIS и в самообслуживании.
Для всех внешних систем, которые подключены, я предоставил Mock-реализации, которые дают некоторые общие данные, поэтому такая служба (библиотека) продолжает работать и работать. Это классический автомат/машина конечного состояния.
Во время начальной загрузки все источники данных подключены. В режиме тестирования подключены макетные реализации. Поэтому, когда я запускаю тесты, служебная библиотека "запускается" из самообслуживаемой службы, а не IIS, а конечный автомат продолжает работать и обрабатывать пакеты данных.
Есть ли способ получить какое-то "тестовое покрытие" от такого запуска.
Я был бы очень признателен, если бы я мог определить, какие кодовые пути попадают в данные примера, которые я предоставляю от макетных объектов. А затем предоставить больше тестовых данных, чтобы получить более высокий охват.
Если бы я мог сделать это без необходимости предоставления "большого количества" тестового кода, было бы здорово. Я думаю, что многие случаи уже покрыты данными, полученными от макетов. Но сейчас у меня нет отправной точки для этого.
Вот несколько примеров кода, чтобы дать более четкое представление о том, что имеется в виду. Конечно, код сильно упрощается.
В очень простом консольном приложении для запуска службы (самообслуживаемая версия)
static void Main(string[] args)
{
using (var host = new ServiceHost(typeof(MyServiceLib.Service.MyServiceLib)))
{
host.Open();
Console.ReadLine();
host.Close();
}
}
В библиотеке сервисов из этого кода вызывается конструктор
public MyServiceLib()
{
Task.Factory.StartNew(this.Scaffold);
}
Это не что иное, как запуск конечного автомата
private void Scaffold()
{
// lots of code deleted for simplicity reasons
var dataSource = new MockDataSource();
// inject the mocked datasource
this.dataManager = new DataManager(dataSource);
// this runs in its own thread. There are parts that are started on a timer event.
this.dataManager.Start();
}
public class DataManager : IDataManager
{
public void Start()
{
while (this.IsRunning)
{
var data = this.dataSource.getNext();
if (data != null)
{
// do some work with the data retrieved
// lots of code paths will be hit from that
this.Process(data);
}
else
{
Thread.Sleep(1000);
}
}
}
public void Process(IData data)
{
switch (data.PackageType)
{
case EnumPackageType.Single:
{
ProcessSingle(data);
break;
}
case EnumPackageType.Multiple:
{
ProcessMultiple(data);
break;
}
// here are lots of cases
default:
{
Logger.Error("unknown package type");
break;
}
}
}
}
То, что я пробовал до сих пор:
со специальной тестовой dll, которая создала бы Host, как показано выше, но хост не может быть создан должным образом, поэтому тестирование не запускается на самом деле. Я получаю сообщение об ошибке "Хост в состоянии сбоя". Я выполнил этот мини-учебник. Несмотря на это, я получаю отчет о покрытии с расчетным охватом около 20%. Но сервис только начинается, он пока не работает.
- Инструменты производительности Visual Studio
Этапы, по существу, описаны в этой статье. Я получаю файл myproject.coverage, но я не могу его просмотреть, потому что у меня только VS Professional, охват, по-видимому, используется только в версиях Test Premium или Ultimate.
Кроме того, попробовав эти два, я приму любой ответ, показывающий, как его запустить и запустить с любым из них (предпочитаемый OpenCover).
Принять ответ, который показывает, как проверить эту настройку и получить покрытие кода при использовании инструментов для генерации большей части кода (как pex, но после пробного просмотра я вижу, что он не генерирует очень хороший код).
Ответы
Ответ 1
Это поможет увидеть действия службы.
Я никогда не пробовал запускать такое приложение "консольного типа" в рамках инструмента покрытия.
Я бы предложил написать тест, чтобы сказать, что NUnit (или любой другой модуль тестирования модулей, это не unit test, очевидно, но техника подходит вполне хорошо).
В тесте вы открываете хост службы, создаете клиента службы, позволяете клиенту выполнять некоторые операции с вашей службой и закрывать хост службы.
Запустите этот тест под инструментом охвата, и вам нужно сделать это.
Я сделал это с NUnit и NCover около 7 лет назад, используя их текущие версии в то время (NCover был свободным программным обеспечением, если я его правильно помню).
Ответ 2
Похоже, что с OpenCover вы действительно получаете покрытие, но служба входит в состояние Faulted
, поэтому вам нужно поймать ошибки из вашего ServiceHost и адреса этого.
В принципе, вам нужен какой-то журнал ошибок, и первое, что я попробую, это посмотреть в системных журналах событий (Win + R, eventvwr.msc, Enter).
Вы также можете попробовать прослушать события Faulted на вашем ServiceHost:
host.Faulted += new EventHandler(host_faulted);
Вот ссылка на другой ответ SO, решая эту проблему:
Как узнать причину события ServiceHost Faulted
Ответ 3
чтобы ваша Unit-Test-Framework определила покрытие, которое вы должны разместить в сервисе внутри "бегуна" фреймворка (иначе процесс, выполняющий тесты).
Покрытие рассчитывается с помощью "бегуна" и означает, что вы не можете получить покрытие, если служба размещена где-нибудь еще.
Ниже я добавлю пример, как это сделать.
Привет
Juy Juka
namespace ConsoleApplication4
{
using System.ServiceModel; // Don't forgett to add System.ServiceModel as Reference to the Project.
public class Program
{
static void Main(string[] args)
{
string arg = ((args != null && args.Length > decimal.Zero ? args[(int)decimal.Zero] : null) ?? string.Empty).ToLower(); // This is only reading the input for the example application, see also end of Main method.
string randomUrl = "net.tcp://localhost:60" + new System.Random().Next(1, 100) + "/rnd" + new System.Random().Next(); // random URL to allow multiple instances parallel (for example in Unit-Tests). // Better way?
if (arg.StartsWith("t"))
{
// this part could be written as a UnitTest and should be
string result = null;
using (ServiceHost host = new ServiceHost(typeof(MyService)))
{
host.AddServiceEndpoint(typeof(IMyService), new NetTcpBinding(), randomUrl);
host.Open();
IMyService instance = ChannelFactory<IMyService>.CreateChannel(new NetTcpBinding(), new EndpointAddress(randomUrl), null);
result = instance.GetIdentity();
host.Close();
}
// Assert.Equals(result,"Juy Juka");
}
else if (arg.StartsWith("s"))
{
// This part runs the service and provides it to the outside. Just to show that it is a real and working host. (and not only working in a Unit-Test)
using (ServiceHost host = new ServiceHost(typeof(MyService)))
{
host.AddServiceEndpoint(typeof(IMyService), new NetTcpBinding(), randomUrl);
host.Open();
System.Console.Out.WriteLine("Service hosted under following URL. Terminate with ENTER.");
System.Console.Out.WriteLine(randomUrl);
System.Console.In.ReadLine();
host.Close();
}
}
else if (arg.StartsWith("c"))
{
// This part consumes a service that is run/hosted outoside of the application. Just to show that it is a real and working host. (and not only working in a Unit-Test)
System.Console.Out.WriteLine("Please enter URL of the Service. Execute GetIdentity with ENTER. Terminate with ENTER.");
IMyService instance = ChannelFactory<IMyService>.CreateChannel(new NetTcpBinding(), new EndpointAddress(System.Console.In.ReadLine()), null);
System.Console.Out.WriteLine(instance.GetIdentity());
System.Console.In.ReadLine();
}
else
{
// This is only to explain the example application here.
System.Console.Out.WriteLine("I don't understand? Please use one of the following (Terminate this instance with ENTER):");
System.Console.Out.WriteLine("t: To host and call the service at once, like in a UnitTest.");
System.Console.Out.WriteLine("s: To host the servic, waiting for clients.");
System.Console.Out.WriteLine("c: To contact a hosted service and display it GetIdenttity result.");
System.Console.In.ReadLine();
}
}
}
// Declaration and Implementation of the Service
[ServiceContract]
public interface IMyService
{
[OperationContract]
string GetIdentity();
}
public class MyService : IMyService
{
public string GetIdentity()
{
return "Juy Juka";
}
}
}