[英]Unit testing void method that creates a new object
我有一个像下面这样的方法:
public void ExecuteSomeCommand()
{
new MyCommand( someInt, SomeEnum.EnumValue ).Execute();
}
我想测试传入我正在创建的ICommand对象的构造函数的枚举值是否正确。 有什么方法可以用Rhino.Mocks做到这一点吗?
选项1:使用Seam
最简单的方法是使用seam重构该方法:
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);
}
这样,您可以通过扩展此类来拦截“new”运算符的创建。 手动执行此操作时,它可能如下所示:
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() { }
}
}
这个假类可以用在你的测试方法中。
选项2:单独的行为和数据
更好的方法是将数据与行为分开。 您的命令包含数据(消息)和行为(处理该消息)。 如果允许您对代码库进行此类更改:将其分开,例如通过定义命令和命令处理程序。 这是一个例子:
// 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
}
}
现在,您可以使用依赖项注入将处理程序注入要测试的类中。 这个类现在看起来像这样:
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
});
}
}
由于您现在将数据与行为分开,因此可以非常轻松地创建伪命令处理程序(或使用Rhino mocks创建它),以检查是否将正确的命令发送到处理程序。 手动,这将是这样的:
public class FakeHandler<TCommand> : IHandler<TCommand>
{
public TCommand HandledCommand { get; set; }
public void Handle(TCommand command)
{
this.HandledCommand = command;
}
}
这个假处理程序可以在整个单元测试项目中重用。 使用此FakeHandler
的测试可能如下所示:
[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);
}
将数据与行为分开不仅使您的应用程序更易于测试。 它使您的应用程序更具弹性。 例如,可以在执行命令时添加横切关注点,而无需对系统中的任何处理程序进行更改。 因为IHandler<T>
是一个具有单个方法的接口,所以编写一个装饰器非常容易,它可以包装每个处理程序并添加诸如日志记录,审计跟踪,分析,验证,事务处理,容错改进等内容。可以在本文中阅读更多相关内容。
没有我知道的。 我想到的最接近的事情是使用工厂,然后创建该工厂的StrictMock
。 像这样的东西:
readonly ICommandFactory factory;
public Constructor(ICommandFactory factory)
{
this.factory = factory;
}
public void ExecuteSomeCommand()
{
factory.Create( someInt, SomeEnum.EnumValue ).Execute();
}
然后,您可以将对invokation的期望放在Create()
。
HTH
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.