These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 
Author Message
 Post subject: Unit Testing, Mock, NHibernate
PostPosted: Wed Aug 30, 2006 3:56 pm 
Beginner
Beginner

Joined: Thu Aug 17, 2006 3:20 pm
Posts: 21
Maybe I am biting off more than I can chew here, but i am having problems unit testing NHibernate

I just started using Test Drive Development and NHibernate, thats why i say maybe I am biting off more than I can chew,

I can load all the configurations, load the session, start a transaction and insert records into a sandbox database, but now i am having problems applying unit testing to this.

I saw some people using NMock, does this eliminate the need to use NHibernate to perform unit testing?

I think I have all the tools needed, but i'm not sure where to go from here, it seems very painstaking to use NHibernate in a unit testing environment.

Any suggestions?

Thanks


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 31, 2006 12:25 am 
Beginner
Beginner

Joined: Mon Nov 21, 2005 6:38 pm
Posts: 30
Location: New Zealand
Unless you are explicitly testing that your NHibernate session modifies your DB as expected, then IMHO your tests will be more robust to mock out NHibernate.

This way you don't have to worry about the state of the DB, DB connections, etc, which can be a pain when your unit tests run on multiple machines.

We use DotNetMock's DynamicMock (and our own mock classes when DynamicMock won't cut it).


Top
 Profile  
 
 Post subject:
PostPosted: Thu Aug 31, 2006 3:13 am 
Beginner
Beginner

Joined: Sun Jun 19, 2005 2:21 pm
Posts: 21
DeanOC,

Can you help us out here and share an example code on how you mock out NHibernate classes?

I am currently trying to understand how object mocking works and how can I apply this to my NHibernate related projects easily. A practical example surely would shed some light on me :-)

Regards,
Robert


Top
 Profile  
 
 Post subject: Mockery
PostPosted: Fri Sep 01, 2006 2:03 am 
Senior
Senior

Joined: Mon Aug 21, 2006 9:18 am
Posts: 179
I typically don't mock out the NHibernate objects directly, but rather the objects that consume the managers I have for creating the ISessionFactory and ISession.
For example, I have an interface called ISessionManager like so
Code:
public interface ISessionManager
{
   ISession CreateSession();
}

The implementation of this would BuildSessionFactory (thus, it would be a Singleton) and also grab an ISession when I ask for it (scoping according to THreadLocal or HttpContext or CallCOntext).
Now, really when you are testing your business entities you shouldn't need to mock any of this since you would typically decouple the NHibernate specifics away from your Domain anyways.
So if I have an IRepository like so
Code:
public interface IRepository<T>
{
   void Save();
  void Delete();
  void ScratchMyButt();

}


My implementation of IRepository may CONSUME my ISessionManager implementation (or just ISession), but my unit testing doesn't need to know all that if I am just testing the entities served by my REpository.

That's where mocking comes in.

I can use some Mock library (I use RhinoMocks or NMock2) to mock the Repository like so:

Code:
Mockery mocks = new Mockery();
IRepository<Thing> repos = mocks.CreateMock<IRepository<Thing>>();


Then I can place expectations on the methods to return static values or whatever like so:
Code:
Thing dummyObject = new Thing();
Expect.On(repos).Method("Get").Will(Return.Value(dummyObject));


Then I can inject this repos into my Service object or whatever and run the test as expected without ever connecting to the DB.

This reallly has nothing to do with NHibernate at this point...just TDD.

Hope this helps...
MIKE
[/b]


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 01, 2006 9:29 am 
Beginner
Beginner

Joined: Thu Aug 17, 2006 3:20 pm
Posts: 21
I don't really follow the idea of your respository.

I have setup NMock to mock my domain objects like so:

Code:
    public interface INUnitSession {

        object FindById(Type type, object id);

    }


    public abstract class MockSession {

        protected Mockery mocks;
        protected INUnitSession session;

        protected virtual void SetUp() {
            mocks = new Mockery();
            session = mocks.NewMock<INUnitSession>();
        }

        protected virtual void TearDown() {
            mocks = null;
            session = null;
        }

    }

Fixture Code:
Code:
            Expect.Once.On(session).Method("FindById").With(typeof(Tech), 1).Will(Return.Value(new Tech()));
            Tech tech = (Tech)session.FindById(typeof(Tech), 1);


Maybe I am doing something along the same lines. I already have an HTTPModule setup to store the ISession to the HttpContext. That way, each request is only one transaction, at the end of the request, if there is any changes it persists it to the DB.

Is this the general consensus?

I was trying to make it so each fixture isnt really depending on other objects in my DomainModel.

thanks!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 01, 2006 1:36 pm 
Senior
Senior

