Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

If you want to automate some of your manual integration testing I can highly recommend WatiN. It is a great library for doing browser automation. The new version allows you to automate Internet Explorer and Firefox through a common interface.

example:

[Fact]
public void SearchForCodingInstinctOnGoogle()
{
  using (IBrowser ie = BrowserFactory.Create(BrowserType.InternetExplorer))
  {
    ie.GoTo("http://www.google.com");
    ie.TextField(Find.ByName("q")).Value = "Coding Instinct";
    ie.Button(Find.ByName("btnG")).Click();
    Assert.True(ie.ContainsText("Coding Instinct"));
  }
}

I am using xUnit.NET in the above example (Fact = Test). xUnit.NET has been mentioned in a lot of blogs lately and I think for good reasons. It comes bundled with a very nice set of extensions that are very useful, especially for integration testing.

ExcelData:

One of the extensions that comes with xUnit is an attribute that lets you fetch test data from an excel file, xUnit will then execute your test method for each row in the excel file. Here is an interesting usage scenario:

[Theory, ExcelData(@"Resources\PortalUrls.xls", "select * from PortalUrls")]
public void UrlShowsExptectedContent(string url, string contentText, bool requireLogin)
{
  InitBrowser(url);
  
  if (requireLogin)
  {
    LoginAction.WithAS();
  }

  Assert.True(Browser.Text.Contains(contentText), string.Format("\"{0}\" was not found", contentText));
}

My current customer is using a portal application to host a number of separate applications (that use a common SSO). With this simple test I was able to check all portal URLs and with a very simple validation check that the applications (and the login) was up and running.

The excel file:

In the excel file you must use the function "Name a Range". This function can be found by selecting the table, including the header and right clicking. It is also found under the "Formulas" tab (Define Name, Name Manager).

The test above is of course a very simple test, It only checks that the portal is configured correctly and that all applications are up and running, but it is a good start. Now I can go into each application/feature and write automated tests for each of the manual test cases.

UI test fragility:

When writing browser tests you need to do a lot of html element selections so the tests become very fragile to UI changes, this is especially true for WebForm applications where element names and ids are constantly changed for minor UI changes.

To combat this problem I tend to place most of the element selections in static helper classes. I also use a RegEx to identify elements. The reason for using a RegEx is to ignore a large part of the id prefix that the WebForm html renderer so gladly adds. example:

public class LoginAction : ActionBase
{
  public static void WithAS()
  {
    Browser.TextField(new Regex(".*txtUserName")).Value = "xxxx";
    Browser.TextField(new Regex(".*txtPassword")).Value = "xxxx";
    Browser.Button(new Regex(".*btnLogin")).Click();
  }
}

There are many more useful xUnit extension attributes, for example: InlineData, PropertyData and SqlServerData. For more information on WatiN and xUnit.NET here are some links:

In most applications you usually store and cache some user information in a session object. Most actions are dependent on this information, which means that many services require this information. This problem can be solved using different approaches. What I usually end up with is creating a user ticket class which holds maybe a subset of the user information (or the complete user class) and other session specific information. The simplest approach is then to pass this object along to each service / method that needs it:

public class WishListService
{
  private IWishListRepository wishListRepository;

  public WishListService(IWishListRepository wishListRepository)
  {
    this.wishListRepository = wishListRepository;
  }

  public void AddToWishList(IUserTicket ticket, Product product)
  {
    // handle all the important stuff
  }
}

The problem with this approach is that all callers need to fetch the user ticket. If you have deep method stacks and the deepest component needs the session information then all methods needs to pass the object along.

A sometimes nicer approach is to let the components fetch the session information on their own (through an interface):

public class WishListService
{
  private IWishListRepository wishListRepository;
  private ITicketAccessor ticketAccessor;

  public WishListService(IWishListRepository wishListRepository, ITicketAccessor ticketAccessor)
  {
    this.wishListRepository = wishListRepository;
    this.ticketAccessor = ticketAccessor;
  }

  public void AddToWishList(Product product)
  {
    IUserTicket ticket = ticketAccessor.Current();
    // handle all the important stuff
  }
}

This approach makes the component easier to use and the method calls are simpler, dependencies to the session (ticket) information is handled by the ITicketAccessor which can be inject by a IoC container.

Another approach is to have a static RuntimeContext class which static properties to access the session information from anywhere in your application:

public class RuntimeContext
{
  private const string KEY_TICKET = "RuntimeContext.Ticket";        

  /// <summary>
  /// Used to access the ticket for the current request
  /// </summary>
  public static IUserTicket Ticket
  {
    get
    {
      // try local execution context first
      IUserTicket ticket = Local.Data[KEY_TICKET] as IUserTicket;
      if (ticket != null)
        return ticket;

      // try session
      if (Local.RunningInWeb && HttpContext.Current.Session != null)
      {
        ticket = HttpContext.Current.Session[KEY_TICKET] as IUserTicket;
        // cache in local 
        if (ticket != null)
          Local.Data[KEY_TICKET] = ticket;
      }

      return ticket;
    }
    set
    {
      Local.Data[KEY_TICKET] = value;

      // store in session if running in web 
      if (Local.RunningInWeb)
      {
        HttpContext.Current.Session[KEY_TICKET] = value;
      }
    }
  }
}

The Local class in the above code is an abstraction over HttpContext.Current.Items and Thread data depending on if the code is being run in web context or not. This last approach is very easy to work with but you have less control over where and how the data is accessed.

There is also Thread.CurrentPrincipal which is a good to use, especially for user authorization. I am not sure which of the approaches I like more, the second one is probably the best one from a purist / TDD point of view. I will post a question in the ALT.NET mailing list to find out what solutions smarter people than me have arrived at.