1

我正在尝试向遗留代码添加测试,当我开始添加代码时,我感觉有些地方出了问题。

在以下代码中,公共方法 RegisterChange 调用两个私有方法:

  1. 获取要存储的对象
  2. 存储对象
public class ChangeService {

    IRepository repository;

    public ChangeService(IRepository repository){
        this.repository = repository;
    }

    public bool RegisterChange( int entityId ){ 
        var entity = GetParsedEntity( entityId );       
        SaveEntity( entity );
        return true;
    }

    private Entity GetParsedEntity( int id ) {
        var entity = repository.GetEntityById( id );
        return new Entity{ Name = entity.Name };
    }

    private void SaveEntity( Entity entity ) {
        repository.Save( Entity );
    }
}

public class ChangeServiceFact(){

    [Fact]
    public void When_valid_entity__Should_save_entity(){

        var mock = new Mock<IRepository>();
        var service = new ChangeService(mock.object);

        var result = service.RegisterChange( 0 );

        Assert.True(result);
    }   
}

因此,当我模拟存储库时,我必须去检查私有方法的代码以了解要模拟哪些操作。

我在这种方法中看到的问题是,由于代码不仅测试测试对象(公共方法)而且测试私有方法,通过查看测试对象并不清楚哪个应该是测试结果(公共方法)。

如果稍后有人决定修改一个私有方法(例如从 GetParsedEntity 抛出异常),测试将继续正确通过,但客户端代码可能会因为此更改而失败。

在这种特殊情况下,我使用 C#、XUnit 和 Moq,但我认为这是一个更一般的测试问题。

4

1 回答 1

3

我在这种方法中看到的问题是,由于代码不仅测试测试对象(公共方法)而且测试私有方法,通过查看测试对象并不清楚哪个应该是测试结果(公共方法)。

您提到的测试对象在不知道其完整合同的情况下没有明显的效果。这里的完整合同是什么?提到的公共方法构造函数,它需要依赖。这里重要的是依赖关系,应该测试与此依赖关系的交互。私有方法(一如既往)是实现细节——与单元测试无关

说了这么多,让我们回到合同上来。ChangeService测试对象(方法)的实际合同是什么?要根据某个 id 从存储库中检索对象,请创建不同的对象并将后者保存在同一存储库中。这是你的测试。

[Fact]
public void ChangeService_StoresNewEntityInRepository_BasedOnProvidedId()
{
    const string ExpectedName = "some name";
    var otherEntity = new OtherEntity { Name = ExpectedName };
    var mock = new Mock<IRepository>();
    var service = new ChangeService(mock.object);
    mock.Setup(m => m.GetEntityById(0)).Return(otherEntity);

    service.RegisterChange(0);

    mock.Verify(m => m.SaveEntity(It.Is<Entity>(e => e.Name == ExpectedName));
} 
于 2013-10-16T20:04:26.407 回答