Posts Tagged ‘Software-Testing’

Integrating QTP With Non-Mercury Products

Friday, June 9th, 2006

Jeff posted a comment about my implication that we have integrated QuickTest Pro into our build processes. I thought I'd elaborate on that a little.

The first thing I should say is that it's not perfect, that we cobbled it together in between developing our testing strategy for our current release and writing regression test cases for some legacy products. We've got plans to improve it, we know pretty much what's missing, but, as with most meta-testing projects, IT WORKS.

The second thing I should say is that we're trying to follow an Agile methodology here, and while we haven't worked it all out, we at least use some of the terminology. If you see some term you're not familiar with, it probably comes from that school.

I'll start with an overview of the process, then discuss some of the steps in detail:

  • At about 2am, CruiseControl starts what we call the "Nightly" build
  • The Nightly build is just an Ant task that starts by building the software (it's a Tomcat webapp, but that's not critical to the process discussed here)
  • A blank test database is created
  • The build is unit-tested, then deployed to a test server
  • QuickTest is called upon to run a test that just verified the most basic things: a user logs in, then several objects are tested for existence and properties
  • The build is declared "Valid", and a more comprehensive suite of tests is run (via QuickTest Pro and other automated testing tools
  • After each test is run, test results are reported back to Rally (our Agile project management software)

As you may see, these processes take the place of many Mercury products (Quality Center, IT Governance, etc.). We're a small shop and can't afford all those tools, and even with the amount of man hours spent on these side projects, I'll bet we've only spent a fraction of what we would spend were we to buy them. I can't quantify that, and I'm sure someone at Mercury would argue that I'm not factoring in anything, but one thing I know for sure: there is a heck of a lot about our needs that Mercury doesn't factor in when they charge their astronomical fees and we do actually pay them. They've provided some tools that allow us to get this stuff done, so we're taking full advantage.

And that should put to bed any speculation that we're secretly in league with Mercury and just using this forum to promote their products.

[/rant]

Here's the list of things we used to do it:

We've written a web-based interface to our suite of QuickTest Pro tests, which allows anyone to run any test at any time on the box running QTP. It's a simple cgi script that takes several parameters: you select the test, enter where you want the results stored, and when the test is complete, it redirects you to an html representation of the test results.

What that really get us is the ability to launch any QTP script from any application that can make an HTTP request. Ant can do this, so we just have a series of Ant tasks that call the GET method, like so:

XML:
  1. <target name="runQuickTest">
  2.     <get verbose="true" src="http://servername/qtp/LaunchQTP.plx?testname=LoginTest"
  3.         dest="${outputDirectory}/results.html" />
  4. </target>

The @dest attribute just tells Ant where to put the resulting document on the build machine. We post it at a location that will be stored as a link in the Artifacts folder of the given build.

So, a Perl script, a VB script, heck, a wget command can launch QTP. That means you can integrate with nearly any product out there.

Now, as to the Perl script itself? It's kinda involved. I'll post that later when I'm able to cover it in more detail. The plot summary is that it uses the Win32::OLE library (available from CPAN, but usually distributed with any ActiveState version of Perl) to do exactly the same kind of interaction with QTP as you could do with VBS. The fact that it's in Perl means we don't have to do any special magic to get to it from within our test harness or any of our other interfaces, most of which are written in Perl.

The ability to integrate QTP tests with our existing build process was invaluable. It allowed us to keep on the track we were on before, using Open Source and keeping tight control over the minutae of our processes.

The magical ID attribute

Tuesday, April 11th, 2006

The company we work for is rewriting one of the web products for the next version, and we have taken the opportunity to request a few changes that will help us write automated tests. Chief among the changes is the inclusion of the ID attribute in every significant html tag - some of the insignificant tags like br and hr are exempt. Now that the development process is under way, we are seeing some results. Having a reliable ID attribute makes all the difference we imagined for writing QuickTest tests.

All this is to say that, if you only get one request of the development process, I recommend that you request the developers follow a standard for giving every html tag an ID attribute.

HTML:
  1. <LABEL ID="lblRequireIDs"> Require IDs on all HTML tags </LABEL>
  2. <INPUT TYPE="checkbox" ID="chkRequireIDs" NAME="RequireIDs" />

assertSucks

Tuesday, April 4th, 2006

I wrote this function this afternoon after being frustrated by the lack of decent checkpoints in QuickTest. I hope you find it insightful.

Visual Basic:
  1. Public Function assertSucks ( sString, sEvent )
  2.     Dim sSuckMessage, bSuck, sNot
  3.     sNot = "not"
  4.     bSuck = False
  5.     If sString = "checkpoints" Then
  6.         bSuck = True
  7.         sNot = "indeed"
  8.     End If
  9.  
  10.     sSuckMessage = "Expected Suckitude. " & sString & _
  11.         " does " & sNot & " suck."
  12.     assertSucks = reportStatus( bSuck, sEvent, sSuckMessage )
  13. End Function

New Object Repository Interface in QuickTest Pro 9

Monday, April 3rd, 2006

The changes to the management of the Object Repository are among the most significant in the new version, and though I haven't been through enough with the product to have a comprehensive opinion, I wanted to post my initial thoughts, and to invite comments from anyone whose experience is similar or different.

First off, let me say that on our team, where possible, we use programmatic descriptions. We minimize our interaction with the OR because we find that this is usually the first area that breaks when something changes in the interface. But we still do use the OR, and we rely on having an easy way to interact with it. I'm happy to report that they have made improvements in version 9, but there are still cases where they didn't go far enough.

The Set Up

In the description below, I opened a new test and associated my AUT's shared repository with the default action. My first inclination was to remove the association between the action's default Local Repository, but then I found that you can't remove the association. In an initial fit of consternation I talked it over with Will, and he was happy about the new behavior.

The Team-Oriented Object Repository

The local vs shared OR is their solution for how to make a team repository easier to manage: during test development, you have read-only access to the objects currently in the repository, but new objects are added to the local repository for that action. At some point later, one test developer goes back in and merges all the objects that should be shared, and either trims the rest from the local, or leaves them there if there's no chance of them being used outside that action.

I'm undecided about this method, but I understand why they've done it. One thing is for sure: this beats the heck out of trying to run the external merge tool every few days, not having any idea when new objects are being created, and all the other stuff we had to do in 8.2 to get it working.

The New Object Repository Interface

When you open the new Object Repository, the first thing you'll notice (besides the fact that it now takes about 5 seconds on my machine, as opposed to less than half a second for version 8.2), is the tree view on the left and the properties frame on the right. Further, the default size for the OR is more like what you'd expect: with the hierarchy collapsed to 3-deep, you can really see things well. The frame is big, and you probably don't have to scroll around much. When you click on an object, the properties frame on the right automatically refreshes to show the selected object's properties.

You'll further notice that the tree is mostly grayed out - that's the shared repository in read-only mode. Local objects are read-write, and any new objects you add go into that. The nice thing about this is how the trees merge seamlessly - if you're adding a new WebElement under a particular browser/page object, one that exists in the shared OR, its hierarchy is copied into the local rep so that it looks like your new WebElement is actually under the same parent as all the other WebElements on that page. This allows you to view the OR the way you always have, and since the files are separated, you won't be stepping on someone else's code, forcing a merge.

Here's the BIG problem with the interface: For the read-only objects (in the shared OR), you can't get extended attribute properties. You can't view them at all. The only properties you can see are the Description Properties, used to identify the object, the Ordinal Identifier, and whatever Additional Details you have turned on with Smart Identification for that object type. Now, I believe 8.2 worked in the same way if you were working with a read-only repository, but since that's the rule now and not the exception (on our team, anyway), it becomes tiresome to go through the steps necessary to view the additional details.

This probably wouldn't gall me so much if I didn't see all that space below the properties that are listed... space where they could have put reams and reams of information, separating TO Properties and Method calls, allowing you to view anything and everything at will. It's a missed opportunity to put all that good information right there on the page.

So what do you have to do to see the properties? Well, you have two options, one easy, one hard. The easy one is this: select the object whose properties you want to view, then click ObjectsCopy to Local from the menu bar (there's no hot key), then click the plus + sign in the Properties frame... this will allow you to CRUD all the TO properties. The side effect of this is that, if you end up making a change to the object, the change will only be made in your local repository, and will not be carried back to the shared OR.

The other option is to open up the Object Manager, a standalone app that allows you to merge ORs, edit properties, reparent, etc., independently from the main QTP interface. I'll be doing a separate write-up about the new OM, because though it's similar to the OR editor, it has its own set of features, annoyances, and tricky behaviors.

Locate in Repository - the Coolest New Feature

This is the coolest thing in the new OR - the ability to click on an object in the AUT, and have it highlighted in the OR. Was it just me, or did everyone else spend an awful lot of time searching for objects in the OR, navingating through trees and trees of crap, when the object on the page was just sitting there laughing at you? Now you click the "Locate in Repository" button, click on the object in the AUT, and it goes right there - if it can find it. It's a safe bet that if QTP can locate the object during a test run, it can locate it in an OR. It doesn't always work, but it's better than the nothing we had before.

Update from Application

Another cool feature: Select an object in the OR hierarchy, then click on its counterpart in the AUT, and its properties will be updated from the AUT. This is useful for when something is out of date, but I can see it being a pain if a whole page's worth of objects has changed and you have to update them all one-by-one. I don't know if there's an effecient way out of this yet, but it's possible that I've missed it. At any rate, I like the fact that it's there.

...and the Bad

They didn't put the "Locate in Application", "Copy to Local", "Highlight in Application", or any of the other object-specific functions in the right-click menu when these objects are highlighted... and it makes me want to scream. There are no appreciable hot keys, no ways to make management of these things more efficient, and you end up getting just about as frustrated as you did in version 8.2.

A really big problem, one that I'm afraid is going to cause many headaches for some really smart people, is that under the File Menu there's an option called "Export Local Objects". When my eye first hit that, I read it the same way I read "Copy to Local" from earlier, that it would perform some sort of merge with the shared OR. So, I could copy the objects to the local OR, edit them at will, then export them back out to the shared OR. No problem, right? Well, this options actually creates a new OR (the binary .tsr file format, not an XML file, by the way), containing the objects from the Local Repository. The intent is that you can create a standalone OR, and maybe use that as a shared OR for other actions. The actuality is that I once overwrote my humongous shared OR with objects from a local one, because like most normal people, I didn't read the warning message that said "this will replace the selected OR!!", clicked OK, and immediately heard groaning coming from a cube or two away. We're all version controlled here, so it was fine, but I'll bet dollars to donuts that someone less organized is going to lose a Lot Of Work as a result of this little detail.

Overall

In the end I haven't worked with the new OR enough to have a firm grasp of what's great and what's bad about it. These are my initial impressions, and I would invite anyone (even from Mercury, since we know you're reading ;) ) to help me to understand if I'm wrong on the new features. I've tried to stick mainly to the in situ OR dialog, because this is already long enough, so next time I'll cover the standalone Object Manager application.

