Introduction

 

People often ask me if I use MVC or WebForms for my Microsoft ASP.NET projects.  I always tell them MVC, but I don’t use it in the classic way that most others do.  That is to say, I emit no html from my views, and for that matter really don’t even use views (with the exception of one view which is essentially my entire app.   People often refer to this as a SPA or Single-Page-Application. What this basically means is that my entire view is built with JavaScript (in my case Sencha’s ExtJS) and the only interaction with the server is to simply bring down Json results.

In this post, I’m going to build a simple app following this principle. It’s purpose will be to take a big pile of text, chop it up into words, then output it in some c# friendly manner.  My motivation for doing this is I happen to need that functionality right now and instead of creating a little awk script to do it, I’m going to right a web site!

 

So, Here is what we will build.  A handy formatter! (and you can run it at:   http://reformatter.peterkellner.net/ )

 

image

image

 

 

Part I, The Visual Studio Project

 

Let’s first create the Visual Studio project.  My development these days is actually using the latest Windows 8 Consumer build with the beta of Visual Studio 2011 running on it.  The nice thing about this is because Microsoft added “Round Tripping” to VS2011, I can now use VS2011 to create the project and you, my happy readers, can download the project and open it in Visual Studio 2010.  This has been long in coming and Microsoft has finally done it for the newest version of Visual Studio.  Thank you Microsoft!

 

image

 

And with an Internet Application with Razor (though I really don’t need all that extra stuff, it does not hurt)

 

image

 

The structure created is as as shown below with a Controller and Views folder and all the proper routing setup.

 

image

 

Because I want to make a very simple MVC app that only emits Json, Let’s create our own empty controller called ReformatterController.  We do that by right clicking on the Controller folder and pressing “Add Controller”, then “Add Empty Controller”.

image

 

Now, let’s create a very very simple controller with just one method that returns a JSON Result.  I’m returning the data in a friendly format to  Sencha’s ExtJS (or SenchaTouch for that matter).  JQuery would be similar.  Here is our new controller class (I’m sure my little tokenizer, line maker could be improved, but I’m just doing this to show some real work.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.Mvc;

namespace MvcLiteApp.Controllers
{
    public class ReformatterController : Controller
    {

        public JsonResult ConvertTextToFriendlyText(string inputText)
        {
            const int maxCharactersInLine = 30;
            bool success;
            string messageReturn;
            var outputDataList = new List<string>();

            try
            {
                var words = inputText.Split(' ').ToList();
                var newLine = new StringBuilder();
                foreach (var word in words)
                {
                    if (newLine.Length + word.Length < maxCharactersInLine &&
                        word.Length < maxCharactersInLine)
                    {
                        newLine.Append(" ");
                        newLine.Append(word);
                    }
                    else
                    {
                        outputDataList.Add(newLine.ToString().Trim());
                        newLine = new StringBuilder();
                        newLine.Append(word);
                    }
                }
                success = true;
                messageReturn = "";
            }
            catch (Exception e)
            {
                messageReturn = e.ToString();
                success = false;
            }

            return Json(new
            {
                Success = success,
                Data = outputDataList,
                message = messageReturn
            }, JsonRequestBehavior.AllowGet);
        }

    }
}

Now, let’s feed it a URL and see what happens.  Here is a sample URL:

http://localhost:3600/Reformatter/ConvertTextToFriendlyText?inputText=Romeo%20Montague%20and%20Juliet%20Capulet%20meet%20and%20fall%20in%20love%20in%20Shakespeare’s%20lyrical%20tale%20of%20%22star-cross’d%22%20lovers.

I actually pasted the sentence in to the the browser’s URL line and it added the nice encoded spaces for me.  The output is as follows:

{"Success":true,"Data":["Romeo Montague and Juliet","Capulet meet and fall in love","in Shakespeare\u0027s lyrical tale"],"message":""}

Notice that it created the nice lines formatted as a JavaScript array!

Next step, let’s make a real JavaScript program out of this.

 

Part 2, Our SPA (Single Page App) That Does Real Work

 

I’m only going to explain here the part of the Sencha Program that actually consumes the data but first, let’s add a little “properness” to our controller so it responds only to POST.  That is, we add an attribute to the top of the method and we change the behavior attribute on the bottom to say to not allow get.

        [HttpPost]
        public JsonResult ConvertTextToFriendlyText(string inputText)
        {
            .......
            return Json(new
            {
                Success = success,
                Data = outputDataList,
                message = messageReturn
            }, JsonRequestBehavior.DenyGet);
        }

And, the JavaScript in the SPA is very straight forward and is here:

var beforeTextAreaComponent = Ext.getCmp('BeforeFormatTextAreaId');
textBefore = beforeTextAreaComponent.getValue();

// how to not pollute global name space here?
var stringbuildervalue = Ext.getCmp('stringbuilderid').getValue();
var javascriptarrayvalue = Ext.getCmp('javascriptarrayid').getValue();
var noformattingvalue = Ext.getCmp('noformattingid').getValue();
var outputwidthvalue = Ext.getCmp('outputwidthid').getValue();

Ext.Ajax.request({
    url: 'ReFormatter/ConvertTextToFriendlyText',
    params: {
        inputText: textBefore,
        stringbuilder: stringbuildervalue,
        javascriptarray: javascriptarrayvalue,
        noformatting: noformattingvalue,
        outputwidth: outputwidthvalue
    },
    success: function(response){
        var localData = Ext.JSON.decode(response.responseText).Data;
        var numberRows = localData.length;
        var newData = '';
        for (var i=0;i<numberRows;i++) {
            newData += localData[i] + '\n';
        }

        var afterTextAreaComponent = Ext.getCmp('AfterFormatTextAreaId');
        afterTextAreaComponent.setValue(newData);

    },
    failure: function(error) {
        alert('error');
    }

});

Now, in reality, I’ve added some useful stuff to the controller (and put the project here for your review, comments and suggestions).

Visual Studio Project TextReformatter.zip

Introduction

Many of us know we should be using test first development, however it is hard to break old habits.  I have to admit, I started to solve this particular problem I’m going to use an an example first without test first, then realized what a pickle I was going to be in proving to myself it worked.  So, I thought, why not blog my experience as I do it.  Using Visual Studio unit testing makes this pretty easy.

 

The Problem

I’m currently building a multithreaded email processor and part of that process is I have to figure out, for any given use whether they are supposed to have there email server checked.  So, the way I look at it, I need a method that takes in the following parameter.

    1. Now – This is actually todays date and time in UTC, but since this method need to be tested, I’m going to pass in now rather just use it directly.
    2. LastActivityOfUserDateTime – This is the last time we saw any activity for this user.
    3. LastEmailSession – This is when the email session was last run (server check).
    4. DefaultSecondsForLastActivityTreshold – This is used to determine weather the user last activity makes that user considered active or inactive.  That is, say this is 60 seconds.  Then, if the user has been active in the last 60 seconds, then this user is considered active.
    5. DefaultSecondsBetweenEmailRetryOutsideThreshold – If the user is not active (as defined by DefaultSecondsForLastActivityTreshold) then this is the number of seconds we should wait before we should try and contact the users email server again.
    6. DefaultSecondsBetweenEmailRetryInsideThreshold – If the user is active (as defined by DefaultSecondsForLastActivityTreshold) then this is the number of seconds we should wait before we should try and contact the users email server again.

Given these 4 parameters, we should write a method that returns a Boolean indicating weather the mail server needs to be rechecked for a given user.

 

The Method To Test

So, let’s write a method signature.  We will use this in our real project, then write some tests to see if it works correctly.  We have not written the actual method yet, we are just defining what it does, then first, very important, writing the tests to prove it will work.  This way, as we add all the corner cases, we can keep making sure the original cases we programmed have not broken.

So, here is how I see the class definition:

public bool IsUserReadyToContactEmailServer(DateTime currentDateTime,
            DateTime lastActivityOfUserDateTime,
            DateTime lastEmailSession,
            int defaultSecondsForLastActivityTreshold,
            int defaultSecondsBetweenEmailRetryOutsideThreshold,
            int defaultSecondsBetweenEmailRetryInsideThreshold)
        {
            bool activeUser = IsUserCurrentlyActive(currentDateTime, lastActivityOfUserDateTime,
                                                      defaultSecondsForLastActivityTreshold);
            int thresholdForRetrySeconds =
                activeUser
                    ? defaultSecondsBetweenEmailRetryInsideThreshold
                    : defaultSecondsBetweenEmailRetryOutsideThreshold;

            // this has to be a double because could be huge number if last email send very long ago
            double timeSinceLastEmailSessionSeconds =
                currentDateTime.Subtract(lastEmailSession).TotalSeconds;

           return timeSinceLastEmailSessionSeconds > thresholdForRetrySeconds;
        }

Creating a Test Project In Visual Studio 2011 Preview

Now that we have our method we want to test, let’s create a test project.  This is very straight forward.  We first just say “File/New Project” then choose “Unit Test Project”.

 

image

 

I’m going to simply create a method called TestMethodIsUserReadyToContactEmailServer().

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace AEWeb.Tests
{
    [TestClass]
    public class UnitTest1
    {
        [TestMethod]
        public void TestMethodIsUserReadyToContactEmailServer()
        {
        }
    }
}

Now, we need to add some guts.  There is an automated way to create the actual test stubs, but at the moment I can’t seem to find it.  In this case, we really just need to test a bunch of corner cases so let’s code the tests up. Below are the tests I’ve come up with including comments that make them self explanatory.

So, as I’m creating my tests, I realize I need to create an extra class to help this one.  That is specifically, I need to add a method that simply determines if the user is currently active.  I’m going to call that IsCurrentlyActive and have it take three paramaters.  currentDateTime, DefaultSecondsForLastActivityThreshold and usersLastActivityDate.  I realized this because solving the full problem of figuring out if the user is ready to sync email is to complex in one step.

If I were developing this without unit tests, I would have figured the same thing out, but then as I built it, I would not be verifying it’s correctness with testing.

Creating Stub Classes

So, my new class I want to test will be this:

 /// <summary>
        /// This really just a supporting method for the above IsUserReadyToContactEmailServer call
        /// </summary>
        /// <param name="currentDateTime">Current datetime (for testing can be anything)</param>
        /// <param name="usersLastActivityDate">time user was last seen </param>
        /// <param name="defaultSecondsForLastActivityTreshold">our definition of what makes a user currently active</param>
        /// <returns></returns>
        public bool IsUserCurrentlyActive(DateTime currentDateTime,
            DateTime usersLastActivityDate,
            int defaultSecondsForLastActivityTreshold
            )
        {

            return true;
        }

The Unit Tests Themselves

So, now is time to actually write the tests.  I’m not going to explain here all the mechanics of testing.  I assume you understand the basics are you right calls to methods, then make assertions for correctness.  If the assertion is wrong, then print that message and of course the test fails.

So, Here are my test cases for just the helper method (IsUserCurrentlyActive).

using System;
using AELib;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace AEWeb.Tests
{
    [TestClass]
    public class UnitTestEmailReadyCheck
    {

        [TestMethod]
        public void UnitTestRecentActivity()
        {
            {
                // a long time ago
                var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
                var lastActivityOfUserDateTime = new DateTime(2012, 2, 1, 12, 0, 0);
                // last activity was a month ago, 2/1/2012 at noon
                // threshhold for calling a user active
                const int defaultSecondsForLastActivityTreshold = 60*15; // let's call this 15 minute threshhold
                bool isActive = new MailServerReadyCheck().
                    IsUserCurrentlyActive(currentDateTime, lastActivityOfUserDateTime,
                                          defaultSecondsForLastActivityTreshold);
                Assert.IsFalse(isActive,
                               "User Should Be Inactive because last activity was 1 month ago and threshhold is 15 minutes");
            }
        }

        [TestMethod]
        public void UnitTestNoRecentActivity()
        {
            // a just inside threshold
            var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
            var lastActivityOfUserDateTime = new DateTime(2012, 3, 1, 11, 55, 0);
            // last activity was 5 minutes before noon on 2/1/2012
            // threshhold for calling a user active
            const int defaultSecondsForLastActivityTreshold = 60*15; // let's call this 15 minute threshhold
            bool isActive = new MailServerReadyCheck().
                IsUserCurrentlyActive(currentDateTime, lastActivityOfUserDateTime,
                                      defaultSecondsForLastActivityTreshold);
            Assert.IsTrue(isActive,
                          "User Should Be active because last activity was 5 minutes ago and threshhold is 15 minutes");
        }

        [TestMethod]
        public void UnitTestCurrentDateBehindActivity()
        {
            // impossible cause of last activity being after current time. just need to
            // make sure it returns true and does not crash
            var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
            var lastActivityOfUserDateTime = new DateTime(2012, 3, 1, 18, 0, 0);
            // last activity was 5 minutes after noon on 2/1/2012
            // threshhold for calling a user active
            const int defaultSecondsForLastActivityTreshold = 60*15; // let's call this 15 minute threshhold
            bool isActive = new MailServerReadyCheck().
                IsUserCurrentlyActive(currentDateTime, lastActivityOfUserDateTime,
                                      defaultSecondsForLastActivityTreshold);
            Assert.IsTrue(isActive,
                          "User Should Be active because last activity was 5 minutes after current time");
        }
    }
}

 

Then, I write the actual method:

 

       /// <summary>
        /// This really just a supporting method for the above IsUserReadyToContactEmailServer call
        /// </summary>
        /// <param name="currentDateTime">Current datetime (for testing can be anything)</param>
        /// <param name="usersLastActivityDate">time user was last seen </param>
        /// <param name="defaultSecondsForLastActivityTreshold">our definition of what makes a user currently active</param>
        /// <returns></returns>
        public bool IsUserCurrentlyActive(DateTime currentDateTime,
            DateTime usersLastActivityDate,
            int defaultSecondsForLastActivityTreshold
            )
        {
            int secondsSinceLastActivity = Convert.ToInt32(currentDateTime.Subtract(usersLastActivityDate).TotalSeconds);
            bool activeStatus = defaultSecondsForLastActivityTreshold > secondsSinceLastActivity;
            return activeStatus;
        }

And finally, run the test (skipping a little debugging to make them all work)

Presto!

 

image

 

For completeness, I’m pasting the full unit test for the other main class I’m interested in testing below (as well as the class itself), however I won’t go into all the details.  Everything is basically the same, just more of it

 

Conclusions and Observations

Turns out, this was quite a bit of work to generate all these tests.  At first pass, I can hear someone saying it seems like a lot to do just for two fairly simple methods.  My answer is that I struggled trying to get it right until I finally decided to write the tests.  Now, I’m confident it works correctly.  What is more, if there is a bug in my algorithm, or something else comes up I need to include (which is very likely) I now have a great way to prove to myself (and others) that my class works.  Also, this serves as great documentation for what expectations are for this very important function.

 

Appendex – Full Unit Test And Class Source

 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace AELib
{
    public class MailServerReadyCheck
    {
        /// <summary>
        /// 
        /// </summary>
        /// <param name="currentDateTime">This is actually todays date and time in UTC, but since 
        ///    this method need to be tested, I’m going to pass in now rather just use it directly</param>
        /// <param name="lastActivityOfUserDateTime"> This is used to determine weather the user last activity
        ///      makes that user considered active or inactive.  That is, say this is 60 seconds.
        ///      Then, if the user has been active in the last 60 seconds, then this user is considered active.</param>
        /// <param name="lastEmailSession">When the last email completed for this user</param>
        /// <param name="defaultSecondsForLastActivityTreshold">This is used to determine weather the user last activity makes that 
        ///      user considered active or inactive.  That is, say this is 60 seconds.  Then, if the user has 
        ///      been active in the last 60 seconds, then this user is considered active.</param>
        /// <param name="defaultSecondsBetweenEmailRetryOutsideThreshold"> If the user is not active (as defined by 
        ///      DefaultSecondsForLastActivityTreshold) then this is the number of seconds we should wait before 
        ///      we should try and contact the users email server again.</param>
        /// <param name="defaultSecondsBetweenEmailRetryInsideThreshold">If the user is active (as defined by 
        ///      DefaultSecondsForLastActivityTreshold) then this is the number of seconds we should wait 
        ///      before we should try and contact the users email server again.</param>
        /// <returns>returns a boolean indicating weather the mail server needs to be rechecked for a given user.</returns>
        public bool IsUserReadyToContactEmailServer(DateTime currentDateTime,
            DateTime lastActivityOfUserDateTime,
            DateTime lastEmailSession,
            int defaultSecondsForLastActivityTreshold,
            int defaultSecondsBetweenEmailRetryOutsideThreshold,
            int defaultSecondsBetweenEmailRetryInsideThreshold)
        {
            bool activeUser = IsUserCurrentlyActive(currentDateTime, lastActivityOfUserDateTime,
                                                      defaultSecondsForLastActivityTreshold);
            int thresholdForRetrySeconds =
                activeUser
                    ? defaultSecondsBetweenEmailRetryInsideThreshold
                    : defaultSecondsBetweenEmailRetryOutsideThreshold;

            // this has to be a double because could be huge number if last email send very long ago
            double timeSinceLastEmailSessionSeconds =
                currentDateTime.Subtract(lastEmailSession).TotalSeconds;

           return timeSinceLastEmailSessionSeconds > thresholdForRetrySeconds;
        }

        /// <summary>
        /// This really just a supporting method for the above IsUserReadyToContactEmailServer call
        /// </summary>
        /// <param name="currentDateTime">Current datetime (for testing can be anything)</param>
        /// <param name="usersLastActivityDate">time user was last seen </param>
        /// <param name="defaultSecondsForLastActivityTreshold">our definition of what makes a user currently active</param>
        /// <returns></returns>
        public bool IsUserCurrentlyActive(DateTime currentDateTime,
            DateTime usersLastActivityDate,
            int defaultSecondsForLastActivityTreshold
            )
        {
            int secondsSinceLastActivity = Convert.ToInt32(currentDateTime.Subtract(usersLastActivityDate).TotalSeconds);
            bool activeStatus = defaultSecondsForLastActivityTreshold > secondsSinceLastActivity;
            return activeStatus;
        }
    }
}

And the tests…

 

using System;
using AELib;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace AEWeb.Tests
{
    [TestClass]
    public class UnitTestEmailReadyCheck
    {

        ///////////////////// IsUserCurrentlyActive follows

        [TestMethod]
        public void UnitTestRecentActivity()
        {
            {
                // a long time ago
                var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
                var lastActivityOfUserDateTime = new DateTime(2012, 2, 1, 12, 0, 0);
                // last activity was a month ago, 2/1/2012 at noon
                // threshhold for calling a user active
                const int defaultSecondsForLastActivityTreshold = 60*15; // let's call this 15 minute threshhold
                bool isActive = new MailServerReadyCheck().
                    IsUserCurrentlyActive(currentDateTime, lastActivityOfUserDateTime,
                                          defaultSecondsForLastActivityTreshold);
                Assert.IsFalse(isActive,
                               "User Should Be Inactive because last activity was 1 month ago and threshhold is 15 minutes");
            }
        }

        [TestMethod]
        public void UnitTestNoRecentActivity()
        {
            // a just inside threshold
            var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
            var lastActivityOfUserDateTime = new DateTime(2012, 3, 1, 11, 55, 0);
            // last activity was 5 minutes before noon on 2/1/2012
            // threshhold for calling a user active
            const int defaultSecondsForLastActivityTreshold = 60*15; // let's call this 15 minute threshhold
            bool isActive = new MailServerReadyCheck().
                IsUserCurrentlyActive(currentDateTime, lastActivityOfUserDateTime,
                                      defaultSecondsForLastActivityTreshold);
            Assert.IsTrue(isActive,
                          "User Should Be active because last activity was 5 minutes ago and threshhold is 15 minutes");
        }

        [TestMethod]
        public void UnitTestCurrentDateBehindActivity()
        {
            // impossible cause of last activity being after current time. just need to
            // make sure it returns true and does not crash
            var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
            var lastActivityOfUserDateTime = new DateTime(2012, 3, 1, 18, 0, 0);
            // last activity was 5 minutes after noon on 2/1/2012
            // threshhold for calling a user active
            const int defaultSecondsForLastActivityTreshold = 60*15; // let's call this 15 minute threshhold
            bool isActive = new MailServerReadyCheck().
                IsUserCurrentlyActive(currentDateTime, lastActivityOfUserDateTime,
                                      defaultSecondsForLastActivityTreshold);
            Assert.IsTrue(isActive,
                          "User Should Be active because last activity was 5 minutes after current time");
        }

        ///////////////////// IsUserReadyToContactEmailServer follows

        [TestMethod]
        public void UnitTestIsUserReadyToContactEmailServerLongTimeAgoUserInActive()
        {
            // let's use these parameters for every test

            // let's call this 30 minute threshhold. no contact within 30 minutes, than user is inactive
            const int defaultSecondsForLastActivityTreshold = 60 * 30;

            // if active user, check email every 2 minutes
            const int defaultSecondsBetweenEmailRetryInsideThreshold = 60 * 2;

            // if inactive user, check email every 20 minutes
            const int defaultSecondsBetweenEmailRetryOutsideThreshold = 60 * 20; 

            // user checked email 5 hours ago and email has not been run for 3 hours
            var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
            var lastEmailSessionDateTime = new DateTime(2011,3,1,2, 0, 0); // email session 2am of 1 year ago (very long ago)
            var lastActivityOfUserDateTime = new DateTime(2011, 3, 1, 7, 0, 0); // user checked email at 7am a year ago

            bool emailSessionNeeded = new MailServerReadyCheck().IsUserReadyToContactEmailServer(currentDateTime,
                lastActivityOfUserDateTime,lastEmailSessionDateTime,defaultSecondsForLastActivityTreshold,
                defaultSecondsBetweenEmailRetryOutsideThreshold,
                defaultSecondsBetweenEmailRetryInsideThreshold);

            Assert.IsTrue(emailSessionNeeded,
                          "user checked email 5 hours ago and email has not been run for 3 hours. Should have needed email session");
        }

        [TestMethod]
        public void UnitTestIsUserReadyToContactEmailServerLongTimeAgoUserActive()
        {
            // let's use these parameters for every test
            // let's call this 30 minute threshhold. no contact within 30 minutes, than user is inactive
            const int defaultSecondsForLastActivityTreshold = 60 * 30;

            // if active user, check email every 2 minutes
            const int defaultSecondsBetweenEmailRetryInsideThreshold = 60 * 2;

            // if inactive user, check email every 20 minutes
            const int defaultSecondsBetweenEmailRetryOutsideThreshold = 60 * 20;

            // user checked email 1 minute ago and email has not been run for 3 hours
            var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
            var lastEmailSessionDateTime = new DateTime(2011, 3, 1, 2, 0, 0); // email session 2am of 1 year ago (very long ago)
            var lastActivityOfUserDateTime = new DateTime(2012, 3, 1, 11, 59, 0); // user checked email 1 minute ago

            bool emailSessionNeeded = new MailServerReadyCheck().IsUserReadyToContactEmailServer(currentDateTime,
                lastActivityOfUserDateTime, lastEmailSessionDateTime, defaultSecondsForLastActivityTreshold,
                defaultSecondsBetweenEmailRetryOutsideThreshold,
                defaultSecondsBetweenEmailRetryInsideThreshold);

            Assert.IsTrue(emailSessionNeeded,
                          "user checked email  1 minute ago and email not been run for 1 year. should have run email again");
        }

        [TestMethod]
        public void UnitTestIsUserReadyToContactEmailServerRecentlyUserActive()
        {
            // let's use these parameters for every test
            // let's call this 30 minute threshhold. no contact within 30 minutes, than user is inactive
            const int defaultSecondsForLastActivityTreshold = 60 * 30;

            // if active user, check email every 2 minutes
            const int defaultSecondsBetweenEmailRetryInsideThreshold = 60 * 2;

            // if inactive user, check email every 20 minutes
            const int defaultSecondsBetweenEmailRetryOutsideThreshold = 60 * 20;

            // user checked email 1 minute ago and email has not been run for 3 hours
            var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
            var lastEmailSessionDateTime = new DateTime(2012, 3, 1, 11, 50, 0); // email session 10 minutes ago
            var lastActivityOfUserDateTime = new DateTime(2012, 3, 1, 11, 59, 0); // user checked email 1 minute ago

            bool emailSessionNeeded = new MailServerReadyCheck().IsUserReadyToContactEmailServer(currentDateTime,
                lastActivityOfUserDateTime, lastEmailSessionDateTime, defaultSecondsForLastActivityTreshold,
                defaultSecondsBetweenEmailRetryOutsideThreshold,
                defaultSecondsBetweenEmailRetryInsideThreshold);

            Assert.IsTrue(emailSessionNeeded,
                          "active user, email checked 10 minutes ago but since active should check again");
        }

        [TestMethod]
        public void UnitTestIsUserReadyToContactEmailServerRecentlyUserInActive()
        {
            // let's use these parameters for every test
            // let's call this 30 minute threshhold. no contact within 30 minutes, than user is inactive
            const int defaultSecondsForLastActivityTreshold = 60 * 30;

            // if active user, check email every 2 minutes
            const int defaultSecondsBetweenEmailRetryInsideThreshold = 60 * 2;

            // if inactive user, check email every 20 minutes
            const int defaultSecondsBetweenEmailRetryOutsideThreshold = 60 * 20;

            // user checked email 1 minute ago and email has not been run for 3 hours
            var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
            var lastEmailSessionDateTime = new DateTime(2012, 3, 1, 11, 50, 0); // email session 10 minutes ago
            var lastActivityOfUserDateTime = new DateTime(2011, 3, 1, 11, 59, 0); // user checked email 1 year ago

            bool emailSessionNeeded = new MailServerReadyCheck().IsUserReadyToContactEmailServer(currentDateTime,
                lastActivityOfUserDateTime, lastEmailSessionDateTime, defaultSecondsForLastActivityTreshold,
                defaultSecondsBetweenEmailRetryOutsideThreshold,
                defaultSecondsBetweenEmailRetryInsideThreshold);

            Assert.IsFalse(emailSessionNeeded,
                          "inactive user, but email checked very recently so should not be checking again");
        }

        [TestMethod]
        public void UnitTestIsUserReadyToContactEmailServerEmailLastSession10MinutesagoActive()
        {
            // let's use these parameters for every test
            // let's call this 30 minute threshhold. no contact within 30 minutes, than user is inactive
            const int defaultSecondsForLastActivityTreshold = 60 * 30;

            // if active user, check email every 2 minutes
            const int defaultSecondsBetweenEmailRetryInsideThreshold = 60 * 2;

            // if inactive user, check email every 20 minutes
            const int defaultSecondsBetweenEmailRetryOutsideThreshold = 60 * 20;

            // user checked email 1 minute ago and email has not been run for 3 hours
            var currentDateTime = new DateTime(2012, 3, 1, 12, 0, 0); // assume current time is 3/1/2012 at noon
            var lastEmailSessionDateTime = new DateTime(2012, 3, 1, 1, 11, 50); // email session 10 minutes ago
            var lastActivityOfUserDateTime = new DateTime(2011, 3, 1, 11, 59, 0); // user checked email 5 minutes ago

            bool emailSessionNeeded = new MailServerReadyCheck().IsUserReadyToContactEmailServer(currentDateTime,
                lastActivityOfUserDateTime, lastEmailSessionDateTime, defaultSecondsForLastActivityTreshold,
                defaultSecondsBetweenEmailRetryOutsideThreshold,
                defaultSecondsBetweenEmailRetryInsideThreshold);

            Assert.IsTrue(emailSessionNeeded,
                          "active user, but email checked very recently so should be checking again");
        }

    }
}

 

And the results of all tests running Smile

image

 

And, if you are still reading, I’ve got 100% of these two methods covered from these unit tests using Code Coverage.

 

image

 

 


© 2012 PeterKellner.net. All Rights Reserved
Follow

Get every new post delivered to your Inbox

Join other followers: