This is the third installment of the “Building Enterprise Frameworks” series, which is the evolving design of an enterprise framework. In the series introduction, we presented a problem faced by many enterprise software teams and delivered a plan. The previous blog entry introduced the preliminary data access layer and domain model, which are a collection of abstractions forming a unified framework or infrastructure. In this blog entry, we refactor the data access layer and build the supporting unit tests.

Before covering testing, we will refactor the Repository introduced in the previous installment. At this time, we will eliminate the RepositoryBase abstract class. After testing and coding additional NHibernateRepositoryBase features, this class provided no immediate value to the enterprise framework. The abstract class was moved to the Framework Repository folder, which was vacated by the previous RepositoryBase. We also refactored the Generic interfaces and classes, which better aligns with our technical design and objectives. The following is the revised Framework project structure.

The following is the IRepository interface, which defines the required Repository implementation methods. As discussed in the previous installment, these are the basic Create-Read-Update-Delete (CRUD) operations.

01
using System;
02
using System.Collections.Generic;
03
using Joemwalton.Framework.Domain;
04
 
05
namespace Joemwalton.Framework.Data.Repository
06
{
07
    public interface IRepository<TEntity, TId>
08
        where TEntity : IEntity<TId>
09
    {
10
        void Save(TEntity entity);
11
        void Remove(TEntity entity);
12
        TEntity FindById(TId id);
13
        List<TEntity> FindAll();
14
    }
15
}

Since we eliminated the RepositoryBase abstract class, the NHibernateRepositoryBase will replace the method implementation for our NHibernate base class. The next installment of the “Building Enterprise Frameworks” series will focus on NHibernate and IoC, so the details of the design will not be discussed here – including session and transaction management. The base class will include the CRUD method implementation, so the concrete Repository implementations remain focused on providing domain specific data services. Basically…eliminating redundant code!

01
using System;
02
using System.Collections.Generic;
03
using NHibernate;
04
using NHibernate.Criterion;
05
using Joemwalton.Framework.Data.Repository;
06
using Joemwalton.Framework.Domain;
07
 
08
namespace Joemwalton.Framework.Data.NHibernate
09
{
10
    public abstract class NHibernateRepositoryBase<TEntity, TId> 
11
        : IRepository<TEntity, TId>
12
        where TEntity : IEntity<TId>
13
    {
14
        private ISessionFactory _sessionFactory;
15
 
16
        /// <summary>
17
        /// NHibernate Session Factory
18
        /// </summary>
19
        public ISessionFactory SessionFactory
20
        {
21
            protected get { return _sessionFactory; }
22
            set { _sessionFactory = value; }
23
        }
24
 
25
        /// <summary>
26
        /// Get current active session
27
        /// </summary>
28
        protected ISession CurrentSession
29
        {
30
            get { return this.SessionFactory.GetCurrentSession(); }
31
        }
32
 
33
        public TEntity FindById(TId id)
34
        {
35
            return this.CurrentSession.Get<TEntity>(id);
36
        }
37
 
38
        public List<TEntity> FindAll()
39
        {
40
            ICriteria query = this.CurrentSession.CreateCriteria(typeof(TEntity));
41
            return (List<TEntity>)query.List<TEntity>();
42
        }
43
 
44
        public void Save(TEntity entity)
45
        {
46
            using (ITransaction transaction = this.CurrentSession.BeginTransaction())
47
            {
48
                this.CurrentSession.SaveOrUpdate(entity);
49
                transaction.Commit();
50
            }
51
        }
52
 
53
        public void Remove(TEntity entity) 
54
        {
55
            using (ITransaction transaction = this.CurrentSession.BeginTransaction())
56
            {
57
                this.CurrentSession.Delete(entity);
58
                transaction.Commit();
59
            }
60
        }
61
    }
62
}

We are finished with the refactoring, so our attention shifts to building unit tests for our framework. As you noticed, we have no concrete classes – this is by design. So…how do we test interface and abstract classes? Why are we creating interfaces again?

The answer to question 2 is loose coupling and ability to test classes in isolation. This also promotes our core design principles and the supporting design patterns, which we covered in the “How to design and build better software for tomorrow?” series.

How do we test interface and abstract classes? The best approach is creating mock objects that implement the interfaces and inherit the base classes. The mock objects will mimic our concrete implementations, but with no real business logic. The following is the Framework Test project structure, which will contain our unit test and mock classes. 

The following is the EntityMock class, which inherits the EntityBase and defines the Generic type as int. This will represent the Id property type.

01
using System;
02
using Joemwalton.Framework.Domain;
03
 
04
namespace Joemwalton.Framework.Test.Mocks
05
{
06
    public class EntityMock 
07
        : EntityBase<int>
08
    {
09
        public EntityMock() 
10
        {
11
            this.Id = 1;
12
            Validate();
13
        }
14
 
15
        protected override void Validate()
16
        {
17
            this.FailedValidations.Clear();
18
            if (this.Id == 1)
19
                FailedValidations.Add("Testing");
20
        }
21
    }
22
}

The mock object contains an implementation for the constructor and Validate method, since the EntityBase handled the Id property. The following is the RepositoryMock class, which will focus on the relevant method implementation to support the framework.

01
using System;
02
using System.Collections.Generic;
03
using Joemwalton.Framework.Data.Repository;
04
using Joemwalton.Framework.Test.Mocks;
05
 