Actions vs Functions in QTP 9.0

Monday, April 3rd, 2006

When deciding whether a piece of code should go in a reusable action or a function, we usually favored functions in QTP 8.2. When Mercury introduced QuickTest 9, they made the argument for functions even more compelling with the Multi Document Interface. Now we can open as many function libraries as we want, but we are still limited to one test. So, if you want to edit a reusable action in another test, you have to close your current test, open the second test, make your changes, save and close the second test then reopen your original test.

In addition to that new argument for functions in QTP 9, the same old arguments from 8.2 still apply. I'll sum up a few of the key differences between reusable actions and functions here.

Passing Data

Actions - can only accept primative data types as parameters (strings, integers, etc)

Functions - can accept arrays, dictionary objects and test objects (i.e. Pages, Frames, WebRadioGroups, etc.)

Resource Usage

Actions - For each action, there’s a folder, three separate vbs files, a local Object Repository, a subfolder containing snapshots, an Excel spreadsheet, and a few seconds of load time.

Functions - There’s the code contained in the Function, and that’s all

API

Actions - You cannot insert calls to Existing Actions through the QTP API, you can only do it through the interface

Functions - You can attach Function Libraries to Test Scripts through the QTP API

Return Values

Actions - Return values are difficult to understand and read in the syntax

