test net initialize c# xunit.net data-driven-tests xunit2

net - xunit fact c#



Las pruebas MemberData se muestran como una prueba en lugar de muchas (4)

Cuando utiliza [Theory] junto con [InlineData] , creará una prueba para cada elemento de los datos en línea que se proporcionan. Sin embargo, si usa [MemberData] solo se mostrará como una prueba.

¿Hay alguna manera de hacer que las pruebas [MemberData] aparezcan como pruebas múltiples?


En mi proyecto reciente experimenté el mismo problema y, después de algunas investigaciones, la solución que se me ocurrió es la siguiente:

Implemente su MyTheoryAttribute personalizado extendiendo FactAttribute junto con MyTheoryDiscoverer implementando IXunitTestCaseDiscoverer y varios MyTestCases personalizados extendiendo TestMethodTestCase e implementando IXunitTestCase a su gusto. MyTheoryDiscoverer debe reconocer sus casos de prueba personalizados y utilizarlos para encapsular los casos de prueba de teoría enumerados en una forma visible para el marco de Xunit, incluso si los valores pasados ​​no están serializados de forma nativa por Xunit y no implementan IXunitSerializable.

¡Lo más importante es que no hay necesidad de cambiar su precioso código a prueba !

Es un poco de trabajo que hacer, pero como ya lo hice yo y está disponible bajo la licencia MIT, no dude en usarlo. Es parte del proyecto DjvuNet que está alojado en GitHub.

El enlace directo a la carpeta correspondiente con el código de soporte de Xunit se encuentra a continuación:

Código de soporte de prueba de DjvuNet

Para usarlo, cree un ensamblaje separado con estos archivos o inclúyalos directamente en su proyecto de prueba.

El uso es exactamente igual al de Xunit TheoryAttribute y se admiten ClassDataAttribute y MemberDataAttribute, es decir:

[DjvuTheory] [ClassData(typeof(DjvuJsonDataSource))] public void InfoChunk_Theory(DjvuJsonDocument doc, int index) { // Test code goes here } [DjvuTheory] [MemberData(nameof(BG44TestData))] public void ProgressiveDecodeBackground_Theory(BG44DataJson data, long length) { // Test code goes here }

El crédito va también a otro desarrollador, pero desafortunadamente no puedo encontrar su repo en github


MemberData puede trabajar con propiedades o métodos que devuelven IEnumerable del objeto []. Verá un resultado de prueba separado para cada rendimiento en este escenario:

public class Tests { [Theory] [MemberData("TestCases", MemberType = typeof(TestDataProvider))] public void IsLargerTest(string testName, int a, int b) { Assert.True(b>a); } } public class TestDataProvider { public static IEnumerable<object[]> TestCases() { yield return new object[] {"case1", 1, 2}; yield return new object[] {"case2", 2, 3}; yield return new object[] {"case3", 3, 4}; } }

Sin embargo, tan pronto como deba pasar objetos personalizados complejos sin importar cuántos casos de prueba tendrá, la ventana de resultados de la prueba mostrará solo una prueba. Este no es un comportamiento ideal y, de hecho, es muy inconveniente al depurar el caso de prueba que está fallando. La solución es crear su propio contenedor que se derivará de IXunitSerializable.

public class MemberDataSerializer<T> : IXunitSerializable { public T Object { get; private set; } public MemberDataSerializer() { } public MemberDataSerializer(T objectToSerialize) { Object = objectToSerialize; } public void Deserialize(IXunitSerializationInfo info) { Object = JsonConvert.DeserializeObject<T>(info.GetValue<string>("objValue")); } public void Serialize(IXunitSerializationInfo info) { var json = JsonConvert.SerializeObject(Object); info.AddValue("objValue", json); } }

Ahora puede tener sus objetos personalizados como parámetros para las teorías de Xunit y seguir viéndolos / depurándolos como resultados independientes en la ventana del corredor de prueba:

public class UnitTest1 { [Theory] [MemberData("TestData", MemberType = typeof(TestDataProvider))] public void Test1(string testName, MemberDataSerializer<TestData> testCase) { Assert.Equal(1, testCase.Object.IntProp); } } public class TestDataProvider { public static IEnumerable<object[]> TestData() { yield return new object[] { "test1", new MemberDataSerializer<TestData>(new TestData { IntProp = 1, StringProp = "hello" }) }; yield return new object[] { "test2", new MemberDataSerializer<TestData>(new TestData { IntProp = 2, StringProp = "Myro" }) }; } } public class TestData { public int IntProp { get; set; } public string StringProp { get; set; } }

Espero que esto ayude.


Pasé mucho tiempo tratando de resolver esto en mi proyecto. Esta discusión relacionada con Github del propio @NPadrutt ayudó mucho, pero todavía era confusa.

El tl; dr es este: [MemberInfo] informará una prueba de grupo único a menos que los objetos proporcionados para cada prueba puedan ser serializados completamente y deserializados implementando IXunitSerializable .

Fondo

Mi propia configuración de prueba fue algo como:

public static IEnumerable<object[]> GetClients() { yield return new object[] { new Impl.Client("clientType1") }; yield return new object[] { new Impl.Client("clientType2") }; } [Theory] [MemberData(nameof(GetClients))] public void ClientTheory(Impl.Client testClient) { // ... test here }

La prueba se ejecutó dos veces, una para cada objeto de [MemberData] , como se esperaba. Como experimentó @NPadrutt, solo un elemento apareció en el Explorador de pruebas, en lugar de dos. Esto se debe a que el objeto Impl.Client proporcionado no se puede serializar por ninguna de las interfaces que admite xUnit (más sobre esto más adelante).

En mi caso, no quería incluir las preocupaciones de la prueba en mi código principal. Pensé que podría escribir un proxy delgado alrededor de mi clase real que engañaría al corredor xUnit para que creyera que podría serializarlo, pero después de luchar con él durante más tiempo del que quisiera admitir, me di cuenta de que la parte que no entendía era :

Los objetos no solo se serializan durante el descubrimiento para contar permutaciones; Cada objeto también se deserializa en el tiempo de ejecución de la prueba cuando se inicia la prueba.

Por lo tanto, cualquier objeto que proporcione con [MemberData] debe ser compatible con una (des) serialización de ida y vuelta completa. Esto me parece obvio ahora, pero no pude encontrar ninguna documentación mientras intentaba averiguarlo.

Solución

  • Asegúrese de que cada objeto (y cualquier elemento no primitivo que pueda contener) pueda ser completamente serializado y deserializado. La implementación de IXunitSerializable xUnit le dice a xUnit que es un objeto serializable.

  • Si, como en mi caso, no desea agregar atributos al código principal, una solución es crear una clase de generador serializable delgada para pruebas que pueda representar todo lo necesario para recrear la clase real. Aquí está el código anterior, después de que lo puse a trabajar:

TestClientBuilder

public class TestClientBuilder : IXunitSerializable { private string type; // required for deserializer public TestClientBuilder() { } public TestClientBuilder(string type) { this.type = type; } public Impl.Client Build() { return new Impl.Client(type); } public void Deserialize(IXunitSerializationInfo info) { type = info.GetValue<string>("type"); } public void Serialize(IXunitSerializationInfo info) { info.AddValue("type", type, typeof(string)); } public override string ToString() { return $"Type = {type}"; } }

Prueba

public static IEnumerable<object[]> GetClients() { yield return new object[] { new TestClientBuilder("clientType1") }; yield return new object[] { new TestClientBuilder("clientType2") }; } [Theory] [MemberData(nameof(GetClients))] private void ClientTheory(TestClientBuilder clientBuilder) { var client = clientBuilder.Build(); // ... test here }

Es un poco molesto que ya no me inyecten el objeto de destino, pero es solo una línea adicional de código para invocar a mi constructor. Y, mis exámenes pasan (y aparecen dos veces), así que no me quejo.


Por ahora, ReSharper puede mostrar todas las pruebas de MemberData con parámetros personalizados cuando sus clases personalizadas anulan a ToString() .

Por ejemplo :

public static TheoryData<Permission, Permission, Permission> GetAddRuleData() { var data = new TheoryData<Permission, Permission, Permission> { { new Permission("book", new[] {"read"}, null), new Permission("book", new[] {"delete"}, new[] {"2333"}), new Permission("book", new[] {"delete", "read"}, new[] {"*", "2333"}) }, { new Permission("book", new[] {"read"}, null), new Permission("music", new[] {"read"}, new[] {"2333"}), new Permission { Resources = new Dictionary<string, ResourceRule> { ["book"] = new ResourceRule("book", new[] {"read"}, null), ["music"] = new ResourceRule("music", new[] {"read"}, new[] {"2333"}), } } } }; return data; }

Permission anula a ToString() , luego en ReSharper Test Session Explorer: