May 11, 2006
Ajax Support is Coming!
ABCWebTest will soon support automated testing of Ajax applications!
With the next release of ABCWebTest it will be possible to automate the testing of Ajax applications such as Google Maps. You'll be able to move the mouse over any element on a web page (e.g. a zoom slider image) drag the mouse in either a vertical or horizontal direction (e.g. dragging a map or dragging a zoom slider up or down to zoom in on the map). ABCWebTest will automatically wait until the Ajax application has updated all the data before returning from a method call.
This means you, as the test writer, don't have to worry about putting in timers/waits/sleeps until the Ajax operation is finished. You'll just use this simple API (example code is testing the Google Maps site):
C#
Image sliderImage = browser.Document.GetImageBySrc("slider.png");
browser.Mouse.MoveOverElement(sliderImage);
browser.Mouse.DragVertically(20); //zoom in on map - move slider up by 20 pixels
Java
Image sliderImage = browser.getDocument().getImageBySrc("slider.png");
browser.getMouse().moveOverElement(sliderImage);
browser.getMouse().dragVertically(20); //zoom in on map - move slider up by 20 pixels
Stay tuned for more details...
Posted by Misha Rybalov at 07:01 AM | Comments (0)
May 09, 2006
The Need for Speed - ABCWebTest is now FAST!
One of the most frequent feedback comments we've received about ABCWebTest is the request to speed up it's execution. We've listened and it's now super-fast!
The main issue before was that in order to ensure that the web page was loaded properly, we had to put in many checks. What we've done since version 0.5 is created a Speed class that allows you to control the speed at which your tests are executing. Think of it like a manual transmission car, except here we only have 2 speeds: low and high. By default, the speed of ABCWebTest is as fast as possible, which should suffice for most situations. However, there will be times when you'll need to slow it down. This is usually the case right before an action opens a popup. In that case, you'd simply slow down ABCWebTest before the action that causes the popup to occur and then speed it back up right after. Here's some sample code in C#:
browser.Speed.SlowDown();
browser.Document.GetAnyButtonByName("click_me_to_open_popup").Click();
browser.Speed.SpeedUp();
Here's the same code in Java:
browser.getSpeed().slowDown();
browser.getDocument().getAnyButtonByName("click_me_to_open_popup").click();
browser.getSpeed().speedUp();
You can change the low and high speed values as you require, but by default the high speed is "0" and the low speed is "1000". This is the approximate number of milliseconds of delay. The higher the value, the larger the delay and the slower your web tests will take to run. Here's how you would change the default speed values:
C#:
browser.Speed.High = 100; //slow down high speed to 100ms instead of the default 0ms
browser.Speed.Low = 800; //speed up low speed to 800ms instead of the default 1000ms
Java:
browser.getSpeed().setHigh(100); //slow down high speed to 100ms instead of the default 0ms
browser.getSpeed().setLow(800); //speed up low speed to 800ms instead of the default 1000ms
The speed increase is available in ABCWebTest as of version 0.5.
Posted by Misha Rybalov at 03:03 PM | Comments (0)
May 05, 2006
NUnit "Quick and Dirty" Tutorial
In the style of the JUnit Quick and Dirty Tutorial, here's a similar tutorial for NUnit. I'm assuming that you're familiar with C#/.NET, downloaded NUnit and have a development environment all setup for compiling and running C# code.
Preparing an NUnit test class
1. create a blank C# file (e.g. FooTests.cs)
2. import the following namespace:
using NUnit.Framework;
3. create a stand alone class that uses the "TestFixture" attribute (e.g. [TestFixture] public class FooTest { })
This is all that's required to setup your class for NUnit tests - fewer steps than for JUnit, in fact. Now you're ready to actually write some tests.
Writing an NUnit test
1. For every test you'd like to run, create a public method that returns void, takes no arguments and uses the "Test" attribute e.g. [Test] public void MyTest() { ... }
Unlike JUnit, there is no need to have the method start with "test" because the "Test" attribute tags the test so that NUnit knows that's your test case.
2. Instantiate the object you'd like to test inside the test method from the previous step.
Foo foo = new Foo();
3. Invoke the method(s) you'd like to test and assign the return values to local variables.
e.g. int actualValue = foo.bar(1, 2, 3);
4. Check the method's return value by using one of NUnit's assert methods. You can get a list of all the assert methods from the NUnit Documentation.
e.g. Assert.AreEqual(4, actualValue);
Initializing and De-initializing Tests
Often, you'll need to initialize or de-initialize a group of tests in the same way. For example if all your tests had to start with the same object creation, you can place that common initialization code into a special NUnit method with the "SetUp" attribute. Similarly, if all your tests had to do the same de-initializing steps, that code can be placed into a method tagged with the "TearDown" attribute.
Your test methods would then not have to perform those common steps anymore and could start with the "meat" of their testing.
The set up and tear down methods can have any name (unlike JUnit which requires they be called setUp() and tearDown(), respectively. Also, these methods don't need to be protected like in JUnit but can be public. If you don't have any use for them, don't include them and by default there will be no set up and tear down.
e.g.
//foo variable should be declared as an instance of the test class
[SetUp] public void SetUp() { foo = new Foo(); }
[TearDown] public void TearDown() { foo.cleanUp(); }
NUnit also has a nice once-only initialization/de-initialization feature where you can do an initialization once before any tests are run (instead of once before every test is run) and once after all tests are run (instead of once after every test is run). Use the "TestFixtureSetUp" and "TestFixtureTearDown" attributes, respectively, like so:
[TestFixtureSetUp] public void TestFixtureSetUp() {
//...
}
[TestFixtureTearDown] public void TestFixtureTearDown() {
//...
}
Grouping Your Tests Into a Suite
You can have a master suite test file that is a collection of all your unit test classes. This is another stand alone C# class that's tagged with the "Suite" attribute. Here's a sample of how it would look like:
using NUnit.Framework;
using NUnit.Core;
namespace ABCWebTest.Tests {
public class AllTests {
[Suite] public static TestSuite Suite {
get {
TestSuite suite = new TestSuite("All Tests");
suite.Add(new AjaxTests());
suite.Add(new BrowserTests());
suite.Add(new BrowserUtilityTests());
suite.Add(new KeyboardTests());
suite.Add(new MouseTests());
suite.Add(new PopupTests());
suite.Add(new SpeedTests());
suite.Add(new TextTests());
suite.Add(new TimerTests());
return suite;
}
}
}
}
Rules of Thumb
These are general guidelines to keep in mind as you're creating more tests with NUnit.
1. Create a single test class for each application class you have.
2. Name each test class the same name as the application class its testing but with "Tests" appended to the end. So if you have an application class called "ShoppingCart.cs" the test class would be called "ShoppingCartTests.cs"
3. Give meaningful names to each test method. I usually name the test method the same name as the method that its testing with some additional info appended to the name. For instance if I'm testing a method called "add" in my ShoppingCart class, I'll create a few test methods to test different ways of adding to a shopping cart:
- [Test] public void Add_SingleProduct() { ... }
- [Test] public void Add_MultipleProducts() { ... }
- [Test] public void Add_InvalidProduct() { ... }
- [Test] public void Add_Null() { ... }
4. The scope of how much checking to do in a single test case (test method) is a judgement call. I usually stick to a single method call with a single set of arguments, but you can stuff a single test case with multiple method calls and assertion checks. In the example above, that would mean creating a single Add() method and testing adding a single product, multiple products, invalid product and null all in one test method.
5. You can use the NUnit console or NUnit GUI to run your tests. The console I find is quicker. I usually use NAnt to run my NUnit tests from the console but I also find it convenient to use the NUnit GUI when I want to run a subset of my tests. Keep in mind, though, that the NUnit GUI uses the GUI thread as the main thread so tests aren't run in the main thread. This may cause issues if your code expects to be running in the main thread - I learned this lesson the hard way.
Posted by Misha Rybalov at 06:00 PM | Comments (0)
May 03, 2006
Dealing with Frustrating Development Issues - Part 2 (The End)
As I previously wrote I was having some issues with trying to get a parent window from MSHTML.IHTMLDocument2. Turns out that the issue is the NUnit GUI!
Who would have thought that a unit testing tool affects how you have to write your core application code, but that seems to be the case. No wonder running these tests worked previously - I was using the NUnit console application. When you use the NUnit GUI, you have to take into account that it uses MTA (multi-threaded apartment threading model) by default. The way I got my code to work was to set the default threading model to STA (single-threaded apartment) like so:
System.Threading.Thread.CurrentThread.ApartmentState = System.Threading.ApartmentState.STA;
The reason this is necessary is because the call to IHTMLDocument2.parentElement has to be done from the main thread. Otherwise you get a nasty System.InvalidCastException. If you're running the NUnit console, it's not a problem because the main thread is the test thread. But if you're running the NUnit GUI, the main thread is the GUI thread so I kept running up against that nasty exception. Setting your code to run in STA mode fixes this problem.
After hours of searching, banging my head against the wall, trying dozens of solutions, it all comes down to a single line of code. I set this in the Browser static constructor so it's automatically executed whenever you use ABCWebTest. I didn't want to force all ABCWebTest.NET users to have to put this line into their NUnit tests.
Posted by Misha Rybalov at 11:50 AM | Comments (0)