Joined: Mon Aug 21, 2006 9:18 am
Posts: 179
I am not sure what you are trying to test. If you are doing integration testing to make sure your methods are persisting your domain objects as they should then you need to make sure while testing that your ISession stuff that it is running in ThreadLocal and not HttpContext since (obviously) there is no HttpContext while running the Unit Tests. I have an SessionManager object that takes care of these details for me using CallContext, thanks to Billy McCafferty (google his name).
Here is the class that might help:
Code:
    /// <summary>
    /// Handles creation and management of sessions and transactions.  It is a singleton because
    /// building the initial session factory is very expensive. Inspiration for this class came
    /// from Chapter 8 of Hibernate in Action by Bauer and King.  Although it is a sealed singleton
    /// you can use TypeMock (http://www.typemock.com) for more flexible testing.
    /// </summary>
    public sealed class NHibernateSessionManager
    {
        #region Thread-safe, lazy Singleton

        /// <summary>
        /// This is a thread-safe, lazy singleton.  See http://www.yoda.arachsys.com/csharp/singleton.html
        /// for more details about its implementation.
        /// </summary>
        public static NHibernateSessionManager Instance
        {
            get
            {
                return Nested.nHibernateSessionManager;
            }
        }

        /// <summary>
        /// Initializes the NHibernate session factory upon instantiation.
        /// </summary>
        private NHibernateSessionManager()
        {
            InitSessionFactory();
        }

        /// <summary>
        /// Assists with ensuring thread-safe, lazy singleton
        /// </summary>
        private class Nested
        {
            static Nested() { }
            internal static readonly NHibernateSessionManager nHibernateSessionManager = new NHibernateSessionManager();
        }

        #endregion

        private void InitSessionFactory()
        {
            NHibernate.Cfg.Configuration cfg = new NHibernate.Cfg.Configuration();

            cfg.Configure();
            try
            {
                sessionFactory = cfg.BuildSessionFactory();
            }
            catch (Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.Message, "HIBERNATER");
                throw;
            }
        }

        /// <summary>
        /// Allows you to register an interceptor on a new session.  This may not be called if there is already
        /// an open session attached to the HttpContext.  If you have an interceptor to be used, modify
        /// the HttpModule to call this before calling BeginTransaction().
        /// </summary>
        public void RegisterInterceptor(IInterceptor interceptor)
        {
            ISession session = threadSession;

            if (session != null && session.IsOpen)
            {
                throw new CacheException("You cannot register an interceptor once a session has already been opened");
            }

            GetSession(interceptor);
        }
        public ISession GetSession()
        {
            return GetSession(null);
        }

        /// <summary>
        /// Gets a session with or without an interceptor.  This method is not called directly; instead,
        /// it gets invoked from other public methods.
        /// </summary>
        private ISession GetSession(IInterceptor interceptor)
        {
            ISession session = threadSession;

            if (session == null)
            {
                if (interceptor != null)
                {
                    session = sessionFactory.OpenSession(interceptor);
                }
                else
                {
                    session = sessionFactory.OpenSession();
                }

                threadSession = session;
            }

            return session;
        }

        public void CloseSession()
        {
            ISession session = threadSession;
            threadSession = null;

            if (session != null && session.IsOpen)
            {
                session.Close();
            }
        }

        public void BeginTransaction()
        {
            ITransaction transaction = threadTransaction;

            if (transaction == null)
            {
                transaction = GetSession().BeginTransaction();
                threadTransaction = transaction;
            }
        }

        public void CommitTransaction()
        {
            ITransaction transaction = threadTransaction;
           
            try
            {
                if (transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack)
                {
                    transaction.Commit();
                    threadTransaction = null;
                }
            }
            catch (Exception ex)
            {
                RollbackTransaction();
                //This rethrow of exception would go unhandled and is unecessary since we are using DTO.Notifications for exception messaging
               // throw new PersistenceException("Transaction.Commit Failed.See Inner Exception for details.", ex);
            }
        }

        public void RollbackTransaction()
        {
            ITransaction transaction = threadTransaction;

            try
            {
                threadTransaction = null;

                if (transaction != null && !transaction.WasCommitted && !transaction.WasRolledBack)
                {
                    transaction.Rollback();
                }
            }
            catch (Exception ex)
            {
                throw new PersistenceException("Transaction.Rollback Failed.See Inner Exception for details.", ex);
            }
            finally
            {
                CloseSession();
            }
        }

        private ITransaction threadTransaction
        {
            get
            {
                return (ITransaction)CallContext.GetData("THREAD_TRANSACTION");
            }
            set
            {
                CallContext.SetData("THREAD_TRANSACTION", value);
            }
        }

        private ISession threadSession
        {
            get
            {
                return (ISession)CallContext.GetData("THREAD_SESSION");
            }
            set
            {
                CallContext.SetData("THREAD_SESSION", value);
            }
        }

        private ISessionFactory sessionFactory;
    }


Notice that the threadSession is keeping ISession in CallContext. You can mock up doing an HttpContext during unittesting but I understand it's hairy ... I've never needed to try it.

_________________
If this helped...please remember to rate it!


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 posts ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.