【发布时间】:2012-10-31 22:03:14
【问题描述】:
我正在使用 IDatabase 类附带的 NPoco 作为数据库调用方法。我想验证进入 NPoco Insert 方法的对象是否具有正确的数据(以域对象的形式)。
public interface IUnitOfWorkProvider
{
IUnitOfWork GetUnitOfWork();
}
public interface IUnitOfWork : IDisposable
{
void Commit();
IDatabase Db { get; }
void SetOneTimeCommandTimeout(int timeout);
void SetGlobalCommandTimeout(int timeout);
}
public interface IDatabase : IDatabaseQuery
{
IDbConnection Connection { get; }
IDbTransaction Transaction { get; }
void AbortTransaction();
void BeginTransaction();
void BeginTransaction(IsolationLevel? isolationLevel);
void CompleteTransaction();
IDataParameter CreateParameter();
int Delete(object poco);
int Delete<T>(object pocoOrPrimaryKey);
int Delete<T>(Sql sql);
int Delete<T>(string sql, params object[] args);
int Delete(string tableName, string primaryKeyName, object poco);
int Delete(string tableName, string primaryKeyName, object poco, object primaryKeyValue);
void Dispose();
Transaction GetTransaction();
Transaction GetTransaction(IsolationLevel? isolationLevel);
object Insert(object poco);
object Insert(string tableName, string primaryKeyName, object poco);
object Insert(string tableName, string primaryKeyName, bool autoIncrement, object poco);
void Save(object poco);
void Save(string tableName, string primaryKeyName, object poco);
IDatabase SetTransaction(IDbTransaction tran);
int Update(object poco);
int Update<T>(Sql sql);
int Update(object poco, IEnumerable<string> columns);
int Update(object poco, object primaryKeyValue);
int Update<T>(string sql, params object[] args);
int Update(object poco, object primaryKeyValue, IEnumerable<string> columns);
int Update(string tableName, string primaryKeyName, object poco);
int Update(string tableName, string primaryKeyName, object poco, IEnumerable<string> columns);
int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue);
int Update(string tableName, string primaryKeyName, object poco, object primaryKeyValue, IEnumerable<string> columns);
}
// my test class file
private IFixture fixture;
private Mock<IUnitOfWork> unitOfWork;
private MyService myService;
private Mock<IDatabase> database; // new based on responses
[SetUp]
public void Setup()
{
fixture = new Fixture().Customize(new AutoMoqCustomization());
database = fixture.Freeze<Mock<IDatabase>>();// new based on responses
unitOfWork = fixture.Freeze<Mock<IUnitOfWork>>();
myService = fixture.CreateAnonymous<MyService>();
}
[Test]
public MyTest()
{
// fails
unitOfWork.Setup(x => x.Db.Insert(It.IsAny<MyDomainObject>()));
myService.CallMyMethod();
unitOfWork.Verify(x => x.Db.Insert(It.IsAny<MyDomainObject>()));
// fails
unitOfWork.Setup(x => x.Db.Insert(It.IsAny<object>()));
myService.CallMyMethod();
unitOfWork.Verify(x => x.Db.Insert(It.IsAny<object>()));
// fails (this was a try based on responses)
database.Setup(x => x.Insert(It.IsAny<object>()));
myService.CallMyMethod();
database.Verify(x => x.Insert(It.IsAny<object>()));
// passes
unitOfWork.Setup(x => x.Db.Insert(It.IsAny<object>()));
myService.CallMyMethod();
unitOfWork.Verify();
}
public class MyDomainObject
{
public void Id {get; set;}
}
被调用的代码(什么应该触发验证)
using (var unitOfWork = unitOfWorkProvider.GetUnitOfWork())
{
MyDomainObject myDomain = anotherService.getMyDomain(DateTime.Now, 100);
unitOfWork.Db.Insert(myDomain);
}
【问题讨论】:
-
你的设置不是我所看到的模拟 - 即你可以做
x.Db,但是当你在另一个级别添加的那一刻,你就超出了设置表达式可以做的事情 - 这个Verify()有效 [正如您所称的x.Db] 的事实强烈证实了这一点。但也许你已经剥离了太多。 -
就像 Ruben 所怀疑的那样,
IDatabase Db被自动模拟,你对错误的对象设定了期望。通常,在执行x => x.Db ...位(Db为空)时会出现空引用异常,但由于您使用 AutoFixture,它会静默通过。您的最后一个示例之所以有效,只是因为对unitOfWork根本没有任何期望。我建议你FreezeIDatabase并对其设定期望。 -
@RubenBartelink Moq 创建了所谓的自动模拟层次结构(也称为递归模拟)-因此,只要它们是虚拟的或抽象的,您就可以根据需要向下添加任意多个级别-以便 Moq 可以代理它们
-
我厌倦了冻结 IDatabase 但不知道如何处理冻结的模拟。当我只是冻结它并且什么都不做时,测试仍然失败。当我在调试时,IDatabase 似乎总是有相同的数字(所以我认为它是同一个对象)
-
@jimmy_keen - 我用我最新的尝试更新了我的帖子,但它们仍然不起作用。
标签: c# .net unit-testing moq autofixture