restricciones permiten metodo investigacion interfaces genericos generico genericas definicion declaraciones constructores clase c# .net generics mocking moq

c# - permiten - Métodos genéricos burlándose en Moq sin especificar T



metodo generico java (4)

A menos que entienda mal lo que necesitas, podrías construir un método como este:

private Mock<IRepo> MockObject<T>() { var mock = new Mock<IRepo>(); return mock.Setup(pa => pa.Reserve<T>()) .Returns(new Mock<IA<T>>().Object).Object; }

Tengo una interfaz con un método de la siguiente manera:

public interface IRepo { IA<T> Reserve<T>(); }

Me gustaría burlarme de la clase que contiene este método sin tener que especificar los métodos de instalación para cada tipo para el que podría usarse. Idealmente, me gustaría que devolviera un new mock<T>.Object .

¿Cómo logro esto?

Parece que mi explicación no estaba clara. Aquí hay un ejemplo: esto es posible ahora mismo, cuando especifico la T (aquí, cadena):

[TestMethod] public void ExampleTest() { var mock = new Mock<IRepo>(); mock.Setup(pa => pa.Reserve<string>()).Returns(new Mock<IA<string>>().Object); }

Lo que me gustaría lograr es algo como esto:

[TestMethod] public void ExampleTest() { var mock = new Mock<IRepo>(); mock.Setup(pa => pa.Reserve<T>()).Returns(new Mock<IA<T>>().Object); // of course T doesn''t exist here. But I would like to specify all types // without having to repeat the .Setup(...) line for each of them. }

Algunos métodos del objeto bajo prueba pueden llamar reserva para tres o cuatro tipos diferentes. Si tengo que configurar todos los tipos, tengo que escribir muchos códigos de configuración para cada prueba. Pero en una sola prueba, no me preocupan todos ellos, simplemente necesito objetos simulados que no sean nulos, excepto el que realmente estoy probando (y para el que con mucho gusto escribo una configuración más compleja).


Aquí hay una forma de hacerlo que parece funcionar. Si todas las clases que está utilizando en IRepo se heredan de una sola clase base, puede usar este como está y nunca tendrá que actualizarlo.

public Mock<IRepo> SetupGenericReserve<TBase>() where TBase : class { var mock = new Mock<IRepo>(); var types = GetDerivedTypes<TBase>(); var setupMethod = this.GetType().GetMethod("Setup"); foreach (var type in types) { var genericMethod = setupMethod.MakeGenericMethod(type) .Invoke(null,new[] { mock }); } return mock; } public void Setup<TDerived>(Mock<IRepo> mock) where TDerived : class { // Make this return whatever you want. Can also return another mock mock.Setup(x => x.Reserve<TDerived>()) .Returns(new IA<TDerived>()); } public IEnumerable<Type> GetDerivedTypes<T>() where T : class { var types = new List<Type>(); var myType = typeof(T); var assemblyTypes = myType.GetTypeInfo().Assembly.GetTypes(); var applicableTypes = assemblyTypes .Where(x => x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract && x.GetTypeInfo().IsSubclassOf(myType)); foreach (var type in applicableTypes) { types.Add(type); } return types; }

De lo contrario, si no tiene una clase base, puede modificar SetupGenericReserve para no usar el parámetro de tipo TBase y, en su lugar, crear una lista de todos los tipos que desea configurar, algo como esto:

public IEnumerable<Type> Alternate() { return new [] { MyClassA.GetType(), MyClassB.GetType() } }

Nota: Esto está escrito para ASP.NET Core, pero debería funcionar en otras versiones excepto para el método GetDerivedTypes.


He encontrado una alternativa que creo que se acerca más a lo que quieres. De todos modos me fue útil así que aquí va. La idea es crear una clase intermedia que sea casi puramente abstracta e implemente su interfaz. La parte que no es abstracta es la parte que Moq no puede manejar. P.ej

public abstract class RepoFake : IRepo { public IA<T> Reserve<T>() { return (IA<T>)ReserveProxy(typeof(T)); } // This will be mocked, you can call Setup with it public abstract object ReserveProxy(Type t); // TODO: add abstract implementations of any other interface members so they can be mocked }

Ahora puedes RepoFake de RepoFake en lugar de IRepo. Todo funciona de la misma manera, excepto que escribes tus configuraciones en ReserveProxy lugar de Reserve . Puede manejar la devolución de llamada si desea realizar aseveraciones basadas en el tipo, aunque el parámetro Type para ReserveProxy es totalmente opcional.


Simplemente haga esto:

[TestMethod] public void ExampleTest() { var mock = new Mock<IRepo> { DefaultValue = DefaultValue.Mock, }; // no setups needed! ... }

Dado que su simulacro no tiene un comportamiento Strict , estará contento con las llamadas que ni siquiera ha configurado. En ese caso, simplemente se devuelve un "valor predeterminado". Entonces

DefaultValue.Mock

asegura que este "valor predeterminado" sea un nuevo Mock<> del tipo apropiado, en lugar de solo una referencia nula.

La limitación aquí es que no puede controlar (por ejemplo, realizar configuraciones especiales en) las "sub-simulaciones" individuales que se devuelven.