Translate

Sunday 23 March 2014

Agile Test Driven Design - Design evolving out of tests

Pre-requisites before you read this - Knowledge of the WCF framework, Web Service Consumption, Unit and Mock Tests and a basic understanding of the difference between a HTTP session context and an examination context (time allotted for tests, questions etc) and a lot of detached reading ability (Nothing directed at anyone personally even though it may seem so) :)

The code and the tests are executable-ready and tested so please do not troll with useless questions or responses.

Agile and the pleasure of Test Driven Design Development

Agile is not so much a pleasure when working with an Agile team as it is a pain when confronted with argumentative, subjective 'technical' experts; when the latter happens, you begin to realize how difficult it is to explain simplicity to these 'expert' project managers to accept their shortcomings and accept that they are not project managers but experts in creating crises and then managing it !

Documentation is 'Tests'

Documentation is the 'D' word - it is like the threat of a nuclear holocaust, the moment the word crops up in a team it is indicative that some reactive measures has crept into the development steps adopted by the team.

The incident of a tester not knowing how to open a VS TS and work with builds but still having the 'confidence' to pull up developers is an indicator of how some believe that having a 'team' writing some nonsense lines on Skype group chat or referring to each other as 'team' is what a team means and that 'testers' have read somewhere that customers write tests in Agile and therefore, being a tester means that the tester is a customer !

There are better PJs doing the rounds than having to encounter such 'poor jokes' within your work environment and that, too, which stinks so much!

Getting back to the D word - the best form of documentation that a team could be blessed with is when a test (Unit, mock or acceptance) 'tells' or 'communicates' how the design of the class, the signature of the method or even the data type of a data member should be.

It is really fortunate that I came across a 'real' example where I could demonstrate how Tests actually works, communicates and evolves a design into a near perfect one - and in far less time than you could conceive of.

I am extracting a simple service out of the whole system below to show how tests should be used to design, right from the beginning of a project.

The requirement

A service model (WCF) that enables a candidate to log in to an online examination application with a service contract as,

using System.ServiceModel;

namespace ABC.LoginService
{
    [ServiceContract]
    public interface ILoginService
    {
        [OperationContract]
        UserInfo DoLogin(string email, string password);

        [OperationContract]
        CandidateSessionInfo GetCandidateSession(UserInfo userInfo);
    }
}

and two data contracts for Candidate's session (not the HTTP session) and user information.

namespace ABC.Service
{
    [DataContract]
    public class CandidateSessionInfo 
    {
        int _id;
        string _title;
        DateTime _startDate;
        DateTime _startTime;
        DateTime _endTime;

        [DataMember]
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        [DataMember]
        public string Title
        {
            get { return _title; }
            set { _title = value; }
        }

        [DataMember]
        public DateTime StartDate
        {
            get { return _startDate; }
            set { _startDate = value; }
        }

        [DataMember]
        public DateTime StartTime
        {
            get { return _startTime; }
            set { _startTime = value; }
        }

        [DataMember]
        public DateTime EndTime
        {
            get { return _endTime; }
            set { _endTime = value; }
        }
    }

    [DataContract]
    public class UserInfo
    {
        int _id;
        string _name;
        string _email;

        [DataMember]
        public int ID
        {
            get { return _id; }
            set { _id = value; }
        }

        [DataMember]
        public string Name
        {
            get { return _name; }
            set { _name = value; }
        }

        [DataMember]
        public string Email
        {
            get { return _email; }
            set { _email = value; }
        }
    }
}

WYTNWYG (What You Think Is Not What You Get) 

This is an example of preconceived notions i.e., WYTNWYG  of Design principles. 

The service contract is simple - it outlines two basic requirements of a login operation but the design begins to go awry in the data contracts that is to provide the login infastructure. 

Let me demonstrate how, through the below tests written in Rhino Mock and NUnit.

using Rhino.Mocks;
using ABC.Service;
using NUnit.Framework;

#if DEBUG
using WebOperationContext = System.ServiceModel.Web.MockedWebOperationContext;
#endif

namespace ABC.MockServices
{
    [TestFixture]
    public class ServicesTests
    {
        WebServiceClient client;
        ILoginService sstudentMock;
        UserInfo studentUnderTest,obj;
        int loginId;
        CandidateSessionInfo sessionObj, sessionUnderTest;

        [SetUp]
        public void init()
        {
            sstudentMock = MockRepository.GenerateMock();
            client = new WebServiceClient(sstudentMock);
        }
        [Test]
        public void testStudentLogin()
        {
            studentUnderTest = new UserInfo();

            studentUnderTest.Email = "abc@gmail.com";
            studentUnderTest.ID = 1;
            studentUnderTest.Name = "abc";
            sstudentMock.Expect(t => t.DoLogin("abc@gmail.com", "pass")).Return(studentUnderTest);
            loginId = client.Login("abc@gmail.com", "pass");
            Assert.AreEqual(studentUnderTest.ID, loginId);
            sstudentMock.VerifyAllExpectations();
        }
        //[Test]
        //public void testSessionForLoginTime()
        //{
        //    sessionUnderTest = new SessionInfo();
        //    sessionUnderTest.ID = 1;
        //    sessionUnderTest.Title = "abcSession";
        //    sessionUnderTest.StartDate = DateTime.Now;
        //    sessionUnderTest.StartTime = DateTime.Now;
        //    sessionUnderTest.EndTime = DateTime.MaxValue;
        //    loginId = client.Login("abc@gmail.com", "pass");
        //    sstudentMock.Expect(t => t.GetSession(obj)).Return(sessionUnderTest);
        //   // sessionObj=student.GetSession(obj);

