Alexander Beletsky's development blog

My profession is engineering

Web development: ASP.NET Ajax, ScriptManager to recover a password

Remember a site we started to create in previous articles. What we’ve got is simple web application, with a registration functionality. During registration you submit email, password and secret phrase. We going to use this secret phrase today to recover forgotten passwords. Functionality is bit artifictial but my goal is to demonstrate usage of ASP.NET Ajax framework, namely ScripManager object as very usefully component for ajaxifying your applications.

Preparation


I’ve added another page called ForgetPassword.aspx, it looks like this:


Script Manager is really powerfull component and one of major feature is how it simplifies working with web services. So, let’s add ScriptManager to application. The place to do that are typically MasterPage to make ScrpitManager be accessible from every content page. It have to be placed in ‘form’ with runat=”server” attribute, otherwise runtime exception will appear on page loading.

<div class="content">
 <div id="left">
   <form id="form1" runat="server">
      <asp:ScriptManager ID="ScriptManager" runat="server">
        <Scripts>
          <asp:ScriptReference Path="~/js/forgetpassword.js" ScriptMode="Auto" />
        </Scripts>
        <Services>
          <asp:ServiceReference Path="~/services/ForgetPassword.asmx" />
        </Services>
      </asp:ScriptManager>
             
      <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
      </asp:ContentPlaceHolder>
    </form> 
 </div>  

* This source code was highlighted with Source Code Highlighter.


Script Manager contains two references - ScriptReference, ServerReference. It is a declarative way to add references, in the same time it is possible to do it programmatically (that is more complicated). What you need to do is to add such declarations, the rest will be done by control. Next, we will add forgetpassword.js script to script folder and new web service instance to services folder. Now, application ready for AJAX!

Ajaxify It


Before we do something more serious let’s check that setup we created are actually working. Go to webservice code (ForgetPassword.asmx.cs):

