给出以下代码
public class Entity
{
public string Name { get; set; }
public string Status { get; set; }
}
public interface IRepository
{
void InsertEntity(Entity entity);
void UpdateEntity(Entity entity);
}
public class Processor
{
private IRepository _repository;
public Processor(IRepository repository)
{
_repository = repository;
}
public void Execute(string name)
{
var entity = new Entity() { Name = name, Status = "Initialized" };
_repository.InsertEntity(entity);
// do other things with the entity
entity.Status = "Processed";
_repository.UpdateEntity(entity);
}
}
我可以编写一个单元测试来验证是否在Execute方法中调用了存储库,并使用方法InsertEntity保存实体的值。换句话说,我想确保在调用InsertEntity时,实体的Status属性值为“Initialized”。所以我的单元测试将是这样的:
[TestMethod]
public void ShouldSaveEntityWithStatusInitialized()
{
var mock = new Mock<IRepository>();
var processor = new Processor(mock.Object);
processor.Execute("test");
mock.Verify(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Initialized")), Times.Once()); // fail
}
但是,即使调用Status =“Initialized”的InsertEntity方法,此代码也会失败(我已调试过)。我认为这是因为实体对象在Execute方法执行期间被更改(最后Status属性更改为“Processed”)并且Moq验证对更改对象的调用。事实上,这个其他单元测试效果很好。
[TestMethod]
public void ShouldUpdateEntityWithStatusProcessedAtTheEnd()
{
var mock = new Mock<IRepository>();
var processor = new Processor(mock.Object);
processor.Execute("test");
mock.Verify(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Processed")), Times.Once());
}
我发现进行第一次单元测试的唯一方法是使用以下解决方法。我使用Moq的回调功能保存Status属性的值,并稍后断言。
[TestMethod]
public void ShouldSaveEntityWithStatusInitialized_withWorkaround()
{
var mock = new Mock<IRepository>();
var processor = new Processor(mock.Object);
string status = string.Empty;
mock.Setup(m => m.InsertEntity(It.IsAny<Entity>())).Callback((Entity e) => status = e.Status);
processor.Execute("test");
Assert.AreEqual("Initialized", status);
}
但我不喜欢那样。我想知道是否有办法让Moq验证在执行STU(被测系统)期间对模拟对象进行的调用,而不是在所有执行完成之后。
由于
答案 0 :(得分:3)
恕我直言,最后一种方法(你称之为“黑客”)是测试状态值的正确方法。我更清楚的是,您要在此测试中验证的是,在调用InsertEntity
方法时,状态设置为“已初始化”。
您要与Verify一起使用的方法更准确地说明您正在测试的内容。你想确认调用了InsertEntity
,或者你想测试参数是“已初始化”,还是两者都有,我不知道。如果你想要测试两者,那么实际上这将是两个不同的单元测试。
您还可以执行以下操作...
mock.Setup(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Initialized")));
但是我不喜欢这种方法,因为这意味着当状态不是“已初始化”的值时,生成InsertEntity
代码将运行而不是模拟。更有可能单元测试仍然会失败,但单元测试返回的断言失败消息更加模糊(由于真实InsertEntity
方法中发生了某些事情而导致失败)。回调准确说明了您在单元测试中测试的内容。
答案 1 :(得分:1)
[TestMethod]
public void ShouldSaveEntityWithStatusInitialized()
{
// arrange
var mock = new Mock<IRepository>();
mock.Setup(m => m.InsertEntity(It.Is<Entity>(e => e.Status == "Initialized")));
var processor = new Processor(mock.Object);
// act
processor.Execute("test");
// assert
mock.Verify();
}