【问题标题】:How to debug verify in moq?如何在起订量中调试验证?
【发布时间】: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 =&gt; x.Db ... 位(Db 为空)时会出现空引用异常,但由于您使用 AutoFixture,它会静默通过。您的最后一个示例之所以有效,只是因为对 unitOfWork 根本没有任何期望。我建议你 Freeze IDatabase 并对其设定期望。
  • @RubenBartelink Moq 创建了所谓的自动模拟层次结构(也称为递归模拟)-因此,只要它们是虚拟的或抽象的,您就可以根据需要向下添加任意多个级别-以便 Moq 可以代理它们
  • 我厌倦了冻结 IDatabase 但不知道如何处理冻结的模拟。当我只是冻结它并且什么都不做时,测试仍然失败。当我在调试时,IDatabase 似乎总是有相同的数字(所以我认为它是同一个对象)
  • @jimmy_keen - 我用我最新的尝试更新了我的帖子,但它们仍然不起作用。

标签: c# .net unit-testing moq autofixture


【解决方案1】:

SUT 中的代码使用 IUnitOfWorkProvider 来生成 IUnitOfWork:

using (var unitOfWork = unitOfWorkProvider.GetUnitOfWork())
{
    MyDomainObject myDomain = anotherService.getMyDomain(DateTime.Now, 100);
    unitOfWork.Db.Insert(myDomain); 
}

您当前尝试模拟的 IUnitOfWork 实例是其他一些实例。它们不是由这个 IUnitOfWorkProvider 生成的。

假设 IUnitOfWorkProvider 已注入您的 SUT,您应该能够冻结它并从那里开始。像这样的东西应该可以工作:

var fixture = new Fixture().Customize(new AutoMoqCustomization());
var uowProviderStub = fixture.Freeze<Mock<IUnitOfWorkProvider>>();
var uowMock = fixture.CreateAnonymous<Mock<IUnitOfWork>>();
var sut = fixture.CreateAnonymous<MyService>();

uowProviderStub.Setup(p => p.GetUnitOfWork()).Returns(uowMock.Object);
uowMock
    .Setup(x => x.Db.Insert(It.IsAny<MyDomainObject>()))
    .Verifiable();

// etc.

这有点麻烦,这真的是试图告诉你Law of Demeter violation不是最好的设计的测试......

【讨论】:

  • 那么你如何绕过这个 demeter 法则?我的意思是我的工作单元是如何设置的。
  • 要摆脱 IUnitOfWorkProvider,您只需将 IUnitOfWork 直接注入服务,并在您的 Composition Root 中管理服务和 UoW 的生命周期。
  • @chobo2 另一种方法是通过使用 AutoFixture.xUnit 和/或在幕后进行 uowProviderStub.Setup(p =&gt; p.GetUnitOfWork()).Returns(uowMock.Object); 位的自定义来将问题扫到地毯下。但请不要这样做 - 使用 Mark 的正确解决方案
  • 我还是不明白为什么它只是不使用同一个对象。我需要 UnitOfWorkProvider 的原因是制作更多的工作单元。我可能会多次调用我的服务中的方法,每个方法都需要自己的工作单元。
  • 您正在构建什么样的应用程序?网络应用?桌面应用?
【解决方案2】:

你需要让你的模拟调用可验证

unitOfWork
    .Setup(x => x.Db.Insert(It.IsAny<MyDomainObject>()))
    .Verifiable();

【讨论】:

  • 为什么?我试过了,它仍然失败。我仍然得到 Moq.MockException : Invocation was not executed on the mock: x => x.Db.Insert(It.IsAny())
  • MockException 还包含执行了哪些调用(如果有),您可以在此处粘贴完整的异常文本吗?
  • Moq.MockException :未在模拟上执行调用:x => x.Db.Insert(It.IsAny()) 在 Moq.Mock.ThrowVerifyException(IProxyCall 预期,表达式表达式, Times times) at Moq.Mock.VerifyCalls(Interceptor targetInterceptor, MethodCall expected, Expression expression, Times times) at Moq.Mock.Verify[T,TResult](Mock mock, Expression1 expression, Times times, String failMessage) at Moq.Mock1.Verify[TResult](Expression `1 表达式,Times times) 在第 393 行的 Tests.Services.ServiceTests.Test()
猜你喜欢
  • 2019-03-03
  • 2012-08-09
  • 1970-01-01
  • 2013-11-26
  • 1970-01-01
  • 1970-01-01
  • 2011-06-11
  • 2021-01-14
  • 1970-01-01
相关资源
最近更新 更多