namespace WebApplication.Services
{
  /// <summary>
  /// Summary description for ForgetPassword
  /// </summary>
  [WebService(Namespace = "http://tempuri.org/")]
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  // To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
  [System.Web.Script.Services.ScriptService]
  public class ForgetPassword : System.Web.Services.WebService
  {
    [WebMethod]
    public string HelloWorld()
    {
      return "Hello World";
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Please note, there is attribute of service class [System.Web.Script.Services.ScriptService] that must be uncommented as soon as webservice is being used from java script code. Do not forget to uncomment it. And simple method is present there, HelloWorld that just returns a string. On a ForgetPassword.aspx page we have such form:

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
  <h2>Please fill form below</h2>
  <div class="box">
    <label>Email: </label><asp:TextBox ID="EmailText" runat="server"></asp:TextBox>
    <label>Secret Phrase: </label> <asp:TextBox ID="SecretPhraseText" runat="server"></asp:TextBox>
    <label>&nbsp</label> <input id="forgot" type="button" value="Forget password" class="button" onclick="onForgotPasswordClick()"/> 
  </div>
  <span id="Password"></span>
</asp:Content>

* This source code was highlighted with Source Code Highlighter.


By pressing on ‘forget’ button onForgotPasswordClick() javascript function will be called (this is the place where we would do AJAX calls). Currently the code is really simple:

function onForgotPasswordClick() {
  WebApplication.Services.ForgetPassword.HelloWorld(onMethodSucceeded, onMethodFailed);
}

function onMethodSucceeded(results, args) {
  alert(results);
}

function onMethodFailed(error) {
}

* This source code was highlighted with Source Code Highlighter.


One one line of code in onForgotPasswordClick() do a bit complex things. It register a call to web service (you have to reference web service with a full class name), HelloWorld does not have any arguments, so we are passing nothing, but register 2 callback functions. One that will be called on successfull finish of AJAX call, second will be called on any failure. Even it might seems no so ‘magic’ ones who created AJAX application without any frameworks would notice how less code have to be created to accomplish this. Finally, we displaying results in alert window. Just run application, click ‘Forgot password’ button and see:



Real work


As soon as we see its OK, let’s do some real work. If users email and secret pharase matches corresponding record in database, we going to show user password on screen without reloading of page. We will add new business object and implementation for recovery of password. I would just paste tests and code of business object (as it is really small and simple one), the rest implementation you will be able to see by going to GitHub. (for ones who followed my previous articlies - I’ve done some refactoring switching to Repository pattern for DAL, since it is more correct and reusable way of working with data, please see details in code).

Tests:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using Company.Product.BLL.Tests.Mocks;
using NUnit.Framework.SyntaxHelpers;
using Company.Product.DAL;

namespace Company.Product.BLL.Tests
{
  [TestFixture]
  public class ForgotPasswordTests
  {
    [Test]
    public void Smoke()
    {
      //INIT
      var data = new ForgotPasswordDataMock();

      //ACT/POST
      var c = new ForgotPassword(data);
    }

    [Test]
    public void RestorePassword_NoUser()
    {
      //INIT
      var data = new ForgotPasswordDataMock();
      var forgot = new ForgotPassword(data);

      //ACT
      var password = forgot.RestorePassword("no such user", "secret");

      //POST
      Assert.That(password, Is.Null, "It is not possible to return password for non-existing user.");
    }

    [Test]
    public void RestorePassword_WrongSecretPhrase()
    {
      //INIT
      var data = new ForgotPasswordDataMock();
      var forgot = new ForgotPassword(data);

      data.InsertUser(new User() { Email = "user@a.com", Password = "password", SecretPhrase = "correct secret" });

      //ACT
      var password = forgot.RestorePassword("user@a.com", "wrong secret");

      //POST
      Assert.That(password, Is.Null, "It is not possible to return password if wrong secret phrase used.");
    }

    [Test]
    public void RestorePassword_CorrectUserAndSecret()
    {
      //INIT
      var data = new ForgotPasswordDataMock();
      var forgot = new ForgotPassword(data);

      var expectedPassword = "password";
      data.InsertUser(new User() { Email = "user@a.com", Password = expectedPassword, SecretPhrase = "correct secret" });

      //ACT
      var password = forgot.RestorePassword("user@a.com", "correct secret");

      //POST
      Assert.That(password, Is.EqualTo(expectedPassword), "Wrong password value returned.");
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Company.Product.DAL;

namespace Company.Product.BLL
{
  public class ForgotPassword
  {
    private IUsersRepository _data;

    public ForgotPassword(IUsersRepository data)
    {
      _data = data;
    }

    public string RestorePassword(string email, string secret)
    {
      var user = _data.FindUserByEmail(email);

      if (user == null)
        return null;

      if (user.SecretPhrase == secret)
        return user.Password;

      return null;
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


After it is ready and tested we are going to integrate with object to Web Service method call.

/// <summary>
/// Summary description for ForgetPassword
/// </summary>
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
[System.Web.Script.Services.ScriptService]
public class ForgotPasswordService : System.Web.Services.WebService
{
  [WebMethod]
  public string RestorePassword(string email, string secret)
  {
    var forgotPassword = new ForgotPassword(new UsersRepository());

    return forgotPassword.RestorePassword(email, secret);
  }
}

* This source code was highlighted with Source Code Highlighter.


After that it is time to use Web Service from javascript code. I’m using jQuery library to work with divs, classes and so on. jQuery does not require any special to add about it, it is so popular that it has been included to VS 2010 and Microsoft is contributing to jQuery as well. Code uses simple and cool effect of fadeOut slideDown effect, just to make application bit more attactive.

function onForgotPasswordClick() {
  var email = $('#email').val();
  var secret = $('#secret').val();

  $("#resultsdiv").fadeOut("fast", function() {
    removeClasses();
    WebApplication.Services.ForgotPasswordService.RestorePassword(email, secret, onMethodSucceeded, onMethodFailed);  
  });  
}

function onMethodSucceeded(results, args) {
  var password = results;

  if (password == null) {
    $("#results").addClass("red");
    $("#results").html("Your email or secret phrase is invalid");   
  }
  else {
    $("#results").addClass("green");
    $("#results").html("Your password: " + password);
  }

  $("#resultsdiv").slideDown("fast");
}

function onMethodFailed(error) {
}

function removeClasses() {
  if ($("#results").hasClass("green")) {
    $("#results").removeClass("green");
  } else if ($("#results").hasClass("red")) {
    $("#results").removeClass("red");
  }
}

* This source code was highlighted with Source Code Highlighter.


Results


As soon as I input some wrong data into email/secret phase control, I’ll see fadding out div that informs me about problem. Like that,



But if I put correct values where, I’ll get my password back:



Conclusions


Nowadays it is even hard to imagine serious web application with out AJAX. It is mostly everywhere. Because of such usabilty and popularity a lot of vendors proposing tools and framework to hanlde common AJAX issues. And of cause, tools for working with AJAX is started to be included to .NET framework. ScriptManager is only one part of it framework. For futher exploration I would recomend to look on ASP.net AJAX Toolkit that is build upon .Net framework abilitites and propose a lot of interesting controls. Moreover, it is open source and you can go ahead and hack it or contribute it.

Sources


Sources are available on GitHub: http://github.com/alexbeletsky/Concept

Bookmark and Share

Gallio runs MbUnit tests very slowly?

Currently I’m looking close to one interesting project called Subtext. It being developed and managed by Phil Haack and recently he announced release of version 2.5. It open-souce so anyone is able to get SVN snapshot and digg into code and even to contribute to this project. I would like to do a separate blog post on Subtext, but now I want to touch another topic.

Subtext project uses MbUnit test framework, that I have no chance to work before. So as my first goal I started to look for tools to work with MbUnit and ways of its integration to Visual Studio. Obvious choice was a Gallio. Gallio is open-source automation test framework, a bundle contains *maybe* all known unit-test systems (NUnit, MbUnit, NCover, TypeMock etc.) as well as add-on to Visual Studio that allows to run MbUnit tests by Testdriven.net runner.

I was preatty happy to find that one exists. I’ve installed the version that official page refered to Gallio v3.1 Update 2 build 397 - x86. Installed on VS 2008 no problem and I’ve started to play with Subtext tests. But I’ve noticed that it works very slow, namely run of particular test takes about 20 sec on my machine. That is too much time for ordinar test. At first I blamed Subtext that it performs test database initialization in [SetUp] function of test assembly. But I just commented out this initialization I saw that situation has not changed.

I googled a bit I found that Oliver Sturm also experienced same problem and did a post on his blog. He did a great job and noticed that reason for such slow down was gallio.utility.exe application that hangs for about 20 sec on each start. Tests are using that for initialization so it slows down each start of tests. He had created a bug report and as Jeff Brown the problem was fixed for v3.2.380. I went to a distribution site and downloaded latest 3.2 vesrion. I’ve installed it and try to run test. Woow, it was run for just a second! Good news I thought! But when I tried to run all tests from a test assembly I was disappointed that it simply does not work. It says about exception during initialization of Gallio. I reported on that with new issue to Gallio.

I’ve decied to try earlier verions of 3.2.. But still had the same problem. So, 3.2 realease does not work on my machine at all and do not know the reason. My only choice was to back to 3.1 but try some more recent daily builds. I’ve got Version 3.1 build 514 and finally it started to work as expected. Test case run took about 4 seconds, slower that 3.2 456 but anyway, not so bad as for 3.1 397.

Gallio is preatty good software, so I really recommed to to check it out. 3.1 514 works fine on Visual Studio 2008.

Hope this one will be useful to one who is going to start with Gallio or met same problem as me.

An Architectural Design Challenge by James Shore

This is a response to James Shore blog entry. James has started “An Architectural Design Challenge” to create a simple application, but with architecture on top of the mind.

James is a famous blogger and author of great book that I’m reading online “The art of Agile development”. I suggest you both familarize with blog and book.

So, he is proposing to took an “An Architectural Design Challenge”. It is required to create a small application that should expose some major architectural aspects of software development. I’ve decied to take part of this challange also. Task has been posted awhile ago, unfortunately I’ve been busy with another stuff, but as a saying goes “better later than never”.

Requirements are:

  1. Write the ROT-13 tool described above. Note that the input file must be loaded entirely into memory.
  2. Provide a configuration mechanism that reads and writes files from one directory when “in testing” and another directory when “in production.”
  3. Wrap your platform’s console and file I/O methods as if each call actually required a dozen lines of code. Your configuration mechanism can use file I/O directly, though.
  4. No slideware allowed. You can use diagrams to illustrate your architecture, but you must code the entire solution. This prevents seemingly-elegant solutions that don’t work in practice.
  5. Use test-driven development to code everything.


I would not say that I’m using some special technics or patterns, just following most simple implementation and tests.

Preparation



As always starting with new design I try to bring it down to 3 major piecies: Business Function, Data, Presentation. So, I’ve added new project Rot13.Core.Library with 3 folders in it - BLL, DAL, Presentation. BLL is a heart will contain implementation of all functions required by application, DAL will provide those functions with required data and Presentation is responsibe how to display information to user. Right after that I’ve added a corresponding test project that would contain all required tests, as we are following TDD strictly tests will be first we we would create. So, solution look like:



Functions



Well, what we need is function that is able to get some input stream, do transformation of the data extracted from stream and send data back to output stream. This ROT13 algorigtm is core function, without all other architectural stuff is senceless, so let’s start and implement it!

ROT13 is primitive cipher, wikipedia contail a lot info about it.

After a several interations of testing and refactoring and came up with such implementation:
Tests:

using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Rot13.Core.Library.BLL;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Rot13.Core.Library.Tests.BLL_Tests
{
  /// <summary>
  /// Summary description for TransformTests
  /// </summary>

  [TestClass]
  public class Rot13Tests
  {
    private static void TestCipher(char[] input, char[] expected)
    {
      //ACT
      char[] output = Rot13Algorithm.Process(input);

      //POST
      CollectionAssert.AreEqual(expected, output);
    }


    [TestMethod]
    public void CipherSmallTest()
    {
      //INIT
      var input = new char[] {
        'a', 'b', 'c', 'd'
      };
      var expected = new char[] {
        'n', 'o', 'p', 'q'
      };

      TestCipher(input, expected);
    }

    [TestMethod]
    public void CipherFullTest()
    {
      //INIT
      var input = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
      var expected = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm";

      TestCipher(input.ToCharArray(), expected.ToCharArray());
    }

    [TestMethod]
    public void CipherTestFromJamesArcticle()
    {
      var input = "The dog barks at midnight.";
      var expected = "Gur qbt onexf ng zvqavtug.";

      TestCipher(input.ToCharArray(), expected.ToCharArray());
    }

    [TestMethod]
    public void CipherRevertTransform()
    {
      var input = "The dog barks at midnight.";
      var expected = "Gur qbt onexf ng zvqavtug.";

      TestCipher(input.ToCharArray(), expected.ToCharArray());
      TestCipher(expected.ToCharArray(), input.ToCharArray());
    }
  }
}

* This source code was highlighted with Source Code Highlighter.

Rot13 (preatty small, heh?):

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

namespace Rot13.Core.Library.BLL
{
  public class Rot13Algorithm
  {
    private static string _original = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    private static string _lookup = "NOPQRSTUVWXYZABCDEFGHIJKLMnopqrstuvwxyzabcdefghijklm";
    
    public static char[] Process(char[] input)
    {
      return (from s in input let position = _original.IndexOf(s) select ((position != -1) ? _lookup[position] : s)).ToArray();
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Configuration


Now, what I need is to make application configurable. It is OK if we put the configuration to a special config file and have a class to work with configuration data. My preference is to have configurations as singletone’s (in the same time I realize some major drawbacks of singletone’s and try to void usage of it, but let’s keep most simple implementation in this solution). Implementation uses Linq to XML to handle XML data. Let’s not forget about one of requirements.. we have several types of configuration: Test, Production. We could decide on run-time what current configuration is active.

Configuration file.

<?xml version="1.0" encoding="utf-8" ?>
<configurations>
 <active type="test" />
 
 <configuration type="test">
  <input val="./testInput"/>
  <output val="./testOutput"/>
 </configuration>
 
 <configuration type="production">
  <input val="./input"/>
  <output val="./output"/>
 </configuration>
</configurations>

* This source code was highlighted with Source Code Highlighter.


using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Rot13.Core.Library.Tests
{
  /// <summary>
  /// Summary description for ConfigurationTests
  /// </summary>
  [TestClass]
  public class ConfigurationTests
  {
    [TestMethod]
    public void Smoke()
    {
      Configuration.Init();
    }

    [TestMethod]
    public void Init()
    {
      try
      {
        var value = Configuration.Settings["some"];
      }
      catch (Exception)
      {
        //OK
        return;
      }

      Assert.Fail();
    }

    [TestMethod]
    public void InputFolderTestMode()
    {
      TestValueInMode("configuration_test.xml", "input", "./testInput");
    }

    [TestMethod]
    public void InputFolderProductionMode()
    {
      TestValueInMode("configuration_production.xml", "input", "./input");
    }

    [TestMethod]
    public void OutputFolderTestMode()
    {
      TestValueInMode("configuration_test.xml", "output", "./testOutput");
    }

    [TestMethod]
    public void OutputFolderProductionMode()
    {
      TestValueInMode("configuration_production.xml", "output", "./output");
    }

    private void TestValueInMode(string configFile, string p, string p_2)
    {
      Configuration.ReInit(configFile);
      var value = Configuration.Settings[p];

      Assert.AreEqual(p_2, value);
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Business object


Next will be development of Business class responsible for primary application feature - transformation of input data, storing it and displaying to user. All these can be repesented as such test (I’m using Rhino mocks here as my favorite mocking framework):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rot13.Core.Library.BLL;
using Rhino.Mocks;
using Rot13.Core.Library.Presentation;
using Rot13.Core.Library.DAL;
using Rhino.Mocks.Constraints;

namespace Rot13.Core.Library.Tests.BLL_Tests
{
  [TestClass]
  public class Rot13ProcessorTests
  {
    MockRepository _mocks = new MockRepository();

    [TestMethod]
    public void Smoke()
    {
      var view = _mocks.StrictMock<IRot13ProcessorView>();
      var data = _mocks.StrictMock<IRot13ProcessorData>();

      var r = new Rot13Processor(view, data);
    }

    [TestMethod]
    public void Process()
    {
      //INIT
      var view = _mocks.StrictMock<IRot13ProcessorView>();
      var data = _mocks.StrictMock<IRot13ProcessorData>();

      With.Mocks(_mocks).Expecting(
        delegate
        {
          Expect.Call(data.Read()).Return("abcd");
          Expect.Call(delegate { data.Store(string.Empty); }).Constraints(Is.Equal("nopq"));
          Expect.Call(delegate { view.Display(string.Empty); }).Constraints(Is.Equal("nopq"));
        }
      ).Verify(
        delegate
        {
          var processor = new Rot13Processor(view, data);

          //ACT
          processor.Process();
        }
      );
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Corresponding implementation:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rot13.Core.Library.Presentation;
using Rot13.Core.Library.DAL;

namespace Rot13.Core.Library.BLL
{
  public class Rot13Processor
  {
    public Rot13Processor(IRot13ProcessorView view, IRot13ProcessorData data)
    {
      View = view;
      Data = data;
    }

    public void Process()
    {
      var input = Data.Read();
      var output = new string(Rot13Algorithm.Process(input.ToCharArray()));

      Data.Store(output);
      View.Display(output);
    }

    #region Properties

    public IRot13ProcessorData Data { get; protected set; }
    public IRot13ProcessorView View { get; protected set; }

    #endregion
  }
}

* This source code was highlighted with Source Code Highlighter.


Context


Before implementation of Data/View classes let’s thing how those will be created and configured. One of my favorite desing pattern is Factory Method. So, both of them should be created by corresponding factories, but what about configuration? For instance Data class have to know what is input and output files? We have to have some kind of Context that would hold all reaquired information. Context itself is constructed from command line arguments of application.

Tests:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace Rot13.Core.Library.Tests
{
  [TestClass]
  public class ContextTests
  {
    [TestMethod]
    public void Smoke()
    {
      //INIT
      var args = new string[] { "in.txt", "out.txt" };

      //ACT
      var context = new Context(args);

      //POST
      Assert.IsNotNull(context);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentException), "Insufficient number of arguments")]
    public void InsuffientArguments()
    {
      //INIT
      var args = new string[] { "in.txt" };

      //ACT / POST
      var context = new Context(args);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentNullException))]
    public void NullArguments()
    {
      //INIT / ACT / POST
      var context = new Context(null);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentException), "Too many of arguments")]
    public void TooManyArguments()
    {
      //INIT
      var args = new string[] { "in.txt", "out.txt", "fail.txt" };

      //ACT / POST
      var context = new Context(args);
    }

    [TestMethod]
    public void ContextCorreclyInitialized()
    {
      //INIT
      Configuration.Init();
      var args = new string[] { "in.txt", "out.txt" };

      //ACT
      var context = new Context(args);

      //POST
      Assert.AreEqual("./testInput/in.txt", context.InputFIlename);
      Assert.AreEqual("./testOutput/out.txt", context.OutputFilename);
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


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

namespace Rot13.Core.Library
{
  public class Context
  {
    public Context(string[] arguments)
    {
      if (arguments == null)
        throw new ArgumentNullException("Arguments parameter is null");

      if (arguments.Count() < 2)
        throw new ArgumentException("Insufficient number of arguments");

      if (arguments.Count() > 2)
        throw new ArgumentException("Too many of arguments");

      InputFIlename = Configuration.Settings["input"] + "/" + arguments[0];
      OutputFilename = Configuration.Settings["output"] + "/" + arguments[1];
    }

    #region Properties

    public string InputFIlename { get; private set; }
    public string OutputFilename { get; private set; }
    
    #endregion
  }
}

* This source code was highlighted with Source Code Highlighter.


Data object


Now, go back to data class. It is rather simple, main resposibility is to be able to read string from file and to store string to file.

Tests:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rot13.Core.Library.DAL;
using Rhino.Mocks;
using System.IO;

namespace Rot13.Core.Library.Tests.DAL_Tests
{
  [TestClass]
  public class Ro13ProcessDataTests
  {
    MockRepository _mocks = new MockRepository();

    [TestInitialize]
    public void Setup()
    {
      Configuration.ReInit("configuration_test.xml");
    }

    [TestMethod]
    public void Smoke()
    {
      var context = new Context(new string[] {"in.txt", "out.txt"} );
      var data = Rot13ProcessDataFactory.Create(context);

      Assert.IsNotNull(data);
    }

    [TestMethod]
    [ExpectedException(typeof(ArgumentNullException))]
    public void ContextIsNull()
    {
      //INIT / ACT / POST
      var data = Rot13ProcessDataFactory.Create(null);
    }

    [TestMethod]
    public void Read()
    {
      //INIT
      var context = new Context(new string[] { "in.txt", "out.txt" });
      var data = Rot13ProcessDataFactory.Create(context);

      //ACT
      var input = data.Read();

      //POST
      Assert.AreEqual(input, "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
    }

    [TestMethod]
    public void Store()
    {
      //INIT
      var context = new Context(new string[] { "in.txt", "out.txt" });
      var data = Rot13ProcessDataFactory.Create(context);
      var testContent = "tests,tests,tests";

      //ACT
      data.Store(testContent);

      //POST
      var actual = File.ReadAllText(context.OutputFilename);
      Assert.AreEqual(testContent, actual);
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Rot13.Core.Library.DAL.Impl
{
  class Rot13FileDataImpl : IRot13ProcessorData
  {
    private string _outputFilename;
    private string _inputFilename;

    public Rot13FileDataImpl(Context context)
    {
      if (context == null)
        throw new ArgumentNullException();

      _inputFilename = context.InputFIlename;
      _outputFilename = context.OutputFilename;
    }

    public string Read()
    {
      return File.ReadAllText(_inputFilename);
    }

    public void Store(string content)
    {
      File.WriteAllText(_outputFilename, content);
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


View object


After the data View is something we need to care about. On our application View is rather simple, so its just pust value on console out put. As with a data class I’m going to use Factory to produce View instance. Factory method will recieve Console object to put values on it. It will allow us to use mocks and make testing of View class more easy.

Tests:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using Rot13.Core.Library.Presentation;
using Rhino.Mocks.Constraints;

namespace Rot13.Core.Library.Tests.Presentation_Tests
{
  [TestClass]
  public class Rot13ProcessorViewTests
  {
    MockRepository _mocks = new MockRepository();

    [TestMethod]
    public void Smoke()
    {
      var console = _mocks.StrictMock<IConsole>();
      var view = Rot13ProcessorViewFactory.Create(console);

      Assert.IsNotNull(view);
    }

    [TestMethod]
    public void Display()
    {
      //INIT
      var console = _mocks.StrictMock<IConsole>();
      var view = Rot13ProcessorViewFactory.Create(console);
      var content = "some sort of string";

      // ACT/POST
      With.Mocks(_mocks).Expecting(
        delegate
        {
          Expect.Call(delegate { console.Write(string.Empty); }).Constraints(Is.Equal(content));
        }
      ).Verify(
        delegate
        {
          view.Display(content);
        }
      );
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


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

namespace Rot13.Core.Library.Presentation.Impl
{
  class Rot13ConsoleViewImpl : IRot13ProcessorView
  {
    private IConsole _console;

    public Rot13ConsoleViewImpl(IConsole console)
    {
      _console = console;
    }

    public void Display(string output)
    {
      _console.Write(output);
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Interface and default implementation of Console:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Rot13.Core.Library.Presentation
{
  public interface IConsole
  {
    void Write(string value);
  }

  public class Console : IConsole
  {
    public void Write(string value)
    {
      System.Console.Write(value);
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


And.. Thats actually it! Everything is ready to be integrated. Let’s re-run all tests to make sure that everything is still as expected. So far we’ve got 23 tests and all are green.

Integration


Let’s create an Application class that will be responsible for application behavior.

Tests:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;

namespace Rot13.Core.Library.Tests
{
  [TestClass]
  public class ApplicationTests
  {
    [TestMethod]
    public void Smoke()
    {
      var app = new Application(new string[] { "in.txt", "out.txt" });

      Assert.IsNotNull(app);
    }

    [TestMethod]
    public void RunApplication()
    {
      //INIT
      var app = new Application(new string[] { "in.txt", "out.txt" });

      //ACT
      app.Run();

      //POST
      var outputFilename = "./output/out.txt";
      Assert.IsTrue(File.Exists(outputFilename));

      var expectedContent = "Yberz vcfhz qbybe fvg nzrg, pbafrpgrghe nqvcvfvpvat ryvg, frq qb rvhfzbq grzcbe vapvqvqhag hg ynober rg qbyber zntan nyvdhn.";
      var actualContent = File.ReadAllText(outputFilename);
      Assert.AreEqual(expectedContent, actualContent);
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rot13.Core.Library.Presentation;
using Rot13.Core.Library.BLL;
using Rot13.Core.Library.DAL;

namespace Rot13.Core.Library
{
  public class Application
  {
    private string[] _arguments;

    public Application(string[] args)
    {
      _arguments = args;
    }

    public void Run()
    {
      //initialize configuration
      Configuration.Init();
      //initialize console
      var console = new Rot13.Core.Library.Presentation.Console();
      //initialize context
      var context = new Context(_arguments);
      //intialize business object
      var processor = new Rot13Processor(Rot13ProcessorViewFactory.Create(console), Rot13ProcessDataFactory.Create(context));
      //run..
      processor.Process();
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


Console application



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Rot13.Core.Library;

namespace Rot13.Application
{
  class Program
  {
    static void Main(string[] args)
    {
      try
      {
        var app = new Rot13.Core.Library.Application(args);

        app.Run();
      }
      catch (Exception e)
      {
        Console.WriteLine(e.Message);
      }
    }
  }
}

* This source code was highlighted with Source Code Highlighter.


It has no sence to create a separate test cases on that class, since we’ve got preatty good coverage following TDD rules implementing core of application.

Conclusions



  • Follow path from business requirements to implementation
  • Keep implementation as simple as possible
  • Test everything, because TDD rules


Source code of application.

Updated


I’ve inited github repository, so sources are available here:

http://github.com/alexbeletsky/JamesShoreRot13