根据私有方法测试公共方法的方法

时间:2013-10-16 19:10:15

标签: c# java unit-testing tdd moq

我正在尝试将测试添加到遗留代码中,当我开始添加代码时,我感觉有些问题。

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

  1. 获取要存储的对象
  2. 存储对象
  3. 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,但我认为这更像是一个普遍的测试问题。

1 个答案:

答案 0 :(得分: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));
}