        //   // Assert.AreEqual(sessionUnderTest.StartTime, sessionObj.StartTime);
        //    //sstudentMock.VerifyAllExpectations();
        //}
        //[Test]
        //public void testSessionForLoginSessionTitle()
        //{
        //    sessionUnderTest = new SessionInfo();
        //    sessionUnderTest.ID = 1;
        //    sessionUnderTest.Title = "AbcSession";
        //    sessionUnderTest.StartDate = DateTime.Now;
        //    sessionUnderTest.StartTime = DateTime.Now;
        //    sessionUnderTest.EndTime = DateTime.MaxValue;
        //    loginId = client.Login("abc@gmail.com", "pass");
        //    sstudentMock.Expect(t => t.GetSession(obj)).Return(sessionUnderTest);
        //    //sessionObj = student.GetSession(obj);           
        //   // Assert.AreEqual(sessionUnderTest.Title, sessionObj.Title);
        //    //sstudentMock.VerifyAllExpectations();
        //}

    }
}

The first test, testStudentLogin, is fine but it is when you move to the next user story - 'getSession...' that the tests, related to the CandidateSessionInfo object tells you, "Hey, as per your design, you need to supply a UserInfo object, where is it?" 

Because 

1. Being on the web platform, you need to send the userinfo object to the getsession...method after the candidate logs in successfully or 
2. Refactor the design.

It is not as simple as choosing between the two - a design, when you arrive or decide it, must have 'testable' artifacts to justify the decision and this is where 'tabled design' fails and test driven design scores!

The test above has communicated (the feedback) that either the contract of the getsession...method is wrong or your programming logic that is not able to maintain the user info object state.

The answer now becomes simple. Your development efforts (and therefore the logic part) has not even started so obviously the choice is clear - refactor the operation contract. (Of course, this explanation of how to make this design decision is only for explanatory purposes - the actual parameters to making this decision could be entirely different based upon the composition of a team or the architect's experience and maturity.)

To  continue...with explanation on why the tests are commented out.

Friday 21 March 2014

Holi Hai ! :) List of Colors - C#

Working with colors is always fun but for that you must have the colors first.

It is like an old joke of mine -

To be or not to be a Macbeth is not the question; the real question is, Lady Macbeth has to be for Macbeth to be or not to be!

So, when I ventured to have a user select a color from a drop-down list for a software module, I was a little surprised (like the cat in the tree on seeing a dog climbing up to say hello to her) that it was not as simple as that.

Lady Macbeth may have known how to poison her husband but she had the poison and that is the crux of the matter - a resourceful woman, for her times!

Then I chanced upon the fact (Google search is almost a fact now) that Reflection (as in System.Reflection) had the solution but the real problems are faced only when you work towards the solution.

Lady Macbeth, too, must have thought it would be hunky-dory all the way until she found  that she really could not taste the poison herself to know what quantity to give to Macbeth ! :D

It is not as if she had to mix Coke or Pepsi with some other drink - she really could not know the taste of poison so, how could she ensure that her prey, her husband, would not find it distasteful and throw it away before drinking it?

The problem is that System.Drawing.Color is not really an enumeration that can be enumerated upon as it does not have a colors collection! So, how to compare the color values after the user inputs the color, when asked "Tippi, tippi, tap, what color do you want?" ?

The solution is to compare the name of the colors by storing the 'reflected' colors into an array of strings and then compare using a foreach loop to match the selected color.

Below is the code:

public class MyColorList{

private string[] colors = null;
private string colorSelected = null;

            private void listAllColors(){
                int colorIndex = 0;
                System.Type colorType = typeof(System.Drawing.Color);
                System.Reflection.PropertyInfo[] colorInfoList =                                                                                               colorType.GetProperties(System.Reflection.BindingFlags.Static |
                    System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Public);

                colors = new string[colorInfoList.Length];

                foreach (System.Reflection.PropertyInfo propertyColor in colorInfoList) {
                    colors[colorIndex] = propertyColor .Name ;
                    colorIndex++;
                }
            }
            private string getColor(string colorName)
            {
                foreach (string color in colors)
                {
                    if (colorName.ToLower() == color.ToLower())
                        colorSelected= color;
                }
                return colorSelected;
            }
}
// Method Calls
            listAllColors();
            currentColor = getColor(ColorAsString);

(c) Of course, needless to say, what happened between Lady Macbeth and Macbeth was their own private affair and none of the world's business but because Shakespeare disagreed with the view that I thought of highlighting it, safe with the knowledge that it was probably not as private as the English Royalty would have it, if it was discussed so vividly in public.