我试图了解如何使用Moq,但是我对Moq应该用于什么感到困惑。我的理解是,模拟框架用于生成在正常情况下难以创建的对象。我看过Moq的例子似乎不仅创建了Moq对象,而且还提供了测试对象的方法 - 这似乎不需要我们用于大多数单元测试的常用Assert等方法。
有人可以确认我是否认为Moq取代了Assert等,或者我是否完全忽略了Moq的观点?
答案 0 :(得分:4)
像Moq这样的模拟框架不会完全替换测试框架的Assert
。有时它确实如此,有时却没有。
让我们首先区分嘲笑和存根。存根纯粹用于隔离并将某种受控行为注入被测系统(SUT)。
Mocks是存根的超集,能够验证是否在模拟上调用了某些东西。在Moq中,Verify()
的调用使得stub成为关于该方法的模拟。调用VerifyAll()
会使所有设置的方法都被模拟。
建议的方法是在测试中应该只有一个模拟。在这个意义上,它与Assert
类似 - 你不应该在测试中验证多个东西。
回到最初的问题。如果您正在执行状态测试,那么您将使用零个或多个存根和一个断言。如果您正在进行交互测试,那么您将使用零个或多个存根和一个模拟。下面是一个示例,其中可能适合使用mocks和asserts来测试相同的服务。
public interface IAccountRepository {
decimal GetBalance(int accountId);
void SetBalance(int accountId, decimal funds);
}
public class DepositTransaction {
IAccountRepository m_repo;
public DepositTransaction(IAccountRepository repo) {
m_repo = repo;
}
public decimal DepositedFunds {get; private set;};
void Deposit(int accountId, decimal funds) {
decimal balance = m_repo.GetBalance(accountId);
balance += funds;
m_repo.SetBalance(balance);
DepositedFunds += funds;
}
}
public class DepositTest {
[TestMethod]
void DepositShouldSetBalance() {
var accountMock = new Mock<IAccountRepository>();
accountMock.Setup(a=>a.GetBalance(1)).Returns(100); //this is a stub with respect to GetBalance
var transation = new DepositTransaction(accountMock.Object);
transation.Deposit(1, 20);
accountMock.Verify(a=>a.SetBalance(1, 120)); //this is a mock with respect to SetBalance
}
[TestMethod]
void DepositShouldIncrementDepositedFunds() {
var accountMock = new Mock<IAccountRepository>();
accountMock.Setup(a=>a.GetBalance(1)).Returns(100); //this is a stub with respect to GetBalance
var transation = new DepositTransaction(accountMock.Object);
transation.Deposit(1, 20);
transation.Deposit(1, 30);
Assert.AreEqual(50, transaction.DepositedFunds);
}
}
答案 1 :(得分:2)
+1,另一个A ......
(重申Igor非常完整的答案)Mocking库只是测试/规范中的工具用途 - 有时候会完成存根或模拟的角色。
基于记录/重放的模拟通常可以替换测试中Assert
的角色。这种风格最近在RhinoMocks中被忽略了(我相信4.0会将它分流到一边),并且在Moq中已经积极劝阻了很长一段时间。我相信这就是你问这个问题的原因。
您应该优化测试:
这里的最后一点是关键点 - 你想要一点你在做Checks,它应该是最后的断言。有时这可能意味着您甚至可以在排列/上下文阶段最终在Moq中执行Setup
调用,然后验证它是否在Assert阶段使用,代码看起来像重复。