Friday 11 April 2014

Logging web test details to a text file

My next approach to logging was to by writing direct to text files. This worked fine and I was able to write large number of lines without obvious problems. I was concerned that opening and closing the files within each test case would be too much load, so I arranged to open the text file once per agent computer. A C# lock on the output file synchronises the multiple writers coming from the many test cases.

The code is written to have only one instance, so its contructor is private. The main class data items are the lock, the actual instance of the class and the output stream.

    public class SingletonWriter
    {
        private static object myLock = new object();
        private static SingletonWriter mySingleton = null;

        StreamWriter outStream;


The constructor generates the file name and creates the corresponding output stream.

        private SingletonWriter(string AgentId)
        {
            string streamName

                = "TestStatusAsComment."
                + System.DateTime.Now.ToString("yyyy-MM-dd.HHmm.")
                + AgentId + ".log";

            outStream = new StreamWriter(streamName, true);

                // true means append to an existing file.
            outStream.WriteLine("Start of output");
            outStream.Flush();
        }


Writeline is the main interface. It needs to access some parts of the web test details, it writes a simple string. After each write the stream is flushed.

        public static void WriteLine(PostWebTestEventArgs e,
                                     string line)
        {
            lock ( myLock ) {
                if ( mySingleton == null ) {
                    string AgentId =

                       ContextReader.GetContextField(
                           e, "$AgentId", "00");
                    mySingleton = new SingletonWriter(AgentId);
                }

                mySingleton.outStream.WriteLine(line);
                mySingleton.outStream.Flush();
            }
        }

Closing the stream allows one test run to generate multiple output files, if the created filenames are different. The stream creation above will append to an existing file rather than overwrite it.

        public static void Close()
        {
            lock ( myLock ) {
                if ( mySingleton != null ) {
                    mySingleton.outStream.WriteLine(

                       "End of output");
                    mySingleton.outStream.Close();
                    mySingleton = null;
                }
            }
        }
    }



Having got a write line routine it is a simple matter to use it in a plugin. The code below writes some details of the test case and the date, plus the contents of one context variable. An extra property is added to allow output from all test cases, or just those that are not passing at the time of the call.

public class WriteTestStatusAsComment : WebTestPlugin
{
    [System.ComponentModel.DisplayName("Write status for all tests")]
    [System.ComponentModel.Description("Only write for failing tests when false.")]
    [System.ComponentModel.DefaultValue("false")]
    public bool WriteStatusForAllTests { get; set; }

    [System.ComponentModel.Category("Additional data")]
    [System.ComponentModel.Description("Include value of this context variable, if any")]
    public string Context1 { get; set; }

    private StringBuilder sb;

    public override void PostWebTest(object sender, PostWebTestEventArgs e)
    {
        if ( WriteStatusForAllTests || e.WebTest.Outcome != Outcome.Pass ) {
            sb = new StringBuilder();

            sb.Append(System.DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff "));
            sb.Append(e.WebTest.Outcome.ToString());
            sb.Append(" Iter: ");
            sb.Append(ContextReader.GetContextField(e, "$WebTestIteration"));
            sb.Append(" UserId: ");
            sb.Append(ContextReader.GetContextField(e, "$WebTestUserId"));
            sb.Append(' ');
            sb.Append(e.WebTest.Name);

            AddContext(e, Context1);

            string message = sb.ToString();
            e.WebTest.AddCommentToResult(message);

            SingletonWriter.WriteLine(e, message);
        }
    }
}


3 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Hi trying to implement your above solution but struggling to get this to work - ContextReader.GetContextField does not exist in current context, any idea what i need to add?

    ReplyDelete
    Replies
    1. Something like:
      public static string GetContextField(PostWebTestEventArgs e, string fieldName)
      {
      object obj;
      bool foundObj = e.WebTest.Context.TryGetValue(fieldName, out obj);
      return foundObj ? obj.ToString() : "_";
      }

      Delete