Functions - Return values work like they do in other languages (i.e. as lvalues)

UPDATE:

In comments, Jugular Bean points out a significant downside to functions that I neglected above. You cannot change the number of parameters that a function accepts without affecting all calls to that function. That is a problem that used to drive me crazy. It seems like every time I put a new function in a library, the next test I write needs that function with one extra option. Now that I use the getopts function to define the parameters in my functions, I can easily add new optional parameters without messing up other tests that already call the function.

QuickTest on the Right

Thursday, March 30th, 2006

My company is good to me. They give me two 19 inch LCDs to look at all day. Unfortunately, Mercury Interactive isn't as good to their employees. If they were, their developers would write software that works well on a dual monitor system.

I've spent quite some time struggling with a problem in QTP 9 where it won't click on a button in a dialog box. I didn't think to try moving the application to the other monitor until I called support. Fortunately, I happened to mention that the dialog was on my right monitor which tipped the support engineer off that I have multiple monitors, and he was able to tell me what the problem is. He said there is little chance of Mercury fixing the problem anytime soon because there is a workaround -- only use one monitor.

I'm not writing this to get you to all sympathize with me, but to tell you that there is a way out of this single-monitor bondage. After finding out this is a monitor related issue, I tried switching monitors. Previously I had QuickTest on the primary display and IE on the secondary. I switched that around so that QuickTest is on the right (secondary) monitor and IE is on the left, and it is working beautifully.

