Tuesday, August 31, 2010

Blog Day is Today - let's open up for guest blogging

Blog Day 2010
In accordance with BlogDay.org
one long moment on August 31st, bloggers from all over the world will post recommendations of 5 new Blogs, preferably Blogs that are different from their own culture, point of view and attitude. On this day, blog readers will find themselves leaping around and discovering new, unknown Blogs, celebrating the discovery of new people and new bloggers.


I would like to expand this by the proposal - Let's open our blogs for guest blogging (sure content should fit with blog theme and pass validation by original author(s)). The back links will track back to guest author's blog. Thus we will expand our net. What do you think? My blog is really open for your greate content in a field (you know - test automation, QA, QC and technologies). At the same time I have some interesting articles to be published as guest author. Let's follow up!

Meanwhile, I follow up the Blog Day goal - 5 blog post:
  1. Google Testing Blog - http://googletesting.blogspot.com/
  2. A Smart Bear blog - http://blog.asmartbear.com/
  3. I.M. Testy blog - http://www.testingmentor.com/imtesty/
  4. Software testing space - http://inderpsingh.blogspot.com/
  5. Corey Goldberg Blog - http://coreygoldberg.blogspot.com/

Prepare to interview with Geekinterview.com and Glassdoor.com


The http://www.geekinterview.com/Interview-Questions goes certainly to my list of Favorites.

It really contains huge database of interesting and original questions which everyone may be questioned upon IT interview. I was surprised - there are a lot of shared real-life questions on Testing (see there is break sown by different sections including automation). The direct links on test automation section, testing tools, separately for QTP and TestComplete users - VBS questions, JS questions

You guys as creator of this resource - are rocks!

Want to get hired by a particular Company? Be prepared by crawling questions from real interviews on Glassdoor.com (inside look at Salaries, Reviews and Interviews provided by real Users). This is a direct link to Interview Tab

If you are lucky one and passed along several tech interviews at Your-Dream-Company, you are rolling over package negotiation with the employer Glassdoor.com again comes to play a role of naviagtor in that tricky moment "Neither play over nor loose offer".

Other useful resources on this topic? All are Welcomed to suggest!

Why so expensive not-ITE tool QTP?

Share market is overwhelming
Price is the biggest one
Feature set is average - mostly focusing on unexperienced Users (minimal programming effort) modernity and novelty- mostly product is outdated if not archaic. The old-man VB script is a history of Windows which is pulled out in Windows for back compatibility

Is it really ITE - NO, QTP is Wizard-affordable tool for unskilled developers (aka manual testers) only.


Agree / Disagree? Let's discuss and maybe argue :)

Monday, August 30, 2010

Links: Unit Test Patterns and Framework overview by Marc Clifton

I don't use "Advanced Unit Test" but it looks pretty and beateful solution.

First, read a view of Unit testing patterns. I found it interesting
http://www.codeproject.com/KB/architecture/autp5.aspx

