Cómo trazar el límite entre pruebas unitarias y pruebas de integración
· Actualizado el: · Go Komura · Pruebas, Pruebas unitarias, Pruebas de integración, Diseño de pruebas, Desarrollo en Windows, C# / .NET
En el diseño de pruebas, lo que resulta discretamente difícil una y otra vez es decidir hasta dónde llevar las pruebas unitarias y a partir de dónde subir a las pruebas de integración.
Lo peligroso aquí son estos dos extremos:
- Convertir todo en pruebas unitarias porque se quiere que corran rápido
- Convertir todo en pruebas de integración porque se parecen más a lo real
El primero termina lleno de mocks y hace fácil pasar por alto los puntos que fallan en producción; el segundo tiende a producir un conjunto de pruebas lento y frágil. En la práctica, los ejes que hay que observar son bastante más claros.
- ¿Lo que se quiere confirmar es la lógica propia o la conexión con el exterior?
- ¿El significado se mantiene aunque se sustituya por un fake en memoria (in-memory)?
- ¿El tema central es el comportamiento de la base de datos, los archivos, HTTP, el DI, la configuración, el framework o el sistema operativo?
- ¿Se quiere ejecutar rápidamente una gran cantidad de patrones de entrada?
Cuando estos cuatro puntos quedan claros, resulta mucho más fácil trazar el límite entre pruebas unitarias y pruebas de integración.
Este artículo está escrito partiendo del diseño de pruebas automatizadas en C# / .NET. Los ejemplos de código usan xUnit, pero el criterio de decisión en sí no depende del framework. Está planteado de forma que se pueda trasladar directamente a JUnit o a pytest.
El contenido se basa en «Integration tests in ASP.NET Core» de Microsoft Learn 1, «Unit testing best practices for .NET», también de Microsoft Learn 2, y «The Practical Test Pyramid» de Martin Fowler 3, todos ellos accesibles a fecha de marzo de 2026. Para que las referencias no queden reducidas a simples números, en el cuerpo del artículo se mencionará también el nombre de la fuente. Las URL de los originales están recogidas en las referencias al final del artículo.
1. Primero, la conclusión
Dicho de forma bastante simplificada, pero útil para el trabajo diario, sería así:
- La lógica pura va en pruebas unitarias
- La conexión, el cableado, la conversión y las diferencias de entorno van en pruebas de integración
- Si algo se puede verificar con cualquiera de las dos, empiece por la prueba unitaria
- Es mejor que la prueba de integración acote el límite en lugar de volverse amplia y pesada
En una frase: la prueba unitaria es la «prueba del juicio» y la prueba de integración es la «prueba de la conexión».
Las cosas cuyo sentido queda completo sin recursos externos —como el cálculo de importes, las transiciones de estado, la validación de entradas, las condiciones de aprobación o la clasificación de excepciones— conviene llevarlas hacia las pruebas unitarias: son más rápidas, se rompen menos y permiten cubrir a fondo los patrones de entrada. Por otro lado, lo que «traiciona en el instante en que se conecta» —la ejecución de SQL, la serialización de JSON / CSV, el enrutamiento, el model binding, el registro en el DI, el bloqueo de archivos, los permisos, el registro de COM, 32 bits / 64 bits, STA / MTA— es más seguro dejarlo del lado de las pruebas de integración.
En Integration tests in ASP.NET Core de Microsoft Learn también se plantea que las pruebas de integración deben limitarse a escenarios de infraestructura importantes, y que conviene elegir la prueba unitaria siempre que sea suficiente.
2. Qué entendemos aquí por pruebas unitarias y de integración
Aquí distinguimos los términos de la siguiente manera.
| Nivel | Qué se confirma | Configuración típica |
|---|---|---|
| Prueba unitaria | La corrección de una única responsabilidad aislada | Usa fake / mock / stub y corta los recursos externos |
| Prueba de integración | La conexión entre varios componentes y el comportamiento que incluye infraestructura y framework | Base de datos real, archivos reales, serializer real, host real, pipeline real, etc. |
| E2E / prueba funcional | El flujo de usuario de la aplicación completa | Aplicación ya desplegada, varios servicios, navegador real o procesos reales |
En la guía de pruebas unitarias de .NET se explica que una buena prueba unitaria es fast / isolated / repeatable (rápida, aislada y repetible) y no depende de factores externos como el sistema de archivos o la base de datos. Para más detalle, Unit testing best practices for .NET resulta muy claro.
Además, la prueba de integración no se refiere únicamente a «una prueba pesada que obligatoriamente usa otro proceso u otro servidor». Incluso dentro del mismo proceso, si se conectan varios componentes reales y se confirma el comportamiento real del framework o de la infraestructura, eso ya se acerca a una prueba de integración.
Por ejemplo, al hacer una prueba unitaria de una controller action de ASP.NET Core, la documentación oficial plantea limitar el objetivo al juicio del propio cuerpo de la action, y tratar en pruebas de integración las interacciones del lado del framework como routing, model binding o filters. Para más detalle, resulta útil consultar Unit test controller logic in ASP.NET Core.
2.1. La diferencia entre fake, mock y stub
En la tabla anterior aparecen juntos fake, mock y stub, pero estos tres no son lo mismo. Como clasificación de los test doubles (los «dobles» que se usan en las pruebas), en este artículo se emplean con el siguiente sentido, siguiendo la distinción que hace Martin Fowler en «Mocks Aren’t Stubs». 4
| Nombre | Qué hace | Uso típico |
|---|---|---|
| stub | Un doble que solo devuelve un valor fijo. No verifica cómo se lo llama | Cuando se quiere fijar una condición de entrada, como «el stock siempre es de 3 unidades» |
| mock | Un doble que verifica la propia forma en que se lo llama. Hace assert sobre el número de llamadas y los argumentos | Cuando se quiere confirmar algo como «que el guardado se llame exactamente una vez» |
| fake | Una implementación ligera que reproduce el mismo comportamiento que lo real | Un repositorio en memoria, un almacén de archivos en un directorio temporal, etc. |
En resumen: el stub sustituye la entrada, el mock verifica la llamada y el fake es una implementación simplificada. Esta distinción resulta relevante en el capítulo 4. El síntoma de «hacen falta 7 mocks» significa, con precisión, que hay 7 objetivos de verificación de llamadas, y es una señal de que esa prueba no está confirmando un único juicio, sino la forma en que se conectan varios componentes.
2.2. Cómo se trata el E2E en este artículo
El tema de este artículo es, ante todo, el límite entre las pruebas unitarias y las pruebas de integración. El E2E / prueba funcional se incluye en la tabla anterior solo a efectos comparativos, pero no se trata en profundidad en el cuerpo del texto.
Solo para dejar clara su posición: en la idea de la pirámide de pruebas, se forma un triángulo en el que cuanto más abajo está la capa, más numerosas y rápidas son las pruebas, y cuanto más arriba está, menos numerosas y más lentas son. «The Practical Test Pyramid» de Martin Fowler es el punto de partida de este planteamiento. 3 Es decir, la proporción consiste en tomar las pruebas unitarias como base, colocar pruebas de integración en cada límite y limitar el E2E a los flujos principales. La forma concreta de distribuirlas se resume en la estructura de 3 capas del capítulo 7.
3. La tabla de decisión de un vistazo
Empecemos por la tabla más útil para el trabajo diario.
| Lo que se quiere confirmar | Prueba principal | Nota |
|---|---|---|
| Cálculo de importes, descuentos, transiciones de estado, validación de entradas | Prueba unitaria | Se quiere cubrir a fondo los patrones de entrada |
| Clasificación de excepciones, selección del mensaje de error, decisión de si reintentar | Prueba unitaria | El sentido queda completo sin I/O real |
| SQL / conversión ORM del repository, transacciones | Prueba de integración | El tema central es el comportamiento de la base de datos real o del provider real |
| Serialización / deserialización de JSON / XML / CSV | Prueba de integración | Un fake difícilmente detecta desajustes en el wire format |
| Enrutamiento, model binding, filtros, middleware | Prueba de integración | Confirmación de la conexión con el framework |
| Transiciones de estado del ViewModel o el Presenter en WPF / WinForms | Prueba unitaria | Tiene sentido aunque no se levante la UI |
| El Binding real, el Dispatcher, el ciclo de vida de los controles, el message loop | Prueba de integración o prueba de UI | El tema central es el comportamiento del framework y de los hilos |
| Rutas de archivo, permisos, bloqueos, carpetas compartidas, códigos de salto de línea, codificación de caracteres | Prueba de integración | Se necesita el comportamiento real del sistema operativo y del sistema de archivos |
| Registro de COM, 32 bits / 64 bits, STA / MTA, carga de DLL | Prueba de integración | El tema central son las diferencias de entorno y los límites entre procesos |
| Arranque de la aplicación completa, verificación de extremo a extremo de los casos de uso principales | E2E / smoke | Basta con pocas pruebas |
La clave para leer la tabla es preguntarse qué prueba está más cerca del «motivo por el que algo se rompe en producción». Decidir en función de la incertidumbre que se quiere reducir, en lugar de la ubicación del código, da resultados más estables.
4. Qué debe cubrir la prueba unitaria
Lo que encaja en la prueba unitaria es la responsabilidad cuyo sentido se mantiene aunque se elimine el mundo exterior.
Por ejemplo:
- Reglas de negocio
- Ramificaciones
- Transiciones de estado
- Validación de entradas
- Clasificación de errores
- Decisión de la política de reintentos
- Cambios de estado del ViewModel / Presenter
- La propia lógica de conversión
En particular, cuanto mayor sea el número de combinaciones, mayor es el valor de llevarlo hacia la prueba unitaria.
Por ejemplo:
- Con cupón / sin cupón
- Con stock / sin stock
- Primer pedido / pedido recurrente
- Administrador / usuario general
- Valor normal / valor límite / valor inválido
Como en estos casos, cuantas más condiciones de ramificación haya, más pesado resulta cubrirlas todas con pruebas de integración. Aquí es más razonable desglosarlas con detalle mediante pruebas unitarias.
Además, en las pruebas unitarias es importante mantener los factores externos bajo control.
- Inyectar la hora actual
- Hacer que el GUID y los números aleatorios se puedan sustituir
- No esperar con sleep
- No tocar la base de datos ni los archivos reales
- No salir a la red real
Cuando se respeta esto, las pruebas se vuelven bastante estables.
4.1. Cuando los mocks se multiplican en una prueba unitaria
Al intentar escribir una prueba unitaria, si se llega a esta situación:
- Hacen falta 7 mocks
- El setup es largo
- El arrange es más largo que el propio cuerpo
- No se ve qué se quiere confirmar
suele deberse a una de estas dos causas:
- Esa clase tiene demasiadas responsabilidades
- Se está forzando dentro de la prueba unitaria un cableado que en realidad debería confirmarse con una prueba de integración
El mock es una herramienta para cortar el mundo exterior, no una herramienta para demostrar que la conexión con lo real es correcta. Si se confunde esto, es fácil terminar en la situación de «todo está en verde, pero falla en producción».
4.2. Un ejemplo mínimo de prueba unitaria
Si nos quedamos solo en palabras, la explicación resulta abstracta, así que vamos a poner un ejemplo de código para cada lado del límite. Empecemos por el lado de la prueba unitaria. La transición de estado del ViewModel tiene sentido completo aunque no se levante la UI, así que este es el territorio de la prueba unitaria.
// .NET 8 / xUnit
// Objetivo: un ViewModel que no toca ningún recurso externo
public sealed class OrderViewModel
{
public decimal Subtotal { get; set; }
public bool IsMember { get; set; }
public bool CanCheckout => Subtotal > 0m;
public decimal Total => IsMember ? Subtotal * 0.9m : Subtotal;
}
public class OrderViewModelTests
{
[Fact]
public void 会員なら1割引きになる()
{
var viewModel = new OrderViewModel { Subtotal = 1000m, IsMember = true };
Assert.Equal(900m, viewModel.Total);
}
[Theory]
[InlineData(0, false)]
[InlineData(1, true)]
public void 小計が0なら確定できない(int subtotal, bool expected)
{
var viewModel = new OrderViewModel { Subtotal = subtotal };
Assert.Equal(expected, viewModel.CanCheckout);
}
}
Aquí no aparecen ni base de datos, ni archivos, ni HTTP. Por eso es rápida, se puede ejecutar en paralelo y con [Theory] se pueden añadir tantos patrones de entrada como se quiera.
Llevar aquí el barrido exhaustivo de ramificaciones se refiere, en concreto, a esta forma de trabajar.
5. Los 4 límites que se suben a la prueba de integración
Los lugares que conviene subir a la prueba de integración se pueden organizar, a grandes rasgos, en cuatro: el formato, el cableado, el entorno y el tiempo.
5.1. El límite del formato
En lo que aquí llamamos formato entra, entre otras cosas:
- JSON / XML / CSV
- El schema y el mapping de la base de datos
- nullable / precision / timezone
- La serialización de enum y de fechas
- La codificación de caracteres y el BOM
- El código de salto de línea
Martin Fowler también señala que el límite en el que interviene serialize / deserialize es candidato a prueba de integración. Para más detalle, resulta útil consultar The Practical Test Pyramid.
Por ejemplo:
- Al convertir un DTO a JSON, el nombre del campo resultó distinto
- Las comillas o los saltos de línea del CSV se rompieron
- El
decimalse redondeó - El tratamiento de
DateTimeOffsetse desajustó en la base de datos - El tratamiento de
nully de la cadena vacía no fue el esperado
Fallos como estos se escapan fácilmente si solo se usan pruebas unitarias.
5.2. El límite del cableado
En el límite del cableado entran, por ejemplo, partes como estas:
- El registro en el DI
- El bind de la configuración
- El enrutamiento
- El model binding
- Los filtros
- middleware
- El arranque del host
- El cableado de eventos
- El Binding de WPF o la conexión de comandos
Aquí el tema central no es «si mi propia función es correcta», sino si varios componentes reales están correctamente conectados.
En ASP.NET Core existe un planteamiento oficial según el cual la prueba unitaria de una controller action se limita al juicio de la action, mientras que routing, model binding y filters se revisan del lado de la prueba de integración.
La idea es la misma aunque no sea para web: en una aplicación de escritorio, la transición de estado del ViewModel es prueba unitaria, y el comportamiento que incluye el Binding real de XAML o el Dispatcher se acerca a la prueba de integración.
5.3. El límite del entorno
En el desarrollo para Windows, este punto es bastante importante.
- Permisos de archivo
- Carpetas compartidas
- Bloqueo de archivos
- El rename desde un archivo temporal
- Privilegios de administrador
- Permisos de arranque de servicios
- Registro de COM
- 32 bits / 64 bits
- STA / MTA
- El origen de carga de las DLL
En estos casos, el protagonista es la propia condición del sistema operativo o del entorno de ejecución. Con un fake en memoria el sentido se pierde bastante, así que es más seguro cubrirlos con pruebas de integración.
En particular, en configuraciones que incluyen software Windows ya existente o COM / ActiveX, es habitual tropezar antes por el registro, la bitness, el modelo de hilos o los permisos que por la propia lógica. Este tipo de fallos es terreno de la prueba de integración que incluye el entorno, no de la prueba unitaria.
5.4. El límite del tiempo
Otro punto que se pasa por alto fácilmente es el tiempo y la concurrencia.
- timeout
- cancellation
- El comportamiento real del retry
- Lo impulsado por timer
- La detención del procesamiento en segundo plano
- race condition
- El orden de cierre durante el shutdown
Lo importante aquí es separar el juicio del comportamiento real.
Por ejemplo:
- Hasta cuántas veces reintentar
- Qué excepciones son objeto de retry
son suficientes con la prueba unitaria. Por otro lado:
- Si el timeout realmente surte efecto
- Si la cancellation se propaga
- Si no se rompe cuando el timer choca con el procesamiento asíncrono
- Si al finalizar los handles y las tareas se cierran limpiamente
se acercan más a la prueba de integración.
5.5. Un ejemplo mínimo de prueba de integración
Con el mismo tema del apartado 4.2, vamos a escribir el otro lado del límite. Lo que se quiere confirmar aquí es si «el importe guardado no se rompe al ir y volver por la base de datos». Se pasa por un archivo SQLite real.
// .NET 8 / xUnit / Microsoft.Data.Sqlite
using System.Globalization;
using Microsoft.Data.Sqlite;
public sealed class OrderRepository(SqliteConnection connection)
{
public void Save(int id, decimal total)
{
using var command = connection.CreateCommand();
command.CommandText = "INSERT INTO orders (id, total) VALUES ($id, $total);";
command.Parameters.AddWithValue("$id", id);
command.Parameters.AddWithValue("$total", total.ToString(CultureInfo.InvariantCulture));
command.ExecuteNonQuery();
}
public decimal FindTotal(int id)
{
using var command = connection.CreateCommand();
command.CommandText = "SELECT total FROM orders WHERE id = $id;";
command.Parameters.AddWithValue("$id", id);
var stored = (string)command.ExecuteScalar()!;
return decimal.Parse(stored, CultureInfo.InvariantCulture);
}
}
public sealed class OrderRepositoryTests : IDisposable
{
private readonly string _databasePath =
Path.Combine(Path.GetTempPath(), $"orders-{Guid.NewGuid():N}.db");
private readonly SqliteConnection _connection;
public OrderRepositoryTests()
{
// Si no se deja Pooling=False, a veces no se puede borrar el archivo db al limpiar
_connection = new SqliteConnection($"Data Source={_databasePath};Pooling=False");
_connection.Open();
using var create = _connection.CreateCommand();
create.CommandText = "CREATE TABLE orders (id INTEGER PRIMARY KEY, total TEXT NOT NULL);";
create.ExecuteNonQuery();
}
[Fact]
public void 保存した金額が丸められずに読み戻せる()
{
var repository = new OrderRepository(_connection);
repository.Save(id: 1, total: 1234.56m);
Assert.Equal(1234.56m, repository.FindTotal(1));
}
public void Dispose()
{
_connection.Dispose();
File.Delete(_databasePath);
}
}
Lo que confirma esta prueba no son las ramificaciones de OrderRepository. Es la parte de la conexión: si el SQL se ejecuta, si el tipo de la columna corresponde correctamente con decimal, y si el valor se conserva al ir y volver. Como SQLite no tiene un tipo decimal, la pregunta de «con qué tipo hay que guardar para que no se rompa al ir y volver» deja de ser un problema de implementación y pasa a ser un problema de diseño de la conexión. Esto no se ve con un fake en memoria.
En este ejemplo se crea un archivo de base de datos temporal por cada clase de prueba y se borra en Dispose. Como la prueba de integración conserva estado, dejar claro cada vez dónde se crea y dónde se descarta es más importante que en la prueba unitaria.
6. Errores de criterio habituales
6.1. Conformarse con hacer mock del repository
Aunque se cubra todo lo relacionado con el repository con mocks, no se sabe:
- Si el SQL es correcto
- Si la transacción surte efecto
- Si coincide con el schema
- Si el mapping no se desajusta
- Si la codificación de caracteres o la precisión no se rompen
El repository suele ser, más que objeto de prueba de lógica, un punto de conexión del límite. En ese caso, es más realista aumentar el peso de la prueba de integración frente a la unitaria.
6.2. Intentar cubrir también el framework en la prueba unitaria de un controller o endpoint
Lo que se quiere observar en la prueba unitaria de una controller action es, más o menos:
- Las ramificaciones condicionales
- La selección del valor de retorno
- La forma de distinguir la llamada a los servicios dependientes
Por otro lado:
- Si la route coincide
- Si el model binding se completa
- Si el filter surte efecto
- Cómo se ve el resultado al pasar por el middleware
corresponde al lado de la prueba de integración. Si se mezclan, resulta difícil saber qué se rompió.
6.3. Barrer todos los patrones de entrada con pruebas de integración
La prueba de integración, precisamente por parecerse a lo real, resulta inevitablemente lenta. Por eso conviene separar: el barrido exhaustivo de ramificaciones va en la prueba unitaria, y los casos representativos del límite van en la prueba de integración.
En la explicación de pruebas de integración de Microsoft Learn también se recomienda, para la base de datos y el sistema de archivos, no ejecutar todos los patrones con pruebas de integración, sino limitarse a escenarios representativos como read / write / update / delete.
6.4. Golpear directamente el entorno de producción de un servicio externo desde CI
Esto es mejor evitarlo por seguridad.
En la prueba de integración importa el «parecido a lo real», pero eso no significa que haya que golpear cada vez el SaaS o la API de producción. Fowler también recomienda levantar los servicios externos en local, colocar un fake, o usar una test instance dedicada.
En la práctica, resulta manejable combinar:
- Base de datos local
- Directorio temporal
- test host
- Un test environment dedicado
- Un fake service con el contrato fijado
7. Estructura recomendada para el trabajo diario
No existe una proporción absolutamente correcta. Sin embargo, las siguientes 3 capas se pueden usar de forma bastante general.
| Capa | Predominante | Qué se coloca |
|---|---|---|
| Capa núcleo | Pruebas unitarias en profundidad | Reglas de negocio, transiciones de estado, validación de entradas, clasificación de errores |
| Capa de límites | Pruebas de integración acotadas | Base de datos, archivos, HTTP, serializer, DI, configuración, COM, permisos |
| Capa global | Un número reducido de smoke / E2E | Verificación de arranque, flujos principales, prevención de la repetición de fallos graves |
En sensación general, lo que se hace grueso en cantidad es la prueba unitaria, y lo que se hace grueso en densidad de límites es la prueba de integración.
El procedimiento recomendado es este:
- Primero, enumerar los límites de la aplicación
- Llevar la lógica hacia una forma que se pueda separar del exterior
- Colocar, en cada límite, «al menos un happy path» y un «failure path representativo»
- Limitar el número de pruebas de extremo a extremo
- Cuando aparezca un bug, añadir la prueba en la capa donde ese bug se pueda reproducir con el menor coste
El punto 5, el último, es el importante.
- Si es un error de reglas, añadir una prueba unitaria
- Si es un error de SQL / binding / configuración / permisos / registro, añadir una prueba de integración
- Si es un fallo que incluye el arranque o la distribución, añadir smoke o E2E
Si se añaden pruebas de esta manera, la responsabilidad de cada prueba se mantiene estable.
8. Las 5 preguntas finales para cuando hay dudas
Por último, resumimos 5 preguntas para usar como comprobación cuando surjan dudas.
- ¿El sentido que se quiere confirmar se mantiene aunque se sustituya por un fake en memoria (in-memory)?
- Si se mantiene, se acerca a la prueba unitaria.
- Cuando algo se rompe, ¿lo que se sospecha no es la lógica sino la conexión o la configuración?
- Si es así, se acerca a la prueba de integración.
- ¿El tema central no es la base de datos, los archivos, el serializer, el DI, la route, el model binding, el sistema operativo, los permisos, la bitness o los hilos?
- Si es así, se acerca a la prueba de integración.
- ¿Se quiere ejecutar rápidamente una gran cantidad de patrones de entrada?
- Si es así, se acerca a la prueba unitaria.
- Cuando esa prueba falla, ¿se sabe de inmediato qué hay que arreglar?
- Si no se sabe, las capas de la prueba están mezcladas.
Ordenando con estas 5 preguntas resulta más fácil evitar decisiones vagas del tipo «prueba de integración porque se parece un poco más a lo real» o «prueba unitaria porque es un poco más rápida».
9. Resumen
Lo más práctico es decidir el límite entre pruebas unitarias y pruebas de integración no por la ubicación del código, sino por qué incertidumbre se quiere reducir.
Los puntos clave se reducen a estos 5:
- La prueba unitaria es la prueba del juicio
- La prueba de integración es la prueba de la conexión
- El barrido exhaustivo de ramificaciones va en la prueba unitaria
- El formato, el cableado, el entorno y el tiempo van en la prueba de integración
- La verificación de extremo a extremo se cubre con un número reducido de smoke / E2E
Lo que más conviene evitar es:
- Creer que un mock ya demuestra también la conexión con lo real
- Intentar cubrir todas las ramificaciones con pruebas de integración
- Mezclar la responsabilidad de la prueba unitaria con la de la prueba de integración
Si tiene dudas, observe antes que nada si ese fallo rompe el «juicio» o rompe la «conexión». Con esta única pregunta se pueden ordenar bastantes casos.
10. Artículos relacionados
- Lista de comprobación para mantener la seguridad mínima en el desarrollo de aplicaciones de Windows
- Hasta dónde es realmente posible convertir una aplicación de Windows en un binario único
- Cuándo se necesitan realmente los privilegios de administrador en Windows
- Qué es Reg-Free COM
11. Referencias
-
Microsoft Learn, Integration tests in ASP.NET Core ↩
-
Microsoft Learn, Unit testing best practices for .NET ↩
-
Martin Fowler, The Practical Test Pyramid ↩ ↩2
-
Martin Fowler, Mocks Aren’t Stubs ↩
Artículos relacionados
Artículos recientes con las mismas etiquetas para profundizar en temas cercanos.
Requisitos mínimos de un logger propio y checklist de pruebas de integración
Para que el log de diagnóstico de una aplicación propia sea confiable, organizamos UTF-8 JSON Lines, los campos obligatorios, flush, rota...
Cómo acelerar la validación de aplicaciones con Windows Sandbox
Cómo usar Windows Sandbox para aislar problemas de permisos de administrador, reproducir entornos limpios y simular falta de permisos o d...
Tabla de decisión: terminar o continuar ante una excepción inesperada
Analiza si una aplicación debe terminar o continuar tras una excepción inesperada, según el daño al estado, los efectos externos, los hil...
Cómo funcionan el portapapeles y arrastrar y soltar — Gestionar correctamente la transferencia de datos OLE en aplicaciones empresariales
Una tabla de Excel se deforma al pegarla y deja de poder pegarse si cierra el origen: es el portapapeles colocando el mismo contenido en ...
WPR/WPA en la práctica — Introducción al análisis de rendimiento del sistema para «todo el PC va lento»
Problemas como «todo el PC va lento» o «el arranque es lento», que el Administrador de tareas no explica, se investigan con WPR/WPA leyen...
Temas relacionados
Estas páginas sitúan el tema en un contexto más amplio de servicios y decisiones.
Temas técnicos de Windows
Portal sobre desarrollo de Windows, investigación de fallos y aprovechamiento de activos existentes.
Servicios relacionados con este tema
El artículo está directamente relacionado con los siguientes servicios.
Consultoría técnica y revisión de diseño
Porque decidir dónde trazar el límite entre pruebas unitarias y pruebas de integración es un tema que se ordena bien como consultoría de revisión de diseño o de estrategia de pruebas antes de la implementación.
Desarrollo de aplicaciones para Windows
Porque en las aplicaciones de Windows los límites de archivos, permisos, COM y 32 bits / 64 bits repercuten directamente en la capa de pruebas, lo que encaja bien con la definición de la estrategia de implementación.
Preguntas frecuentes
Preguntas habituales en las consultas sobre el tema del artículo.
- ¿Cómo se debe distinguir el uso de las pruebas unitarias y las pruebas de integración?
- En una frase, la prueba unitaria es la «prueba del juicio» y la prueba de integración es la «prueba de la conexión». Las cosas cuyo sentido queda completo sin recursos externos, como el cálculo de importes, las transiciones de estado, la validación de entradas o la clasificación de excepciones, se llevan hacia la prueba unitaria; y lo que «traiciona en el instante en que se conecta», como la ejecución de SQL, la serialización de JSON/CSV, el enrutamiento, el registro en el DI, el bloqueo de archivos, los permisos, el registro de COM o 32bit/64bit, se coloca del lado de la prueba de integración. Si algo se puede verificar con cualquiera de las dos, empiece siempre por la prueba unitaria.
- ¿Qué tipo de cosas conviene subir a la prueba de integración?
- En general se pueden organizar en cuatro límites: formato, cableado, entorno y tiempo. El formato incluye el JSON/CSV, el mapping de la base de datos o la codificación de caracteres; el cableado es la conexión de componentes reales como el registro en el DI, el enrutamiento o el model binding; el entorno es el comportamiento real del sistema operativo, como los permisos de archivo, el registro de COM, 32bit/64bit o STA/MTA; y el tiempo abarca el timeout, la cancellation o las race condition. Como en todos estos casos el sentido se pierde con un fake en memoria, es más seguro cubrirlos con pruebas de integración.
- ¿Qué problema hay en que los mocks se multipliquen en una prueba unitaria?
- Si se llega a una situación en la que hacen falta 7 mocks, el setup es largo y no se ve qué se quiere confirmar, suele deberse a que esa clase tiene demasiadas responsabilidades, o a que se está forzando dentro de la prueba unitaria un cableado que en realidad debería confirmarse con una prueba de integración. El mock es una herramienta para cortar el mundo exterior, no una herramienta para demostrar que la conexión con lo real es correcta. Si se confunde esto, es fácil terminar en la situación de «todo está en verde, pero falla en producción».
- ¿Con qué estructura y proporción conviene organizar las pruebas?
- Lo que se puede usar de forma general es una estructura de 3 capas. La capa núcleo mantiene en profundidad las reglas de negocio y las transiciones de estado con pruebas unitarias; la capa de límites coloca pruebas de integración acotadas en la base de datos, los archivos, el serializer o el DI; y la capa global cubre la verificación de arranque y los flujos principales con un número reducido de smoke/E2E. El barrido exhaustivo de ramificaciones se ejecuta con pruebas unitarias, y la prueba de integración se limita, en cada límite, a al menos un happy path y a un failure path representativo. Cuando aparece un bug, se añade la prueba en la capa donde ese bug se pueda reproducir con el menor coste.
Perfil del autor
Página de presentación del autor del artículo.
Go Komura
Representante de KomuraSoft LLC
Especializado en desarrollo de software para Windows, consultoría técnica e investigación de fallos, sobre todo en proyectos con sistemas existentes y errores difíciles de reproducir.