Some days ago my good friend Gian Maria Ricci showed on his blog Alkampfer’s Place a nice way to integrate twitter and TFS showing how you can have tweets related to TFS actions.

The idea was pretty cool, I actually do not a have a TFS up and running in my tests or development environments, but I wanted to have the same notifications when an automatic test on a build machine fails.

So I just ‘stole’ some of his code and realized a quick add-in for NUnit: it’s basically a simple test decorator, which get installed with the highest priority, so it is executed as the last one in the decorator’s chain. The test is just a wrapper around the original code and it checks the result of the original test method, if there’s a failure or an exception it uses a twitter account to send a notification to all the subscribers.

The Addin can be configured using an xml file called Twitter.config that must be deployed along with the addin. You can deploy the addin globally (placing it in the NUnit addins folder) or use my previous extension and deploy it directly along with your tests so it can be launched and used on a ‘per-project’ basis.

Here’s the code for the addin:

namespace Structura.NUnitExtensions.Twitter
{
   /// <summary>
   /// send a twitter message using the account specified in the Twetter.config, this file must be deployed with the addin
   /// </summary>
   [NUnitAddin(Description = "Send a Twetter message whenever a test fails")]
   public class TwitterDecorator : IAddin, ITestDecorator
   {
      public TwitterAccount Account { get; set; }
 
      #region IAddin Members
 
      public bool Install(IExtensionHost host)
      {
         System.Diagnostics.Trace.WriteLine("TwetterDecorator: Install called");
         IExtensionPoint2 decorators = (IExtensionPoint2)host.GetExtensionPoint("TestDecorators");
         if (decorators == null) return false;
         decorators.Install(this, 9);
 
         // read the configuration
         string filepath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase);
         filepath = Path.Combine(filepath, "Twitter.config").Replace("file:\\", "");
         Account = TwitterAccount.Load(filepath);
 
         return true;
      }
 
      #endregion
 
      #region ITestDecorator Members
 
      public Test Decorate(Test test, MemberInfo member)
      {
         System.Diagnostics.Trace.WriteLine("TwetterDecorator: decorate called");
         if (test is NUnitTestMethod)
         {
            test = new TwitterTestMethod(((NUnitTestMethod)test).Method, Account);
         }
         return test;
      }
 
      #endregion
   }
 
   public class TwitterTestMethod : NUnitTestMethod
   {
      public TwitterTestMethod(MethodInfo method, TwitterAccount account)
         : base(method)
      {
         Account = account;
      }
 
      public TwitterAccount Account { get; set; }
 
      public override void Run(TestResult testResult)
      {
         // let's run the test
         base.Run(testResult);
         // check for success or failure and send the twitter message
         // if (testResult.IsFailure)
         if (!testResult.IsSuccess)
         {
            string message = string.Empty;
            if (testResult.IsFailure)
               message = string.Format("{0} - Failure: {1} {2}", Account.Project, testResult.FullName, testResult.Message);
            if (testResult.IsError)
               message = string.Format("{0} - Error: {1} {2}", Account.Project, testResult.FullName, testResult.Message);
 
            TwitterService twitter = new TwitterService();
            twitter.SendMessage(Account.Username, Account.Password, message);
         }
      }
   }
 
   public class TwitterAccount
   {
      public string Username { get; set; }
      public string Password { get; set; }
      public string Project { get; set; }
 
      public static TwitterAccount Load(string filename)
      {
         XmlSerializer ser = new XmlSerializer(typeof(TwitterAccount));
         using (StreamReader sr = new StreamReader(filename))
            return (TwitterAccount)ser.Deserialize(sr);
      }
   }
}

Project source code:

Related Content