Opción 1: Utilice una costura
La forma más fácil es refactorizar ese método con una costura:
public void ExecuteSomeCommand()
{
this.CreateCommand(someInt, SomeEnum.EnumValue).Execute();
}
// Your seam
protected virtual ICommand CreateCommand(int someInt,
SomeEnum someEnum)
{
return new MyCommand(someInt, SomeEnum.EnumValue);
}
esta manera se puede interceptar la creación del operador 'nuevo' extendiendo esta clase. Al hacer esto a mano, podría verse así:
public FakeSomeService : SomeService
{
public int SomeInt;
public SomeEnum SomeEnum;
protected override Command CreateCommand(int someInt,
SomeEnum someEnum)
{
this.SomeInt = someInt;
this.SomeEnum = someEnum;
return new FakeCommand();
}
private sealed class FakeCommand : Command
{
public override void Execute() { }
}
}
Esta clase falsa se puede utilizar en sus métodos de prueba.
Opción 2: Comportamiento independiente y datos
Una mejor manera serían para separar los datos del comportamiento. El comando tiene tanto datos (el mensaje) como el comportamiento (manejo de ese mensaje). Si puede realizar dicho cambio en su base de código: separe esto, por ejemplo, definiendo comandos y controladores de comando. Aquí está un ejemplo:
// Define an interface for handling commands
public interface IHandler<TCommand>
{
void Handle(TCommand command);
}
// Define your specific command
public class MyCommand
{
public int SomeInt;
public SomeEnum SomeEnum;
}
// Define your handler for that command
public class MyCommandHandler : IHandler<MyCommand>
{
public void Handle(MyCommand command)
{
// here your old execute logic
}
}
Ahora puede utilizar la inyección de dependencia para inyectar un controlador en la clase que desea probar. Esta clase ahora se verá así:
public class SomeService
{
private readonly IHandler<MyCommand> handler;
// Inject a handler here using constructor injection.
public SomeService(IHandler<MyCommand> handler)
{
this.handler = handler;
}
public void ExecuteSomeCommand()
{
this.handler.Handle(new MyCommand
{
SomeInt = someInt,
SomeEnum = someEnum
});
}
}
Desde ahora separado los datos de la conducta, que será muy fácil crear un controlador de comandos falsa (o crearlo usando Rhino se burla) que comprueba si el comando correcto fue enviado al controlador. Manualmente este sería el siguiente:
public class FakeHandler<TCommand> : IHandler<TCommand>
{
public TCommand HandledCommand { get; set; }
public void Handle(TCommand command)
{
this.HandledCommand = command;
}
}
Este controlador falsos pueden ser reutilizados a través de su proyecto de prueba de unidad. Una prueba que utiliza este FakeHandler
podría tener este aspecto:
[TestMethod]
public void SomeTestMethod()
{
// Arrange
int expected = 23;
var handler = new FakeHandler<MyCommand>();
var service = new SomeService(handler);
// Act
service.ExecuteSomeCommand();
// Assert
Assert.AreEqual(expected, handler.HandledCommand.SomeInt);
}
La separación de los datos a partir del comportamiento no sólo hace que su aplicación más comprobable. Hace que su aplicación sea más resistente para cambiar. Por ejemplo, las preocupaciones transversales se pueden agregar a la ejecución de comandos, sin la necesidad de hacer cambios a cualquier controlador en el sistema.Como el IHandler<T>
es una interfaz con un único método, es muy fácil escribir un decorator que puede ajustar todos los controladores y agregar cosas como registro, seguimiento de auditoría, creación de perfiles, validación, manejo de transacciones, improvisación de errores, etc. Puede leer más al respecto en this article.
¿El constructor lanza una excepción si el valor enum es inesperado? – MattDavey
El propio constructor no lanza. Los argumentos se pasan por el método Execute al modelo de vista de una vista que se crea a través de una clase NavigationManager. El nuevo modelo de vista luego usa la enumeración para alguna propiedad de visualización y si se trata de un valor inesperado, arroja. – alimbada