New Feature in QuickTest Pro 9: Multiple Document Interface

Monday, March 27th, 2006

This is first in a series of analyses of the new features of QTP 9. Over the next few days we'll be posting more in varying levels of deatil, and within a week or two we hope to have just about all of them covered. We'll try to address where they went right, but we're not going to leave out where they could have gone farther. Please add comments where you agree/disagree with out conclusions. We want to be an accurate source of information, so if we're wrong, TELL US!

In my opinion, the Multiple Document Interface is the best feature of version 9. It is going to alter how we address the question of External Actions vs Functions.

With the new interface you can have one test script open, and then any number of Function Libraries. Under the FileOpen and FileNew menu items, there's now a separate entry for Function Libraries, and the libraries do appear in the Recent File list. The function libraries then appear as tabs across the top. From these tabs, you can right-click and it will bring up a small menu with "Save" and "Close" for options. There's also a "Save All" button in the toolbar. All in all, it's nice.

But more importantly than any of these small victories, there are the larger implications of the feature: you can now view, develop, execute, and debug your functions.

In the past, I've written the function inside my test script, made sure it worked properly, then put it in the VBS file. Then, if I find a bug in my function, I've had to copy it back into my test script, then repeat the whole thing ad nauseum. It's even one of the things Mercury advises in their training materials.

Allowing me to write and debug the code in situ means I don't need to be afraid to write new functions, or groan when I find a bug. In fact, if I get to a breakpoint where I'm calling an external function, it will open the library and allow me to step into the code without the extra step of opening it myself.

With this feature, we plan to shift the way we develop our framework: since we have full freedom to develop code in small chunks, we don't always want to spend the necessary overhead to create External Actions. We'll only create actions in certain situations. I'll discuss this topic in detail under a different heading. For now, I just wanted to post a description of this nice new feature, and compliment Mercury on implementing it very effectively.

Next, I'll cover in detail the little interface changes they've made. Some good, some not so good.

Comparison of QuickTest Pro 9.0 vs QTP 8.2

Monday, March 27th, 2006

How do you measure the overall quality of a product? One way is to take the spec, take the final product, and compare the two feature by feature. Well, QTP 9 went GA today, and we've pored over it so thoroughly you'd think we've had it for months. First I'll present the summary, then over the next few days we will dissect the minutae of what they did, how well they did it, and whether or not it's going to make your lives easier. Here's a bullet-by-bullet summary of how well they did against their own press release:

  • Open and edit multiple Object Repositories - yes, the fact is that you can do this, but it's not always easy, and there were a lot of missed opportunities to go from good to great
  • Multiple Object Repositories per test asset - I hate the term test asset. I think in this case they mean Action, because as far as I can tell, there is no other "thing" you can associate an OR with, and in version 9 you can associate multiple ORs with any given action. There are problems with this, but I'll get to those later
  • Easy (Object Repository) conversion to/from XML - Yep, for what it's worth, the conversion is easy. It is very very very slow, and the schema is complicated, but both of those are pretty understandable.
  • Easily copy/move objects between Object Repositories - Yes, you can even drag/drop objects from one repository to another, but maybe more importantly, you can reparent an object... this is super-duper cool, because you can now take all the objects under "Frame_4" and "Frame_37" and consolidate them under "Frame". There are some HUGE problems with this feature that will probably be made easier in future releases, but it's still possible and nice when you just have to do one or two objects
  • Manage functions and keywords centrally - I don't know what this means, outside from the ability to view/edit functions from within the main IDE. If that's the case, they're kinda cheating in making this a separate bullet point from the "Step Into..." point below. If that's not the case, I have no idea what this means and shall declare it an example of "silly marketingspeak"
  • New user interface - sure, OK. It's not like the difference between vi and Visual Studio .NET, and they haven't provided an Eclipse plugin yet (note to self: write Eclipse plugin for QTP), but I guess they've made some changes
  • Missing resources panel - This is très cool, but not nearly as cool as the way they make it sound
  • Comment and uncomment multiple lines in Expert View - One of those little nice things they've done to provide features we're used to in every other programmer IDE on the market
  • Indent and un-indent multiple lines in Expert View - ibid
  • Pass parameters between Actions - I have no idea what this means, since you could do this before. Maybe they didn't broadcast that you could do it before. Whatever
  • Multiple Document Interface for function libraries - I guess this means you can open more than one function lib at a time. That's yet another restatement of the "Manage Centrally" nonsense from the earlier bullet. But I'm guessing they list it three separate times because it's simply the single best feature of version 9. It's changed the way we compose our tests, which is funny, because it essentially means that we use Mercury's scaffolding a lot less because they've made it easier for us to write our own
  • Step Into Function Definition Code While Debugging the Test - Yes, THIS RULES!
  • Enhanced IntelliSense Support - the main thing I notice here is that your user-defined RegisterUserFunc methods now appear in the code. Maybe that was there before, but I don't know. I could never make it work

