创建新对象的单元测试 void 方法

本文关键字:单元测试 void 方法 对象 新对象 创建 | 更新日期: 2023-09-27 17:56:11

>我有一个如下方法:

public void ExecuteSomeCommand()
{
    new MyCommand( someInt, SomeEnum.EnumValue ).Execute();
}

我想测试传递给我正在创建的 ICommand 对象的构造函数的枚举值是否正确。有什么方法可以让我用Rhino.Mocks做到这一点吗?

创建新对象的单元测试 void 方法

选项 1:使用接缝

最简单的方法是用接缝重构该方法:

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 模拟创建它)来检查是否向处理程序发送了正确的命令。手动操作,这看起来像这样:

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();
}

然后,您可以期望调用 Create()