This is really interesting vision on unit patterns? Running automation over UI and aka grey-box automatoin is perfectly modeled and presented in terms of MVC pattern (Model-View-Controller) -shortcut within the page
The rest content (framework itself, shortcuts:

Wednesday, August 25, 2010

Tuesday, August 24, 2010

Working with objects in JS

The starting point to get confidence over JavaScript OO (object-oriented approach) full support take a look at this post (http://nefariousdesigns.co.uk/archive/2006/05/object-oriented-javascript/). Emulation? Right - these are not C++ or Java techniques with deidcated keywords (protected, private, abstract, extends, template...) and operators (::, ->, : <>...). But - it (JS) works and fits pretty well to develop GUI, test automation harness, tools and utilities. Keep it simple, right?

Hovewer the primary intention of the post is to give Ready-to-Go utilities to work with JS's objects as data structures for TestComplete (JS) users. The excerpt is here:

 //USEUNIT TypeTraits  
function Clone ( obj )
{
if (!TypeTraits.IsReference ( obj ))
{
return obj;
}
var newObj = new Object ( );
for ( var key in obj )
{
newObj [ key ] = Clone ( obj [ key ] );
}
return newObj;
}
function toString( obj )
{
var _collectionToString = function( obj, indent ){
var newIndent = indent || "";
newIndent += " ";
var pairs = [];
for (var key in obj)
{
if ( obj[key] === null )
{
var value = "null";
}
else if ( typeof obj[key] == "object" )
{
var value = _collectionToString( obj[key], newIndent )
}
else
{
var value = obj[key];
}
pairs.push( "\n"+newIndent+key+" : "+value );
}
return ( "{"+pairs+"\n"+indent+"}" ).replace( /\{[\n\s]+\}/mg,"{}"); //remove all spaces between empty {}
}
return _collectionToString( obj, "" )
}
function merge( /*Object*/ base, /*Object*/ another )
{
/*
PURPOSE:
Merge base object with another object
RETURNS
Merged object.
Fields of "base" replaced with those of "another"
*/
//Clone base into resulting object to avoid damage to base object
var result = new Object();
if ( base )
{
for ( property in base )
{
if ( base.hasOwnProperty(property) )
result[property] = base[property];
}
}
// merge
if ( another )
{
for ( property in another )
{
if ( another.hasOwnProperty(property) && ( another[property] !== null) )
result[property] = another[property];
}
}
return result;
}

Monday, August 23, 2010

I Do Need Self-Test!

ROI modeling <-> reverse to estimation formula

Control recognition problem

TestComplete has lots of built-in features to operate with properties and methods of tested object. As was mentioned in a previous section, all wrappers over controls of AUT are implemented in components. This part of codebase is much often subject of changes from release to release and usually those changes are unpredictable. One day, our team was not ready to run in-time full regression tests against fresh build because of recent changes in DOM tree for many objects. We were need to invite something robust, not silent and not verbose; this mechanism should report something into logs when a control was not found directly but it should attempt to recognize that control using set of provided properties. We developed robust pattern that posses obvious advantages:

  • Easy code maintenance: to call an object located on a page, just write something like this:

var conext = Common.GetAppTop();

var res = recognizeControl ( w.Panel(0).Panel(0).Panel(0).TextNode(0).Link(0), ["ObjectType","innerHTML"], ["Link","Log in"], context);

  • Robust change management: if object direct path was changed on a page, test log will report an error but the control will be found indirectly either within a particular html element (e.g. cell, row) or within whole web page.
  • Automatic checking of correspondence between properties values of direct object path and specified object properties for indirect search. In this way, we achieve discipline of test development process and may reveal hidden mismatches. For instance, this code can reveal that “Log In” link has changed innerText property to “Log Innn” but test will continue work with this control because direct object path is correct.

In conclusion, the proposed approach of object recognition provides checking relevant properties and expected object location simultaneously. The code snippet of the proposed pattern is presented below.


Figure. Test automation framework

Code snippet of pattern for object recognition and verification on the fly in JavaScript:


 

function recognizeControl( directPath, _propCollection, _propValues, topParentPath )

{

    try

    {

        var res;

         if ( typeof ( _propCollection ) == 'string' )

        {

            if ( Init.GetCurBrowser() == "firefox" )

            _propCollection = _propCollection.replace( /innerText/g, "innerHTML" );

            propCollection = _propCollection;

        }

            else

                propCollection = Arrays.ConvertArray ( _propCollection );

        if ( typeof ( _propValues ) == 'string' )

            propValues = _propValues;

            else

                propValues = Arrays.ConvertArray( _propValues );

 

        if ( TypeTraits.IsFit( directPath ) && directPath.Exists )

        {

            if ( TypeTraits.IsFit( _propCollection ) && TypeTraits.IsFit( _propValues ))

                PropMatch ( directPath, _propCollection, _propValues );

            return directPath;                            //specified direct link is returned

        }

                 var res;

                if ( TypeTraits.IsFit( topParentPath )&& topParentPath.Exists )

                    res = topParentPath.FindChild( propCollection, propValues, 200 );

                if ( TypeTraits.IsFit( res )&& res.Exists )

                    return res; //the object was found located within the Parent object

       

                    else {                

                                        if ( TypeTraits.IsFit( topParentPath ) && topParentPath.Exists )

                                               Log.Warning( 'Can`t find child with property name [ ' + propCollection.toString()              + ' ]; and property value [ ' + _propValues.toString() + ' ]; on parent object;','    Parent:\r\n' + topParentPath.FullName + '\r\n    Page:\r\n' + GetPage().URL, 300, f );

                                       

                                        res = GetPage().FindChild ( propCollection, propValues, 200 );

                                        if ( TypeTraits.IsFit( res ) &&  res.Exists )

                                               return res; //the object was found located within the Page

                                               else

                                                      Log.Error( 'Cant find child with property name [ ' + _propCollection.toString() + ' ]; and property value [ ' + _propValues.toString() + ' ]'+'');

                    }

            }

        return false; //default returning if nothing was found

    }

 

    catch ( ex ) {

        Log.Error ( 'Exception occurs in Splitter.recognizeControl', ex.description, 300, f );

        Init.exceptionHandler( arguments );

        return false;

    }

 

}

 

// PropMatch validates if object having direct path has predefined properties as alternatives;

 

function PropMatch ( directPath, _propCollection, _propValues ){

 

    var str;

 

    try {

        if (( typeof _propCollection == 'string' ) || ( typeof _propCollection == 'number' ))

            _propCollection = new Array( _propCollection );

 

        if (( typeof _propValues == 'string' ) || ( typeof _propValues == 'number' ))

            _propValues = new Array( _propValues );

 

        for ( var i = 0; i < _propCollection.length; i++ ){

            var iProp = _propCollection[i].toString();

            var iProp = eval ( directPath.FullName + "." + iProp + ";" );

            if ( typeof iProp != 'undefined' ){

                var hasText; // Boolean flag for text existing

                if ( _propValues[i].indexOf ( '*' ) == -1 ){

                    hasText = ( iProp == _propValues[i]);

                }

                    else

                        hasText = true;

                if ( !hasText )

                    Log.Warning ("The direct path to object [" + directPath.FullName + "] has property [" +  _propCollection[i] + "] value [" + iProp + "] but expected value is [" + _propValues[i] + "]");

            }

            else

                Log.Warning ( "Object [" + directPath.FullName + "] has no property [" +  _propCollection[i] + "]");

        }

    }

 

    catch ( ex ){

    Log.Error( 'Exception occurs in Splitter.PropMatch', "The direct path to object [" + directPath.FullName+"] has property [" +  _propCollection.toString() + "] value [" + iProp + "] but expected value is [" + _propValues.toString() + "]", 300, f );

    return false;

    }

}


Wednesday, August 18, 2010

Automated testing of JS apps - embedded overview

I found tasty presentation on JS (Java Script) code test automation (Unit) in LinkedIn. Well, as fan of JS, cant's skip it to be shared. Enjoy this overview with frameworks comparision and various code snippets!

Tuesday, August 17, 2010

Why We use “FAIL” for many reasons?

Why We use “FAIL” for many reasons?

Really why we are so restricted by test automation tools such as Mercury QTP and AutomatedQA TestComplete and other popular ones give us just 2 options – either report Pass or report Fail. If a test log contains at least one Fail – the whole run is getting Fail. This is OK. However I definitely want to have different statuses for checkers belonging to test design and other things occurring from time to time in automation. I propose the following breakdown:

Failed = on unmatched Assertions, comparisons and checkpoints. If test automation is checking GUI consistency, explicit GUI checkpoints should be designed

Error = on any runtime errors, exceptions, unexpected failures, compilation errors, missed objects, improper interfaces/methods/properties applied to objects. Generally – all fails out of checkpoint and assertions

Unfortunate thing is that tools do not provide such statuses. Basically we have only Fail and Warning (but the last one does not affect overall status). The problem is rising when your team builds huge test automation suites and this team is responsible for test execution analysis. And not only for analysis and reporting to customers and management but that team should do it timely and efficiently with minimal effort. It means also that product issues should be reported ASAP and first to provide whole real picture on build quality as early as possible. The second thing is clearing up testing code, fix infrastructure problems, and discover reasons of unexpected failures (all that is what I indicated as Error). Well, actually the optimal work path under this pressure is essential. But how I can differentiate where is Fail in order to process and analyze first and where is that unexpected Error (the second matter), if tools vendors gave me generic Fail only??? I must to drill down to all failed test instances one by one.

Another not good practice is to continue test execution on any failure. I rather prefer to stop and cleanup execution at any failure (either Failed or Error)

Here we go and the time to workaround is come.

We will create custom status in high-level reporting.

Turn off logging at all at the very testing beginning:

QTP: Reporter.Filter = rfDisableAll

TestComplete: Log.Enabled = False

then we will need to write 2 wrapping functions which will report Failed and Warning

//Call this function on any test checkpoint/assertions failed

function LogFail (message, priority, options) {

Log.Enabled = True // QTP: Reporter.Filter = rfEnableAll

//Log Failure code

Log.Enabled = False // QTP: Reporter.Filter = rfDisableAll

CleanUp (True, "Fail")

}

//Call this function on any unexpected error, exception…

function LogError (message, priority, options) {

Log.Enabled = True // QTP: Reporter.Filter = rfEnableAll

//Log Failure code

Log.Enabled = False // QTP: Reporter.Filter = rfDisableAll

CleanUp (True, "Error")

}

After that we will learn high-level reporting (aka Dashboard) to understand and properly display if a test log contains at least one Warning – show up Error. Otherwise – overall run status should be matched with native status decided by test tool, i.e. Pass=Pass, Fail=Fail

In TestComplete/QTP test run should message to Dashboard (TestRunStatus variable) DB in such a way within Cleanup () method:

Function CleanUp (doTerminate, ErrLevel) { //doTerminate True/False

//Some operations to free execution

//…

//

If (ErrLevel = “Fail”) TestRunStatus = Fail

Else If (ErrLevel = “Error”) TestRunStatus = Error

//Calling termination whole test execution

If (doTerminate) TerminateExecution()

}

So what we’ve got? Localization project issues (including defects) is getting quicker, as Fail now means failure in functionality or behavior of tested application (AUT) – wrong result which is also known as deviation from Expected result; opposite status Error means something unexpected happened (test code problem, infrastructure, changes in AUT interfaces (e.g. changed Button caption results in impossibility to recognize the Button on screen).

Moving forward, someone can come up that Error and/or Fail could be split up on a few more. E.g. Error set can be considered as Exception | Environmental Error | 3rd Party exception | Object missed | Object improper method/property | Execution crash. We can think out any custom execution statuses but not sure if that really needed.

The proposed solution is good way to avoid mix up of different failures. Another approach I’d like to recommend (especially for those who use TestComplete) is to employ priority of logged messages (in our case Fail). TestComplete allows specifying priority of posted message to log. This gives opportunity to make more flexible model of failures reporting. Somehow it implies to make your framework remember maximum number of priority of posted Fail to the log. Finally maximum priority value will be Fail priority for test run overall. Then you can design Dashboard with usable coloring to represent priority and to give direction to analysis order from huge scope. In the example below, Fail (500) could be used as Fail in application test itself (assert/checkpoint fail), Fail (100) – unexpected exception (e.g. database connection exception), Fail (300) – Browser hang (who knows, maybe our tested application is too leaking and caused to browser hang)


Thursday, August 12, 2010

Customizable random strings generator in JavaScript (TestComplete)

What this guy can give you? Answer: the class allowing to generate a custom (predefined by pattern or type) string. The code is fully compatible with TestComplete - just add it as library to your Project. Another application - to use as routine code for JS scripts in web. In this case you have to hack some line, like logging, other TestComplete's function calls.

 //USEUNIT TypeTraits   
var _String;
function RandomStringFactory ( minLen, maxLen, type, pattern )
{
new GeneratedStringObject ( minLen, maxLen, type, pattern );
return _String;
}
function GeneratedStringObject ( minLen, maxLen, type, pattern )
{
this.minLen = minLen;
this.maxLen = maxLen;
this.type = type;
this.pattern = pattern;
_String = GenerateStringWrapper ( this );
}
function GenerateStringWrapper ( obj )
{
return GenerateString ( obj.minLen, obj.maxLen, obj.type, obj. pattern );
}
function GenerateString ( minLen, maxLen, type, pattern ) //minLen, maxLen, type, pattern
{
//var minLen=11, maxLen=1150, type="free", pattern="dddcccsss$dddsssccc";
var resString = "";
var len;
if ( ValidateParameters ( minLen, maxLen, type ))
{
//Randomize();
len = minLen+Math.floor ( Math.random ( ) * ( maxLen - minLen ));
switch ( type )
{
case ("alpha") : resString = ProduceAlphaStr ( len );
break;
case ("num") : resString = ProduceNumStr ( len );
break;
case ("alphanum") : resString = ProduceAlphaNumStr ( len );
break;
case ("email") : resString = ProduceEmailStr ( len );
break;
case ("validEmail"): resString = ProduceValidEmailStr ( len );
break;
case ("free") : resString = ProduceFreeString ( len );
break;
case ("custom"):
if ( pattern != "" && pattern != null )
resString = ProduceCustomString ( pattern );
else
Log.Error ( "No pattern for randomly generated field with type Custom string" )
break;
default: resString = ProduceFreeString ( );
break;
}
}
return resString;
}
function ValidateParameters ( minLen, maxLen, type )
{
if ( !isNaN ( minLen ) && !isNaN ( maxLen ) && minLen <= maxLen )
if ( TypeTraits.IsFit ( type ))
return true;
else
return false;
}
function ProduceCustomString(pattern)
{
var res = "";
var AlphaSet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var NumSet = "0123456789";
var SpecialCharSet = "!#$%&')(*+,-./:;<=>?@[\]^_`{|}~";
//Randomize();
for ( var i = 0; i < pattern.length; i++)
{
var curChar=pattern.charAt (i);
switch (curChar)
{
case ("c") : ch = AlphaSet.charAt (Math.floor ( Math.random () * ( AlphaSet.length )));
break;
case ("d") : ch = NumSet.charAt ( Math.floor ( Math.random () * ( NumSet.length )));
break;
case ("s") : ch = SpecialCharSet.charAt ( Math.floor ( Math.random () * ( SpecialCharSet.length )));
break;
default: ch = curChar; //any const char, e.g. @ - apply as is
}
res += ch;
}
return res;
}
function ProduceAlphaStr( len )
{
var pattern = "";
for ( var i = 0; i < len; i++ )
pattern += "c"
return ProduceCustomString ( pattern );
}
function ProduceNumStr ( len )
{
var pattern = "";
for ( var i = 0; i < len; i++ )
pattern += "d"
return ProduceCustomString ( pattern );
}
function ProduceAlphaNumStr ( len )
{
//Randomize();
var pattern="";
var fl;
for ( var i = 0; i < len; i++ )
{
fl = Math.floor ( Math.random () * (2));
var ch = (fl == 0) ? "d":(( fl==1 ) ? "c" : "s");
pattern += ch;
}
return ProduceCustomString ( pattern );
}
function ProduceFreeString ( len )
{
//Randomize();
var pattern = "";
var fl;
for ( var i = 0; i < len; i++ )
{
fl = Math.floor ( Math.random () * (3));
var ch = (fl==0) ? "d":((fl==1) ? "c" : "s");
pattern+=ch;
}
return ProduceCustomString ( pattern );
}
function ProduceEmailStr ( len )
{
//Randomize();
var pattern = "";
var fl;
var ret = true;
var ch = "";
for ( var i = 0; i < len; i++ )
{
var remainedCh = len - i;
if ( remainedCh > 9 )
{
fl = Math.floor ( Math.random () * (2));
ch = (fl == 0) ? "d":((fl == 1) ? "c" : "s");
}
else
{
if ( ret == true )
{
ch = "@";
ret = false;
}
else
{
if ( remainedCh == 4 )
ch= ".";
else
ch= "c";
}
}
pattern += ch;
}
return ProduceCustomString ( pattern );
}
function ProduceValidEmailStr ( len )
{
//Randomize();
var pattern = "";
var fl;
var ret = true;
var ch = "";
for ( var i = 0; i < len; i++ )
{
var remainedCh = len - i;
if ( remainedCh > 9 )
{
fl = Math.floor ( Math.random () * (2));
ch = (fl == 1) ? "d":"c";
}
else
{
if ( ret == true )
{
ch = "@";
ret = false;
}
else
{
if ( remainedCh == 4 )
ch= ".";
else
ch= "c";
}
}
pattern += ch;
}
return ProduceCustomString ( pattern );
}
function GetRandomElementFrom( /*Array*/ itemSet )
{
/*
PURPOSE
Select and return random element from given array of items
ARGUMENTS
itemSet: array of elements of any type;
RETURNS
random element form given set
*/
if ( !itemSet || (itemSet.length == 0 )){
return null
}
else {
return itemSet[ Math.floor( Math.random() * itemSet.length ) ]
}
}
function test ()
{
var w;
for ( i = 0; i < 20; i++ )
{
w = GenerateString (12, 20, 'validEmail', null );
w = w.toLowerCase ();
Log.Message (w);
}
}

Friday, August 6, 2010

Technically efficeint and clearly explained Framework over Selenium

Guys did their best to highlight clearly in detail the approach and organization of framework over Selenium IDE. Actually I worked with 3 frameworks relying on XML. The difference is only that their framework connect data with UI elements explicitly in XML. My frameworks usually bind data and UI in scripting code. So that test data, steps (test case instructions) and UI map are loosely coupled.
Also their framework could be improved with advantages of applying XML schema and other XML techniques (http://at4qa.blogspot.com/2010/01/tools-xml-orm-for-efficient.html)

GTAC_SeleniumFramework

Thursday, August 5, 2010

JSON generic converters

For those who AJAXing and work closely with JS this intercahnge format is essential. For those who things it's tricky - check out first the origianl site devoted to JSON - json.org and then - wikipedia page (http://en.wikipedia.org/wiki/JSON) to realize how it's easy. Built-in JS's methods stringify and parse are basic for processing JSON back/forth strings

The advanced scenarios are smart Stringifiers and converters forth/back XML. The last one is especially applied task for making data compatible between 2 technologies (one may not know about JSON) and to improve performance of data transition (JSON is ~30% less and faster in remote data excahnging then XML)

Just to share with you guys ready-to-go Java Script libraries:

Those who relies on frameworks and platforms - JSON's classes with all needed features are included in Java, .Net and some databases (JSOn serializers on a fly)

P.S. jsut for fun - the same problem the same solution - Win Registry serializer on VC++ http://www.codeguru.com/cpp/w-p/system/registry/article.php/c2859

Tuesday, August 3, 2010

Microsoft Testing framework (Microsoft.VisualStudio.TestTools namespace) overview

If you are working within .Net and other MS's platforms, you probably should be happy having Visual Studio Team System as part of Visual Studio IDE. This is getting more interesting if your ALM is Team Foundation Server as Visual Studio with TFS perfectly fit and compatible(can't be different?)

What I like - is detailed useful and thought-out Team Test API. Yes API! This is presented within 5 namespaces (references are here):
Microsoft.VisualStudio.TestTools.LoadTesting
Microsoft.VisualStudio.TestTools.UnitTesting
Microsoft.VisualStudio.TestTools.UnitTesting.Web
Microsoft.VisualStudio.TestTools.WebTesting
Microsoft.VisualStudio.TestTools.WebTesting.Rules

Besides of API, VS Team System provides GUI User-friendly tools for managing test sets, code edition within VS IDE (with all native capabilities) and record-playback feture. Supporting several languages running over CLR is also benefit of testing framework from Microsoft.

One more important thing is Load testing pure functionality as part of Test Team API (performance/load modeling, profiling, watching performance counters, monitoring DB productivity and so fort) and all that goes with VS license!

The Getting Started page of Unit Testing framework can be found on this MSDN's page. As ususal in unit testing, API from MS gives various assertions, implemented within few classes. I found interesting and most useful from my standpoint, the following classes and theirs polymorphic assertions methods:
Assert, StringAssert, CollectionAssert, AssertFailedException
The polymorphic approach of assertions afford the abstract interface which ease programming by datatype independency. It means proper implemntation of a particular data type is an internal thing and yuo should not care about that, as well as type trating, code and workflow safety, error and exception handling, reporting. Check out the benefit, for example from overloading list of Assert.AreEqual

Data-driven testing (DDT) is mostly implemented as data native agnostic model and I found that DDT is provided in 2 classes: DataSourceElement and
DataSourceAttribute. If you need to process details of a particular test, just instantinate an object of TestContext


To understand a standart unit test workflow, check out the excerpts (from MSDN):

the original calss implementation
using System;

namespace SampleClassLib
{
public class DivideClass
{
public static int DivideMethod(int denominator)
{
return (2 / denominator);
}
}
}
the testing code is

using Microsoft.VisualStudio.TestTools.UnitTesting;
using SampleClassLib;
using System;
using System.IO;
using System.Windows.Forms;

namespace TestNamespace
{
[TestClass()]
public class DivideClassTest
{
[AssemblyInitialize()]
public static void AssemblyInit(TestContext context)
{
MessageBox.Show("Assembly Init");
}

[ClassInitialize()]
public static void ClassInit(TestContext context)
{
MessageBox.Show("ClassInit");
}

[TestInitialize()]
public void Initialize()
{
MessageBox.Show("TestMethodInit");
}

[TestCleanup()]
public void Cleanup()
{
MessageBox.Show("TestMethodCleanup");
}

[ClassCleanup()]
public static void ClassCleanup()
{
MessageBox.Show("ClassCleanup");
}

[AssemblyCleanup()]
public static void AssemblyCleanup()
{
MessageBox.Show("AssemblyCleanup");
}

[TestMethod()]
[ExpectedException(typeof(System.DivideByZeroException))]
public void DivideMethodTest()
{
DivideClass target = new DivideClass();
int a = 0;
int actual;
actual = target.DivideMethod(a);
}
}
}

If the shown snippets are not clear why and how, study the Structure of Unit Tests page

Looks like everything in the box.
AND one more interesting feature in .Net Reflection - thus testers and developers see internal staff of implementation, such as all properties and methods of UI control and so forth. Btw, using that test code basic verification testing (aka baseline) can be generated on fly

This simple VBS script can be used to health monitor your infrastracture

This simple VBS script can be used to health monitor your infrastracture. Each monitored machine should have turn on RPC service.

The provided code should be saved as .vbs, e.g. HostsHealthMon.vbs

To call the script, run in CL this (or make cmd file to wrap this call):
cscript.exe HostsHealthMonitor.vbs>>\\YouRStorage\HostsHealtMon_Log.txt



Option Explicit

Dim Hosts, RootUser, RootPwd, forceAlarmCPU, forceAlarmHD, forceAlarmRAM, forceAlarmConnection
Dim ptr, partitionRedLimit, CPUUsageRedLimit, RAMFreeRedLimit, IterationDelay, resetCount
Dim lastCacheLogPath
Dim httpServerUrl

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Description: The Sub is responsible for initialization and reseting (on beginning of each cycle) settings
' Populate this set of variables With your data as descibed in comments for each block to configure run
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Sub InitVars ()

'Machines for checking details: host name or IP, User Id and password
Hosts = Array("host1", "10.90.23.255", "host02", "host03", "Server01")
RootUser = Array("Admin", "User", "Admin", "User01", "TestUser")
RootPwd = Array("123west", "123west", "123west", "123west", "123west")

'Flags reseting
forceAlarmConnection = Array (True, True, True, True, True)
forceAlarmCPU = Array (True, True, True, True, True)
forceAlarmHD = Array (True, True, True, True, True)
forceAlarmRAM = Array (True, True, True, True, True)

'Threasholds
partitionRedLimit = "5"
CPUUsageRedLimit = "95"
RAMFreeRedLimit = "50"

'Settings for defining frequency of the reporting
IterationDelay = 100
resetCount = 5

'Only latest results will be stored in this file (no history). E.g. a host had a problem with CPU load in previous full iteration and now it soes not, then the log file will contain nothing about that
lastCacheLogPath = "\\hostStorage\HostsHealtMon_Cache.txt"
Call Delete_File ( lastCacheLogPath )

End Sub

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Description: removes specified file
' Parameter sFile - name of folder to be removed
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Function Delete_File (ByVal sFile)
On Error Resume Next
Const c_Proc = "Delete_File"
Const c_DeleteReadOnly = True

Dim iResult, oFSO

' Define default error code
iResult = True

Set oFSO = CreateObject("Scripting.FileSystemObject")

' Check if specified folder exists
If Not oFSO.FileExists(sFile) Then
iResult = True

' The file found
Else

' Remove specified folder
Call oFSO.DeleteFile (sFile, c_DeleteReadOnly)

' Check operation result
If Err.Number <> 0 Then
Wscript.Echo "Can't remove the file '" &amp; sFile
iResult = False
Else
iResult = True
End If

End If ' The folder found
Set oFSO = Nothing
Delete_File = iResult

End Function

'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Description: Writes in file
' Parameters: objFile - file object
' strText - text to write
' Result: True / False
'''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Public Function FileWrite(ByRef sFile, ByVal sText)
Dim iResult
Dim objFSO, objFile
iResult = True
Const ForAppending = 8

Set objFSO = CreateObject("Scripting.FileSystemObject")

Err.Clear
Set objFile = objFSO.OpenTextFile (sFile, ForAppending, True)

If Err.Number <> 0 Then
Wscript.Echo "Error appending to file " &amp; sFile
iResult = False
Err.Clear
Else
objFile.WriteLine sText
objFile.Close

End If

Set objFSO = Nothing
Set objFile = Nothing

FileWrite = iResult
End Function


Sub Main()

InitVars ()

Do While True
On Error Resume Next
ptr=0
resetCount = resetCount - 1
For Each Host In Hosts
Set objSWbemLocator = CreateObject("WbemScripting.SWbemLocator")

Dim alarmStr, tmpStr
alarmStr = ""
tmpStr = ""

Err.Clear
Set objWMIService = objSWbemLocator.ConnectServer(Host, "root\CIMV2", RootUser (ptr), RootPwd (ptr), "MS_409")

If Err.Number <> 0 Then
'Wscript.Echo "Warning establishing RPC connection with " &amp; Host &amp; ": "&amp; Err.description
tmpStr = Date &amp; " " &amp; Time &amp; " " &amp; "Error: Unable to establish RPC connection with " &amp; Host &amp; ": "&amp; Err.description
If CBool(forceAlarmConnection(ptr)) = True Then
Wscript.Echo tmpStr
alarmStr = alarmStr &amp; " <br />" &amp; tmpStr
forceAlarmConnection(ptr) = False
End If
End If

'Wscript.Echo Date &amp; " " &amp; Time &amp; " " &amp; Host

'Show free space on logical disk
Set colItems = objWMIService.ExecQuery( _
"SELECT * FROM Win32_LogicalDisk",,48)
For Each objItem In colItems
'Wscript.Echo "FreeSpace: " &amp; objItem.FreeSpace
If objItem.FreeSpace <>"" Then
If CInt(Left(objItem.FreeSpace, Len(objItem.FreeSpace)- 9)) < CInt(partitionRedLimit) Then
tmpStr = Date &amp; " " &amp; Time &amp; " " &amp; "ALARM event - Free space on a partition at : " &amp; Host &amp; " = " &amp; Left(objItem.FreeSpace, Len(objItem.FreeSpace)- 9) &amp; " GBs"
If CBool(forceAlarmHD(ptr)) = True Then
Wscript.Echo tmpStr
alarmStr = alarmStr &amp; " <br />" &amp; tmpStr
forceAlarmHD(ptr) = False
End If
End If

'Else
' forceAlarmHD(ptr) = True
End If
Next

'Show CPU load percentage
Set colItems = objWMIService.ExecQuery( _
"SELECT * FROM Win32_Processor",,48)
For Each objItem In colItems
'Wscript.Echo "CPU load on: " &amp; Host &amp; " = " &amp; objItem.LoadPercentage
If CInt(objItem.LoadPercentage) > CInt(CPUUsageRedLimit) Then
WScript.Sleep(3000)
If CInt(objItem.LoadPercentage) > CInt(CPUUsageRedLimit) Then 'Double check after 5 sec

tmpStr = Date &amp; " " &amp; Time &amp; " " &amp; "ALARM event - CPU usage on: " &amp; Host &amp; " = " &amp; objItem.LoadPercentage &amp; " %"

If CBool(forceAlarmCPU(ptr)) = True Then
Wscript.Echo tmpStr
alarmStr = alarmStr &amp; " <br />" &amp; tmpStr
forceAlarmCPU(ptr) = False
End If

End If

'Else
' forceAlarmCPU(ptr) = True
End If
Next

'Show Memory MB free space
Set colItems = objWMIService.ExecQuery( _
"SELECT * FROM Win32_PerfFormattedData_PerfOS_Memory",,48)
For Each objItem In colItems

If CInt(objItem.AvailableMBytes) < CInt(RAMFreeRedLimit) Then
WScript.Sleep(1000)
If CInt(objItem.AvailableMBytes) < CInt(RAMFreeRedLimit) Then 'Double check after 5 sec

tmpStr = Date &amp; " " &amp; Time &amp; " " &amp; "ALARM event - RAM free space: " &amp; Host &amp; " = " &amp; objItem.AvailableMBytes &amp; " MB"

If CBool(forceAlarmRAM(ptr)) = True Then
Wscript.Echo tmpStr
alarmStr = alarmStr &amp; " <br />" &amp; tmpStr
forceAlarmRAM(ptr) = False
End If

End If

End If
Next

'Wscript.Echo alarmStr
If alarmStr<>"" And alarmStr<>" <br />" Then
Call FileWrite(lastCacheLogPath, alarmStr)
End If

Set objSWbemLocator = Nothing
ptr = ptr + 1

Next

WScript.Sleep(IterationDelay)

If resetCount <= 0 Then
Call InitVars()
End If

Loop

End Sub

Call Main