Designing for testability – an valid architecture choice? (Part 2)

This is second part of design for testability series of posts.  First part of the series covered initial separation of concerns between manager and provider classes, so in case you haven’t read that already, jump here and read it first, because the story here starts where it ended there. 

Redesign step 2 – IUserProvider

The redesign covered in this bog post would be based on  service stub design pattern. I have already blogged about that pattern so here I would just summarize it as a pattern where direct interaction between two types becomes indirect communication of one class with interface of the second class which decouples second class concrete implementation from communication.

If we would take a look at line 21 of the UserManger class code from last code sample of previous post we would see this line

userCollection = UserProvider.GetUserCollection(userName);

we could see that UserManager is tightly coupled to a UserProvider class and due to the fact that UserProvider talks to DB that prevents easy testing of the UserManager business logic,  because making of any test has a requisite of performing DB related setup.

Provider changes

To get rid of that DB constraint, we would need to define an IUserProvider interface which would have members of the UserProvider class and which UserManager would reference in line 17.

But, right at the start there is a problem:  our provider class is currently static internal class and interfaces are not applicable on any static type members, so to define user provider interface we first had to remove static modifiers from class and method definition.
At the end,  result is provider class implementing provider interface:

 image[46]

UserManager changes

In UserManager class, we would define a static field of the IUserProvider type which would be by default initialized to an UserProvider instance and which NumberOfUsersActiveInLast10Days method would then use that field to call the provider method instead of UserProvider class

We would expose an internal setter property which would set the value of the _userProvider instance field.

Manager code would look then like this

using System;
using System.Collections.Generic;

namespace DAL
{
    public static class UserManager
    {
        private static readonly IDictionary <string , IList<user>> _userCache
            = new Dictionary<string , IList<user>>();
        private static IUserProvider _userProvider=new UserProvider();

        internal static IUserProvider UserProvider
        {
            set { _userProvider = value; }
        }

        public static int NumberOfUsersActiveInLast10Days(string userName)
        {
            if (string.IsNullOrEmpty(userName))
                throw new ArgumentException("User id not sent");

            IList userCollection;
            if (_userCache.ContainsKey(userName))
                userCollection = _userCache[userName];
            else
            {
                userCollection = _userProvider.GetUserCollection(userName);
            }

            int result = 0;
            foreach (User user in userCollection)
            {
                if (user.LastActivity > DateTime.Now.AddDays(-10))
                {
                    result++;
                }
            }
            return result;
        }
    }
}

In line 8, I there is field of type IUserProvider containing the UserProvider instance

In line 12-15, there is internal setter only UserProvider property which can be used to replace the DB dependable  UserProvider with some mocked/stubbed type implementing the IUserProvider.
All what is needed to be done is to set the UserManager.UserProvider=something and the user manager would use that instead of UserProvider class functionality.

In line 27, UserManager is now using the IUserProvider field value instead of the UserProvider class, decoupling by that manager and provider classes .

Deciding to have internal UserProvider property is solving the request of not exposing internals to the code using class but at the same time it is preventing also test fixture class to use it too 😦

Exposing internal members in right amount

Luckily, NET 2.0 offers very easy workaround for this problem and all what is needed is adding of an simple assembly attribute which would declare internal members of the DAL assembly visible to the assembly containing the tests.

We need to open AssemblyInfo.cs  file

image

Presuming our test assembly would be DAL.Test.dll we need to add next assembly attribute definition:

   1: [assembly: InternalsVisibleTo("DAL.Test.dll")]

The result of that setting applied, would be that UserProvider property would be accessible in the Test assembly but not visible to any other assembly. That’s how testing the business logic of UserManager.NumberOfActiveUsersInLast10  method could be performed without any DB related set ups because we would just set up UserProvider internal property of the UserManager class to some desired stubbed and mocked provider class

Doing that, we designed for testability (enabled stub/mock  provider injection) and avoid exposing exposing internals to “outer space”

What’s wrong with this code?

Let’s imagine that there is also CompanyManager class with method GetActiveUsers which calls the UserManager.NumberOfActiveUsersInLast10 days for users which name starts with A, B and C and returns the list of their results. CompanyManager class could look in that case like this

using System.Collections.Generic;

namespace DAL
{
    public class CompanyManager
    {
        private IUserManager _userManager = new UserManager();

        internal IUserManager UserManager
        {
            set { _userManager=value; }
        }


        public IList GetActiveUsers()
        {
            IList result = new List();
            result.Add(_userManager.NumberOfUsersActiveInLast10Days("A"));
            result.Add(_userManager.NumberOfUsersActiveInLast10Days("B"));
            result.Add(_userManager.NumberOfUsersActiveInLast10Days("C"));
            return result;
        }
    }
}

With current code design,if CompanyManager and UserManager would be in the same DAl.Test.dll we could try to use the fact that internals of user manager would be accessible so the test could look something like this

        [Test]
        public void GetActiveUsers_TestCaseOfZeroUsers()
        {
            IUserProvider userProvider= mockRepository.DynamicMock<IUserProvider>();
            Expect.Call(userProvider.GetUserCollection(null))
                .IgnoreArguments()
                .Return(new List<User>());

            mockRepository.ReplayAll();

            UserManager.UserProvider = userProvider;
            IList<int> results= CompanyManager.GetActiveUsers();
            Assert.IsTrue(results.Count==0);
        }

In line 4, we are (courtesy of RhinoMocks) creating a dummy user provider based on IUserProvider interface.

In line 5-7, we are mocking the desired behavior of the user provider – to return empty collection of users (more about mocking: here and  here )

In line 8, we are setting the UserProvider to the mocked provider.

So, as we can see it is possible to mock user manager  dependency and solve the problem but IMHO it is unacceptable solution because to test the company manager business logic we are forced to know and rely on internal concepts of user manager. Beside the fact that this doesn’t look cool in our example, in real world there are  usually more then one dependency, so to test the CompanyManager we would have to know and set up internals of a lot of external components, which would make effective writing tests in real world mission impossible.

(to be continued)

Advertisements

Posted on December 2, 2007, in Uncategorized. Bookmark the permalink. Leave a comment.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: