2013-05-11 44 views
0

我是新来的NHibernate,甚至更新的版本来MOQ(或其他类似的框架)。在日夜搜索(谷歌+ stackoverflow +其他)后,我正在寻求帮助。起订量NHibernate的ISession.Get不叫模仿会话对象

的情况是(应该是)简单。我想单元测试一个使用NHibernate作为ORM层的C#WCF服务的调用。该方法在完成一些初始工作后,找到要连接的数据库,然后调用SessionProvider(会话工厂的管理器)为分片数据库返回nhibernate会话。然后我尝试使用ISession.Get <>()从数据库中检索一个对象,然后做一些工作。问题在于GUID(我在db中查找的条目的关键)是在调用开始时生成的,我无法知道它可能在WCF调用的范围之外。因此,我不能使用sqllite或其他技术来预先填充必要的数据来控制测试。我所希望的是,我可以以某种方式模拟(注入一个虚假的层?)对Session.Get的调用,以返回一个无效的对象,这将导致WCF调用抛出。

这里的测试代码片段:

var testRequest = ... (request DTO) 
var dummyBadObject = ... (entity in DB) 

var mock = new Mock<ISession>(MockBehavior.Strict); 
mock.Setup(m => m.Get<SampleObject>(It.IsAny<Guid>())).Returns(dummyBadObject); 

var exception = Assert.Throws<FaultException>(() => applicationService.SomeMethod(testRequest)); 
Assert.AreEqual(exception.Code.ToString(), SystemErrorFault.Code.ToString()); 

当我运行,而不是与模拟的Isession对象交互本次测试,应用服务代码调用实际的Isession对象的获取从会话工厂,可连接到数据库并获得正确的对象。似乎我错过了一些非常基本的关于模拟或注入的东西。任何帮助将不胜感激。

感谢, 肖恩

+0

你已经设置了一个模拟的ISession。但你在SessionProvider中注册的地方在哪里? – 2013-05-11 22:45:05

+0

SessionProvider是应用程序服务程序集的一部分,它在初始化时初始化静态会话提供程序,然后创建会话工厂高速缓存以按需提供会话。因此,我无法直接注册模拟会话,而无需在为我执行内部工作的应用程序服务上创建测试方法。我希望Moq能够为ISession做一个“全局”拦截,然后发出模拟会话。现在,这是我对Moq做什么的理解完全错误的地方。也许微软的假货可能是合适的? – Shawn 2013-05-12 02:33:42

回答

1

根据我们的意见,问题是,模拟考试是从你怎么对他们的看法完全不同。

他们不神奇拦截的从一个接口派生类的作品。它们只是它的动态实现。

创建Mock<ISession>是不是从创建实现ISession一类非常不同。您仍然需要将其注入依赖它的服务中。

您可能会有权审查你的整个堆栈,因为这样做的能力取决于一个良好的解耦设计。

提出建议阅读:Inversion of control

+0

谢谢!看起来我有很好的阅读能力,这很棒。 – Shawn 2013-05-12 15:12:41

0

我重新设计的部件在我的应用程序有哪些轮流持有ServiceContext对象中的所有其他(曾经被认为是静态的)应用程序所使用的组件。在这种情况下,这将是会话提供者(或ISessionFactory缓存),以及类似的WCF通道工厂缓存。不同之处在于,ServiceContext提供了方法来覆盖不同组件的默认实例,允许我使用模拟替换它们进行测试,并在测试完成时恢复原始组件。这使得我可以建立一个测试,我嘲笑一路从session缓存到ISession.Get /保存/载入等

var mockDatabaseSessionFactory = new Mock<DatabaseSessionManager>(MockBehavior.Strict); 
var mockSession = new Mock<ISession>(MockBehavior.Strict); 
var mockTransaction = new Mock<ITransaction>(MockBehavior.Strict); 

mockDatabaseSessionFactory.Setup(x => x.GetIndividualMapDbSession()).Returns(mockSession.Object); 
mockDatabaseSessionFactory.Setup(x => x.GetIndividualDbSession(It.IsAny<UInt32>())).Returns(mockSession.Object); 
mockDatabaseSessionFactory.Setup(x => x.Dispose()); 
mockSession.Setup(x => x.BeginTransaction()).Returns(mockTransaction.Object); 
mockSession.Setup(x => x.Dispose()); 
mockTransaction.Setup(x => x.Commit()); 
mockTransaction.Setup(x => x.Dispose()); 

// Setups to allow for the map insertion/deletion to pass 
mockSession.Setup(x => x.Get<IndividualMap>(It.IsAny<string>())).Returns((IndividualMap)null); 
mockSession.Setup(x => x.Load<IndividualMap>(It.IsAny<string>())).Returns((IndividualMap)null); 
mockSession.Setup(x => x.Save(It.IsAny<IndividualMap>())).Returns(new object()); 
mockSession.Setup(x => x.Delete(It.IsAny<IndividualMap>())); 

// Our test condition for this test: throw on attempt to save individual 
mockSession.Setup(x => x.Save(It.IsAny<Individual>())) 
    .Throws(new FaultException(ForcedTestFault.Reason, ForcedTestFault.Code)); 

// Test it - but be sure to back up the previous database session factory 
var originalDbSessionFactory = ServiceContext.DatabaseSessionManager; 
ServiceContext.OverrideDatabaseSessionManager(mockDatabaseSessionFactory.Object); 
try 
{ 
    var exception = Assert.Throws<FaultException>(() => applicationService.AddIndividual(addIndividualRequest)); 
    Assert.IsTrue(ForcedTestFault.Code.Name.Equals(exception.Code.Name)); 
} 
catch (Exception) 
{ 
    // Restore the original database session factory before rethrowing 
    ServiceContext.OverrideDatabaseSessionManager(originalDbSessionFactory); 
    throw; 
} 

ServiceContext.OverrideDatabaseSessionManager(originalDbSessionFactory); 
ServiceContext.CommunicationManager.CloseChannel(applicationService); 

幸运的是,代码设计是不是太糟糕O_O :)所以我重新考虑了这一点,现在代码覆盖率达到了100!感谢迭戈让我朝着正确的方向前进。