c# wcf unit-testing integration-testing code-coverage

c# - Servicio WCF alojado en IIS: pruebas de integración y cobertura de código



unit-testing integration-testing (4)

Ayudaría a ver las operaciones del servicio.

Nunca intenté ejecutar una aplicación de "tipo de consola" bajo una herramienta de cobertura.

Yo sugeriría escribir una prueba con, digamos, NUnit (o cualquier otro marco de prueba de unidad; obviamente no es una prueba de unidad, pero la técnica encaja bastante bien).

En la prueba, abre el host del servicio, crea un cliente del servicio, le permite ejecutar algunas operaciones en su servicio y cierra el host del servicio.

Ejecute esta prueba con una herramienta de cobertura, y debería terminar.

Hice eso con NUnit y NCover hace aproximadamente 7 años, usando sus versiones actuales en ese momento (NCover era software libre, si lo recuerdo bien).

Para un proyecto he programado una biblioteca de servicios wcf. Se puede alojar en IIS y en un servicio auto hospedado.

Para todos los sistemas externos que están conectados, he proporcionado implementaciones simuladas que proporcionan algunos datos genéricos, por lo que el servicio (biblioteca) sigue funcionando y funcionando. Es una máquina clásica de autómatas / estados finitos.

Durante el arranque, todas las fuentes de datos están conectadas. En el modo de prueba, las implementaciones simuladas están conectadas. Entonces, cuando ejecuto pruebas, la biblioteca de servicios se "inicia" desde un servicio auto hospedado, no IIS y la máquina de estado continúa ejecutando y procesando paquetes de datos.

¿Hay alguna manera de obtener algún tipo de "cobertura de prueba" de tal ejecución?

Realmente apreciaría si pudiera decir qué rutas de código son afectadas por los datos de ejemplo que proporciono desde los objetos simulados. Y luego proporcionar más testdata para obtener una mayor cobertura.

Si pudiera hacer esto sin tener que proporcionar "un montón de código adicional de prueba", sería genial. Creo que muchos casos ya están cubiertos a partir de los datos proporcionados por los objetos simulados. Pero ahora mismo no tengo un punto de partida para eso.

Aquí hay algunos ejemplos de código para dar una imagen más clara de lo que significa. El código está fuertemente simplificado, por supuesto.

En una aplicación de consola muy simple para iniciar el servicio (versión auto hospedada)

static void Main(string[] args) { using (var host = new ServiceHost(typeof(MyServiceLib.Service.MyServiceLib))) { host.Open(); Console.ReadLine(); host.Close(); } }

En la biblioteca de servicios, se llama a un constructor desde ese código

public MyServiceLib() { Task.Factory.StartNew(this.Scaffold); }

Lo que no hace nada más que arrancar la máquina de estados.

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

Lo que he intentado hasta ahora:

  1. OpenCover

con una dll de prueba especial que crearía el Host como se muestra arriba, pero el host no se puede crear correctamente, por lo que la prueba no comienza realmente. Aparece un mensaje de error "El host está en estado de error". Seguí este mini-tutorial . A pesar de que recibo un informe de cobertura con una cobertura calculada de alrededor del 20%. Pero el servicio apenas está comenzando, no está haciendo ningún trabajo hasta ahora.

  1. Visual Studio Performance Tools

Los pasos se describen esencialmente en este artículo . Obtengo un archivo myproject.coverage, pero no puedo verlo, porque solo tengo un VS Professional, la cobertura parece ser solo de uso en las versiones Test Premium o Ultimate.

Además de haber probado esos dos, aceptaré cualquier respuesta que muestre cómo ponerlo en funcionamiento con cualquiera de ellos (se prefiere openCover).

Aceptará una respuesta que muestre cómo probar esta configuración y obtener una cobertura de código al tiempo que aprovecha las herramientas para generar la mayor parte del código (como lo haría pex, pero después de la prueba veo que no genera un código muy bueno).


Parece que con OpenCover realmente está obteniendo la cobertura, pero el servicio está entrando en estado Con Faulted , por lo que necesita detectar las fallas de su Proveedor de Servicio y dirigirse a eso.

Básicamente, necesitas algún tipo de registro de errores, y lo primero que intentaría es buscar en los registros de eventos del sistema (Win + R, eventvwr.msc, Enter).

También puede intentar escuchar los eventos con fallas en su ServiceHost:

host.Faulted += new EventHandler(host_faulted);

Aquí está el enlace a otra respuesta de SO que aborda este problema: Cómo averiguar la razón del evento ServiceHost Faulted


Sugeriría probar su lógica de negocios y no el código de arranque. Me refiero a probar la clase DataManager y no el alojamiento y el código de inicialización. Puede escribir una prueba de unidad, utilizando uno de los marcos de prueba de unidad, por ejemplo, NUnit. Luego puede ejecutar sus pruebas en Visual Studio con Resharper Ultimate o en su herramienta de integración continua con cobertura de código, como OpenCover o dotCover para obtener la cobertura de su código.

[TestFixture] public class DataManagerTests { [Test] public void Process_Single_Processed() { // Arrange IData data = new SingleData(); DataManager dataManager = new DataManager(); // Act dataManager.Process(data); // Assert // check data processed correctly } }


para permitir que su Unidad-Test-Framework determine la cobertura que tiene para alojar el servicio dentro del "corredor" del framework (también conocido como el proceso que está ejecutando las pruebas). La cobertura se calcula por medio del "corredor", lo que significa que no puede obtener cobertura si el servicio se encuentra en otro lugar. A continuación voy a agregar un ejemplo de cómo hacer esto.

Saludos 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''s 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"; } } }