06
namespace Joemwalton.Framework.Test.Mocks
07
{
08
    public class RepositoryMock 
09
        : IRepository<EntityMock, int>
10
    {
11
        public void Save(EntityMock entity)
12
        {
13
            throw new NotImplementedException();
14
        }
15
 
16
        public void Remove(EntityMock entity)
17
        {
18
            throw new NotImplementedException();
19
        }
20
 
21
        public EntityMock FindById(int id)
22
        {
23
            return new EntityMock();
24
        }
25
 
26
        public List<EntityMock> FindAll()
27
        {
28
            return new List<EntityMock> { new EntityMock() };
29
        }
30
    }
31
}

The mock object implements the IRepository interface, where the contract defines the basic CRUD operations. The method implementation is not important, since we are not testing the ability of the Repository to retrieve or persist objects. The IRepository is a Generic interface, so the concrete entity type definition is required.

With the mock objects available, we can build our unit tests for the entity and Repository framework. In the following, the unit tests are based on the Microsoft Visual Studio Test libraries, although you can also build your tests with the open source NUnit framework. In either case, the goal is building the necessary unit tests for the framework.

01
using System;
02
using Microsoft.VisualStudio.TestTools.UnitTesting;
03
using Joemwalton.Framework.Data.Repository;
04
using Joemwalton.Framework.Test.Mocks; 
05
 
06
namespace Framework.Test
07
{
08
    [TestClass]
09
    public class EntityTest
10
    {
11
        public EntityTest() { }
12
 
13
        public TestContext TestContext { get; set; }
14
 
15
        [TestMethod]
16
        public void GetIdTest()
17
        {
18
            EntityMock mock = new EntityMock();
19
            int expected = 1;
20
            Assert.AreEqual(expected, mock.Id);
21
        }
22
 
23
        [TestMethod]
24
        public void SetIdTest()
25
        {
26
            EntityMock mock = new EntityMock();
27
            int expected = 2;
28
            mock.Id = expected;
29
            Assert.AreEqual(expected, mock.Id);
30
        }
31
 
32
        [TestMethod]
33
        public void GetFailedValidationsTest()
34
        {
35
            EntityMock mock = new EntityMock();
36
            Assert.AreEqual(1, mock.GetFailedValidations().Count);
37
            string expected = "Testing";
38
            Assert.AreEqual(expected, mock.GetFailedValidations()[0]);
39
        }
40
    }
41
}

In the above EntityTest class, we decorate the class with TestClass and test methods with TestMethod attributes. The test methods create an instance of the EntityMock and validate the results, although the EntityMock instantiation can also be accomplished during the test class initialization and shared for all test methods. The following is the RepositoryTest, which follows the same approach as the EntityTest.

01
using System;
02
using System.Text;
03
using System.Collections.Generic;
04
using System.Linq;
05
using Microsoft.VisualStudio.TestTools.UnitTesting;
06
using Joemwalton.Framework.Test.Mocks;
07
 
08
namespace Joemwalton.Framework.Test
09
{
10
    [TestClass]
11
    public class RepositoryTest
12
    {
13
        public RepositoryTest() { }
14
 
15
        public TestContext TestContext { get; set; }
16
 
17
        [TestMethod]
18
        [ExpectedException(typeof(NotImplementedException))]
19
        public void SaveTest()
20
        {
21
            RepositoryMock mock = new RepositoryMock();
22
            mock.Save(new EntityMock());
23
        }
24
 
25
        [TestMethod]
26
        [ExpectedException(typeof(NotImplementedException))]
27
        public void RemoveTest()
28
        {
29
            RepositoryMock mock = new RepositoryMock();
30
            mock.Remove(new EntityMock());
31
        }
32
 
33
        [TestMethod]
34
        public void FindByIdTest()
35
        {
36
            RepositoryMock mock = new RepositoryMock();
37
            EntityMock expected = new EntityMock();
38
            EntityMock actual = mock.FindById(1);
39
            Assert.AreEqual(expected.Id, actual.Id);
40
        }
41
 
42
        [TestMethod]
43
        public void FindAllTest()
44
        {
45
            RepositoryMock mock = new RepositoryMock();
46
            EntityMock expected = new EntityMock();
47
            List<EntityMock> actual = mock.FindAll();
48
            Assert.AreEqual(1, actual.Count);
49
            Assert.AreEqual(expected.Id, actual[0].Id);
50
        }
51
    }
52
}

The next step is run the tests. This is simple using Visual Studio, which includes several convenient options depending on your version. The Test menu or toolbar will provide an option to “Run All Tests in Solution”, which will run the tests and report the results. Alternatively, the Test View window will provide another interface to run tests.

In the Test View, you can highlight all tests and select “Run Selection” from the toolbar. This will execute the unit tests and display the information in the Test Results window, which appears below.

As you can see, the Test View window also provides several options to run and debug tests. Unfortunately, you will lose these handy features with an open source or non-Microsoft test tool.

In summary, we created mock objects for our domain model and Repository framework. Once we developed the mock objects, we created unit tests to ensure the relevant base implementation is working as expected. As we refactor the framework, the unit tests will ensure changes do not introduce bugs or break existing features. The introduction of a Continuous Integration (CI) process will further extend the test value with an event-driven build and test execution process. This can be accomplished using Microsoft Team Foundation Server (TFS), CruiseControl.NET, Team City or several other CI products.

What’s next? The next installment will focus on NHibernate including the SessionFactory, mapping and transaction management. This will segue into the IoC and Spring.NET support, which will provide many time saving NHibernate features.

Finally, I received several requests for a Java implementation. So…I am planning to build a Java equivalent enterprise framework solution. Thanks again for your comments and suggestions!!!!

Previous: Building Enterprise Frameworks – Data and Domain

Thinking Enterprise Architecture