Подтвердить что ты не робот

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;
            }
        }
     }
}

То, что я пробовал до сих пор:

  • OpenCover

со специальной тестовой dll, которая создала бы Host, как показано выше, но хост не может быть создан должным образом, поэтому тестирование не запускается на самом деле. Я получаю сообщение об ошибке "Хост в состоянии сбоя". Я выполнил этот мини-учебник. Несмотря на это, я получаю отчет о покрытии с расчетным охватом около 20%. Но сервис только начинается, он пока не работает.

  1. Инструменты производительности Visual Studio

Этапы, по существу, описаны в этой статье. Я получаю файл myproject.coverage, но я не могу его просмотреть, потому что у меня только VS Professional, охват, по-видимому, используется только в версиях Test Premium или Ultimate.

Кроме того, попробовав эти два, я приму любой ответ, показывающий, как его запустить и запустить с любым из них (предпочитаемый OpenCover).

Принять ответ, который показывает, как проверить эту настройку и получить покрытие кода при использовании инструментов для генерации большей части кода (как pex, но после пробного просмотра я вижу, что он не генерирует очень хороший код).

4b9b3361

Ответ 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";
    }
  }
}