Title: Windows Live Agents Testing: Improving the Framework
Slug: windows-live-agents-testing-improving-the-framework
Date: 2008-08-21 10:40:00
Author: Kartones
Lang: en
Tags: Testing, Development, Windows Live Agents
Description: A detailed guide on improving the default testing framework for Windows Live Agents.

 <p align="justify"><span style="line-height: 1.428571429;">This is part three and last on my WLA Testing posts. Read parts </span><a href="https://blog.kartones.net/post/windows-live-agents-testing-introduction" style="line-height: 1.428571429;">one</a><span style="line-height: 1.428571429;"> and </span><a href="https://blog.kartones.net/post/windows-live-agents-testing-building-the-framework" style="line-height: 1.428571429;">two</a><span style="line-height: 1.428571429;"> if you haven't because we will go into more complex and advanced tests for our testing framework.</span></p> <p>In the previous post we built a basic framework containing better summary reports, base error procedures to mark a test as failed, and Assert methods to compare strings and objects.</p> <p>While all of these are useful, writing tests to test conversational patterns is still a bit difficult, requiring some code for a simple "check if this is the output of that", so we're going to build Asserts to compare arrays, and then use it to build Asserts that compare conversation patterns (with multiple responses) and simple conversation dialogs (example: if user says A then ask B and if says B then answer C).</p> <p>This are the procedures we will build:</p> <p><font face="Courier New">declare procedure TestExErrorIfContainsArray(TESTMETHOD, STRING, REGEXARRAY)<br>declare procedure TestExErrorIfDoesNotContainArray(TESTMETHOD, STRING, REGEXARRAY)<br>declare procedure TestExCompareQuery (REGEX, QUERY)<br>declare procedure TestExCompareQueryNotEqual (REGEX, QUERY)<br>declare procedure TestExCompareQueryArray (REGEXARRAY, QUERY)<br>declare procedure TestExCompareQueryArrayNotEqual (REGEXARRAY, QUERY)<br>declare procedure TestExDialog(EXPECTEDRESULTS,DIALOGRESPONSES,DIALOGQUERY)</font>  </p><p> </p> <p>We'll start with the Errors:</p> <p><font face="Courier New"><font color="#008000">// Tests if a string contains at least one of the regular expressions in an array</font><br>procedure TestExErrorIfContainsArray(TESTMETHOD, STRING, REGEXARRAY)<br>  CONTAINS = false<br>  STRING_NO_NEWLINES = StringSubstitute(STRING, "\n", "X") <font color="#008000">// This is to make "." match on new lines, since Contains doesn't support the "s" modifier yet<br></font>  for value REGEX in REGEXARRAY<br>    if (Contains(REGEX, STRING)) &amp;&amp; ((STRING_NO_NEWLINES eq STRING) || (Contains(REGEX, STRING_NO_NEWLINES)))<br>      CONTAINS = true<br>  if (CONTAINS)<br>    ERROR_DESC - Expected: \"EscapeHtmlChars(ObjectToString(REGEXARRAY))\"<br>    call TestExError(TESTMETHOD, ERROR_DESC, STRING) </font> </p><p><font face="Courier New"><font color="#008000"><br>// Tests if a string does not contain at least one of the regular expressions in an array</font><br>procedure TestExErrorIfDoesNotContainArray(TESTMETHOD, STRING, REGEXARRAY)<br>  CONTAINS = false<br>  STRING_NO_NEWLINES = StringSubstitute(STRING, "\n", "X") <font color="#008000">// This is to make "." match on new lines, since Contains doesn't support the "s" modifier yet</font><br>  for value REGEX in REGEXARRAY<br>    if (Contains(REGEX, STRING)) &amp;&amp; ((STRING_NO_NEWLINES eq STRING) || (Contains(REGEX, STRING_NO_NEWLINES)))<br>      CONTAINS = true<br>  if (!CONTAINS)<br>    ERROR_DESC - Expected: \"EscapeHtmlChars(ObjectToString(REGEXARRAY))\"<br>    call TestExError(TESTMETHOD, ERROR_DESC, STRING)</font>  </p><p>As you can see, we just search for a string but not against a string but an array, with a <b>For-value-in</b>.</p> <p> </p> <p>Then, the simple query (one question - one answer) Asserts:</p> <p><font face="Courier New"><font color="#008000">// Tests a query against a regular expression for matching</font><br>procedure TestExCompareQuery (REGEX, QUERY)<br>  - Test: \"TestExCompareQuery\"<br>  call IncrementTotalTests()<br>  RETURNED = ExecuteQuery(QUERY)<br>  call TestExErrorIfDoesNotContain("TestExCompareQuery", RETURNED, REGEX) <br></font> </p><p><font face="Courier New"><font color="#008000">// Tests a query against a regular expression for not matching</font><br>procedure TestExCompareQueryNotEqual (REGEX, QUERY)<br>  - Test: \"TestExCompareQueryNotEqual\"<br>  call IncrementTotalTests()<br>  RETURNED = ExecuteQuery(QUERY)<br>  call TestExErrorIfContains("TestExCompareQueryNotEqual", RETURNED, REGEX)</font>  </p><p>Here the trick is calling <font face="Courier New">ExecuteQuery()</font> and storing it's output. This function works as if a user had typed something, processing it and returning the Agent's response. We catch the response and compare it as we did with a simple string Assert.</p> <p> </p> <p>But what if we've built an agent rich in answers, that can give different responses to the same pattern? Here's where the array error procedures we've just written come to help:</p> <p><font face="Courier New"><font color="#008000">// Tests a query against a regular expression array to see if contains at least one of its elements</font><br>procedure TestExCompareQueryArray (REGEXARRAY, QUERY)<br>  - Test: \"TestExCompareQueryArray\"<br>  call IncrementTotalTests()<br>  if !IsObject(REGEXARRAY)<br>    ERROR_DESC - "REGEXARRAY" not an array<br>    call TestExError("TestExCompareQueryArray", ERROR_DESC, QUERY)<br>  else<br>    RETURNED = ExecuteQuery(QUERY)<br>    call TestExErrorIfDoesNotContainArray("TestExCompareQueryArray", RETURNED, REGEXARRAY) <br></font> </p><p><font face="Courier New"><font color="#008000">// Tests a query against a regular expression array to see if does NOT contain any of its elements</font><br>procedure TestExCompareQueryArrayNotEqual (REGEXARRAY, QUERY)<br>  - Test: \"TestExCompareQueryArrayNotEqual\"<br>  call IncrementTotalTests()<br>  if !IsObject(REGEXARRAY)<br>    ERROR_DESC - "REGEXARRAY" not an array<br>    call TestExError("TestExCompareQueryArrayNotEqual", ERROR_DESC, QUERY)<br>  else<br>    RETURNED = ExecuteQuery(QUERY)<br>    call TestExErrorIfContainsArray("TestExCompareQueryArrayNotEqual", RETURNED, REGEXARRAY)</font>  </p><p>What we do is ExecuteQuery() again and check it's value against an array of expected responses.</p> <p> </p> <p>And finally, here's the code of a simple dialog Assert:</p> <p><font face="Courier New"><font color="#008000">// Tests a menu and all the responses defined in its parameter<br>// Note: Responses and expected results should be in the same order in the arrys</font><br>procedure TestExDialog(EXPECTEDRESULTS,DIALOGRESPONSES,DIALOGQUERY)<br>  - Test: \"TestExDialog\"<br>  call IncrementTotalTests()<br>  RESULTSNUM = SCount(EXPECTEDRESULTS)<br>  if (RESULTSNUM != SCount(DIALOGRESPONSES))<br>    ERROR_DESC - "EXPECTEDRESULTS" and "DIALOGRESPONSES" should have same number of elements<br>    call TestExError("TestDialog", ERROR_DESC, RESULTSNUM)<br>    exit<br><font color="#008000">  // Always trigger the dialog, then choose one option each time and check its response</font><br>  for I in 0 to RESULTSNUM-1<br>    MENURESPONSE = ExecuteQuery(DIALOGQUERY)<br>    ANSWERRESPONSE = ExecuteQuery(DIALOGRESPONSES[I])<br>    call TestExErrorIfDoesNotContain("TestExDialog", ANSWERRESPONSE, EXPECTEDRESULTS[I])</font>  </p><p>The code is quite self-explanatory, but basically it starts a dialog pattern with <font face="Courier New">DIALOGQUERY</font>, and then loops asking the agent each question (contained in <font face="Courier New">DIALOGRESPONSES</font> array) and each response against the <font face="Courier New">EXPECTEDRESULTS</font> array.</p> <p> </p> <p>As the code for some examples of this asserts is quite big and I don't want a huge post, I've instead <a href="https://kartones.net/downloads/wla002.zip">uploaded the full testing framework with a test DDL file</a> so you can directly add them to a buddyscript project and launch the sample tests.</p> <p>I hope you've learned how to better test your Windows Live Agents with this posts.</p> <p>I will write just one more post and then leave the legacy to my good friend <strong>PedroAfa</strong>, who is still working on a daily basis developing WLAs.</p>