Test Level and Checkpoints in QuickTest Pro (9 and earlier)

Friday, March 24th, 2006

We don't use Checkpoints at all in QTP. They're just not flexible to the degree that we need them to be.

Whenever you call a checkpoint, there's a lot of stuff going on, very little of which you have control over. You can put in some things that make it more flexible, like regular expressions and comparisons to other output values, but in the end you have this big black box of stuff that you can't really get at. Plus, in the end, you have a Reporter.ReportEvent that goes to the test results as a Pass or Fail, and you have no way to alter the strings.

The problem with that is, as I've said in previous posts, I want different tests to behave differently in different circumstances, but I don't want to rewrite any of the code. When I'm not running deep or strict tests, I want the test results to be sparse, just letting me know when the broad strokes of the tests have been completed. The Test Results Viewer that comes with QTP is difficult enough to use (e.g. it doesn't remember filter settings, etc.), so when I filter the test results based on pass/fail, I want to zero in quickly on the tests that were important to me when I ran the tests. Sometimes we use test actions as a means to an end, not an end in themselves, and in those cases I don't want to see every detail.

I have a lot of other problems with the checkpoints as they are, but some of my problems may be a lack of education. My team abandoned checkpoints too early to really explore the ins and outs and what-have-yous (mainly because of the reporting problem), so I'd like to hear from anyone who is still using them and gets what they need out of them. I'll state some of the problems below. I'm not 100% sure that I'm right on these, so pretend that with all of them I've added "This is the experience I've had, and I've seen no evidence to contradict it. If you have some knowledge to impart, by all means let me know."

Some of these problems are:

  • The Standard Checkpoints seem to be very, very slow. It's nice that you can look into specific object properties and all that, but the fact that it slows down the script is a real pain when you have a lot of them in there at once
  • For Table Checkpoints, you can't be at all flexible in column or row checking. Our application allows the user to change the order of columns, etc., which means we need to
  • For Text checkpoints, you must specify the nebulous "text before" and "text after", when, in my AUT at least, those things are seldom reliable or consistent
  • Worse yet, when it comes to localization, they'll be in a different language. You could specify values from a data table, etc., but sometimes I would rather be able to say "the text to the right of the object with XYZ properties", or "the text that is in this frame, and I don't care about the words, only that the font color is red and the CSS class reference is std_WarnText"

Like I said, there may be other ways to do this, but I haven't found them. When I have coded around things to shoehorn my checkpoints into working, I've often later found them to be very fragile. I had an occurrence recently of a long, drawn-out test script, that probably contained over a thousand lines of code. The one line of code that broke consistently and with no good error message was the one leftover checkpoint from my earlier days. I finally removed it and replaced it with our new, preferred method.

What is that new, preferred method? I'll talk about that soon. To give you a hint, think about JUnit.

