tag propiedad c# moq

propiedad tag c#



Cómo hacer que Moq ignore los argumentos que son ref o out (3)

En RhinoMocks, solo puedes contar tus simulacros a IgnoreArguments como una declaración general. En Moq, parece que tienes que especificar It.IsAny () para cada argumento. Sin embargo, esto no funciona para los argumentos de referencia y salida. ¿Cómo puedo probar el siguiente método cuando necesito Moq la llamada de servicio interna para devolver un resultado específico?

public void MyMethod() { // DoStuff IList<SomeObject> errors = new List<SomeObject>(); var result = _service.DoSomething(ref errors, ref param1, param2); // Do more stuff }

Método de prueba:

public void TestOfMyMethod() { // Setup var moqService = new Mock<IMyService>(); IList<String> errors; var model = new MyModel(); // This returns null, presumably becuase "errors" // here does not refer to the same object as "errors" in MyMethod moqService.Setup(t => t.DoSomething(ref errors, ref model, It.IsAny<SomeType>()). Returns(new OtherType())); }

ACTUALIZACIÓN: Por lo tanto, cambiar los errores de "ref" a "out" funciona. Así que parece que el problema real es tener un parámetro ref que no puedes inyectar.


Como @nemesv mencionó anteriormente, It.IsAny devuelve un valor nulo, por lo que no puede usarlo como un parámetro de referencia. Para que la llamada funcione, es necesario pasarle un objeto real.

El problema se produce cuando no tiene acceso a la creación del objeto que desea pasar por ref. Si tuvieras acceso al objeto real, simplemente podrías usarlo en tu prueba y olvidarte de intentar burlarte de él.

Aquí hay una solución que usa la técnica de Extraer y Reemplazar que te permitirá hacer eso. Como su nombre lo indica, usted extrae el bit de código problemático en su propio método. Luego, anula el método en una clase de prueba que hereda de la clase en prueba. Finalmente, configura su objeto real, lo pasa a su nueva clase de prueba de creación y prueba sus llamadas por ref.

Aquí hay mucho tiempo de código (creado), pero muestra el antes y el después, con una prueba de aprobación al final.

using System; using System.Collections.Generic; using Moq; using MoqRefProblem; using NUnit.Framework; namespace MoqRefProblem { //This class is the one we want to have passed by ref. public class FileContext { public int LinesProcessed { get; set; } public decimal AmountProcessed { get; set; } } public interface IRecordParser { //The ref parameter below is what''s creating the testing problem. void ParseLine(decimal amount, ref FileContext context); } //This is problematic because we don''t have a //seam that allows us to set the FileContext. public class OriginalFileParser { private readonly IRecordParser _recordParser; public OriginalFileParser(IRecordParser recordParser) { _recordParser = recordParser; } public void ParseFile(IEnumerable<decimal> items) { //This is the problem var context = new FileContext(); ParseItems(items, ref context); } private void ParseItems(IEnumerable<decimal> items, ref FileContext context) { foreach (var item in items) { _recordParser.ParseLine(item, ref context); } } } } //This class has had the creation of the FileContext extracted into a virtual //method. public class FileParser { private readonly IRecordParser _recordParser; public FileParser(IRecordParser recordParser) { _recordParser = recordParser; } public void ParseFile(IEnumerable<decimal> items) { //Instead of newing up a context, we''ll get it from a virtual method //that we''ll override in a test class. var context = GetFileContext(); ParseItems(items, ref context); } //This is our extensibility point protected virtual FileContext GetFileContext() { var context = new FileContext(); return context; } private void ParseItems(IEnumerable<decimal> items, ref FileContext context) { foreach (var item in items) { _recordParser.ParseLine(item, ref context); } } } //Create a test class that inherits from the Class under Test //We will set the FileContext object to the value we want to //use. Then we override the GetContext call in the base class //to return the fileContext object we just set up. public class MakeTestableParser : FileParser { public MakeTestableParser(IRecordParser recordParser) : base(recordParser) { } private FileContext _context; public void SetFileContext(FileContext context) { _context = context; } protected override FileContext GetFileContext() { if (_context == null) { throw new Exception("You must set the context before it can be used."); } return _context; } } [TestFixture] public class WorkingFileParserTest { [Test] public void ThisWillWork() { //Arrange var recordParser = new Mock<IRecordParser>(); //Note that we are an instance of the TestableParser and not the original one. var sut = new MakeTestableParser(recordParser.Object); var context = new FileContext(); sut.SetFileContext(context); var items = new List<decimal>() { 10.00m, 11.50m, 12.25m, 14.00m }; //Act sut.ParseFile(items); //Assert recordParser.Verify(x => x.ParseLine(It.IsAny<decimal>(), ref context), Times.Exactly(items.Count)); } }


Como ya descubrió, el problema está en su argumento ref .

Moq actualmente solo admite la coincidencia exacta para los argumentos de ref , lo que significa que la llamada solo coincide si pasa la misma instancia que ha usado en la Setup . Así que no hay una coincidencia general, por lo que It.IsAny() no funcionará.

Ver inicio rápido de Moq

// ref arguments var instance = new Bar(); // Only matches if the ref argument to the invocation is the same instance mock.Setup(foo => foo.Submit(ref instance)).Returns(true);

Y el grupo de discusión Moq:

La coincidencia de referencia significa que la configuración coincide solo si se llama al método con esa misma instancia. It.IsAny devuelve nulo, así que probablemente no sea lo que está buscando.

Use la misma instancia en la configuración como la de la llamada real, y la configuración coincidirá.