【发布时间】:2023-03-19 06:55:01
【问题描述】:
我正在尝试向遗留代码添加测试,当我开始添加代码时,我感觉有些地方出了问题。
在以下代码中,公共方法 RegisterChange 调用了两个私有方法:
- 获取要存储的对象
- 存储对象
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,但我认为这是一个更一般的测试问题。
【问题讨论】:
-
测试肯定不会通过,因为调用
RegisterChange时会抛出异常。如果它有时会抛出异常,那么由做出更改的人来为此添加测试。它仍应符合公共方法的 documented (ahem) 行为。 -
@JonSkeet,让我们说对 GetParsedInt 的更改不是破坏性的,而是一项新功能,在这种情况下,旧测试并没有意识到这一点。我知道一个新的测试会选择新的功能测试。所以,我的问题是“这是正常的方法吗?” (只是给它一个名字)。谢谢。
-
这样说:
GetParsedEntity的代码是否内联到公共方法中不应该影响测试。这是一个实现细节。所以,如果你没有破坏任何东西,只是添加一个单独测试的新功能,你关心什么?在我看来是合理的。 -
关键是在设计测试时不要对实现感兴趣。您应该对文档/合同描述的效果感兴趣。将“它的目的是什么”与“它的目的是如何实现它”分开。 (这就是使用假货而不是模拟物也派上用场的地方......)
-
@andymaster01:当我们讨论模拟/假货主题时,您可能想阅读一下this question。长话短说,现在 mock 和 fake 之间的区别相当模糊。
标签: c# java unit-testing tdd moq