In the meantime I want to talk a little more about Test Level, and the different levels we've defined. There are 6 levels, and we're retaining the option to have more. Each level gives an indication of how strictly it behaves, and how its behavior is recorded into the test results.

  • Test Level 0: Loosest testing possible. Try at all costs to be smart about selecting options, and just get through the code by any means necessary. Also, don't log anything that's not an error (and that's a QTP Run error, not a "test failed" error)
  • Test Level 1: Be as loose as level 0, but report all non-errors as "micDone" entries in the results. This way, we've at least kept track of all the things we've done, but when you filter by Warnings and Errors, you'll only get the stuff that was considered Very Important by the test developer
  • Test Level 2: Be as loose as level 0, but report all "smart" behaviors as warnings. When something doesn't exist in a WebList and you have to guess at it by clicking the first non-empty string, report that fact as a "micWarning". If the specified value was in the list, report it as a "micDone"
  • Test Level 3: Again, be as loose as level 0, but report all "smart" behaviors as "micFail", and all correctly specified values as "micPass"
  • Test Level 4: This time, don't be at all smart about how values are selected. If the developer specified something to be a certain way, report the problem as "micFail", then exit the test iteration (like QuickTest would by default, except that the intent is to give very detailed error information about what we were looking for and what appeared instead)
  • Test Level 5: Same as Level 4, but exit all test iterations immediately

The nice thing is that you can ratchet up or down the Test Level as the test goes on. You can test shallow in order to achieve some sort of setup, like object or scenario creation, then knock it up a level or two when you start the really important part of the test.

Our company has tasked us with asking a bit more out of our automated tests than is normally recommended. We understand that you're not usually successful when you try to do outrageously large regression tests, or to simulate manual scripts. But they want something in between "normal" automation and 100% regression coverage, and techniques like this allow us to move much closer to that goal than ever before.

The ability to take the same scripts that perform the low-level field validation, then employ them as "just a step along the way", has been paramount. It takes a while to develop the initial tests, but the development gets faster and faster as we build our library. Then, with very little effort, we can build a smart suite that can be reused against new builds, against customer-deployed sites, and against new versions of the product.

And we don't use Mercury's checkpoints anywhere.

Next, I'll post an example of another Smart method, the one we use for WebLists. It employs heavy use of Test Level to determine its behavior and logging.

Strictness in QuickTest (9.0 and before), Part II

Thursday, March 23rd, 2006

In our QTP framework, "strictness" has two dimensions: Test Level and Test Weight. Test Level refers to how loose the test is allowed to be when, say, selecting specific values from a WebList or WebRadioGroup. Test Weight refers to how important a given test is, and whether or not it will be run in the current cycle.

When we run tests, we run them at a default Test Level of 0, which is to say, "when you encounter a WebList or other input type, if the value I have specified does not exist, or only exists partially, just select a valid response and move on."

The Test Weight is 200 (out of a possible 1000). The lower the Test Weight, the more important the test. When you run a 200-weight suite, you are essentially running a smoke test, where you want to run the least number of tests to validate that the system is up and running. This is what we run every time Cruise Control deploys a new site. A suite level of 800 or 1000 tells it to run every stinkin' test we've written, and it takes much longer. That's what we run at 2 am when nobody is here.

Let's say I'm writing a QTP test where I create 15 new user accounts (each with different access features), log in as each, then perform existence checks on objects (buttons, checkboxes, etc) that should be enabled/disabled according to the access features. Given the number of different roles, features, and grants in our system, there can be over 800 permutations. Further, since we have between 4 and 8 hosted customer sites, this number mushrooms. As we host more customers, we want to scale our tests to be able to handle any number of customers, and we don't want to have to change to code to do so.

Even further, we want to design the code so that we can re-use all these actions (User creation, logging in, access checking, etc.) in other scripts where we don't want to run any checks beyond the most basic "did it work?" functionality.

Test Weight and Test Level are Environment variables, set in XML files, and altered at run-time to fit our needs (via an external utility). Each action has its own internal Test Weight, which is compared at run-time to Environment variable. If this internal Test Weight is greater than the weight of the suite being run, the action will short circuit and return to immediately without reporting anything or executing. If the internal Test Weight is less than or equal to the Environment variable, the test will execute.

Test Level is a lot more low-level. It is checked on a function-by-function basis, and the function in question always executes. The difference is, the Test Level will tell the function how to execute, then how to report its progress. An example would be (as above) a WebList, where you specify that "abc" be selected. If Test Level is 0 and "abc" doesn't exist in the list, it will take the first non-empty string in the list and just select it without looking deeper into the matter. The idea is that if I'm testing a site in a different language, or a customer-deployed site that contains completely different information, the test will still work just fine.

Having these two dimensions on our tests ensures that we can run exactly the same scripts on multiple databases with varying degrees of thoroughness as the situation dictates.

Next, I'll delve a little deeper into the different Test Levels we use, then I'll post the code for this WebList.SmartSelect method, and how it implements the concept.