Así es como me gustaría escribir uno de un par de pruebas de unidad para una fábrica de este tipo (con xUnit.net):
[Fact]
public void CreateReturnsInstanceWithCorrectParam1()
{
var sut = new FooFactory();
var expected = new object();
var actual = sut.Create(expected, new object());
var concrete = Assert.IsAssignableFrom<Foo>(actual);
Assert.Equal(expected, concrete.Object1);
}
¿Rompe encapsulación? Sí y no ... un poco. La encapsulación no solo trata sobre el ocultamiento de datos; más importante aún, se trata de protecting the invariants of objects.
Asumamos que Foo expone esta API pública:
public class Foo : IFoo
{
public Foo(object param1, object param2);
public void MethodDefinedByInterface();
public object Object1 { get; }
}
Mientras que la propiedad Object1
viola levemente el Law of Demeter, no meterse con los invariantes de la clase porque es de sólo lectura.
Además, la propiedad Object1
es parte de la clase Foo concreto - no la interfaz IFoo:
public interface IFoo
{
void MethodDefinedByInterface();
}
Una vez que darse cuenta de que en una API de acoplamiento flexible, concrete members are implementation details, tales sólo de hormigón de sólo lectura establecimientos un impacto muy bajo en la encapsulación. Piénsalo de esta manera:
El constructor Foo pública es también una parte de la API de la clase Foo hormigón, por lo que sólo mediante la inspección de la API pública, aprendemos que param1
y param2
son parte de la clase. En cierto sentido, esto ya 'rompe la encapsulación', por lo que hacer que cada parámetro esté disponible como propiedades de solo lectura en la clase concreta no cambia mucho.
Tales propiedades proporcionan la ventaja de que ahora podemos probar la forma estructural de la clase Foo devuelta por la fábrica.
Esto es mucho más fácil que tener que repetir un conjunto de pruebas de unidades de comportamiento que, debemos suponer, ya cubren la clase concreta de Foo. Es casi como una prueba lógica:
- De las pruebas de la clase concreta de Foo, sabemos que utiliza correctamente/interactúa con sus parámetros de constructor.
- De estas pruebas también sabemos que el parámetro constructor está expuesto como una propiedad de solo lectura.
- De una prueba de FooFactory sabemos que devuelve una instancia de la clase concreta de Foo.
- Además, sabemos por las pruebas del método Create que sus parámetros se pasan correctamente al constructor Foo.
- Q.E.D.
Las pruebas unitarias deben depender de los requisitos funcionales y las expectativas sobre el componente que se está probando. No veo ningún valor de unidad de prueba de esa clase sin el resto del contexto. – ivowiblo
@ivowiblo Creo que está confundiendo las pruebas unitarias con las pruebas de estilo BDD. Si no escribe una prueba para esta unidad, ¿cómo sabe si funciona? –
hacer la prueba de la unidad, pero ¿qué vas a probar? cual es la expectativa para esa unidad? Dije que no veo el valor de hacer una prueba unitaria de esa clase sin saber nada más. Hacer pruebas unitarias solo por hacer pruebas unitarias no tiene ningún sentido. Las pruebas (unidades, comportamientos, funcionalidades completas, lo que usted desee) siempre deben estar guiadas por las expectativas y los requisitos. Si no, es solo esnobismo (si esa palabra existe). – ivowiblo