James's profileJames McCaffreyBlogLists Tools Help

Blog


    August 29

    The Difference Between Unit Testing and Module Testing

    A common source of confusion for new software testers is the difference between unit testing and module testing. In general, unit tests are a collection of tests written by a developer during the software development process. Module tests are a collection of tests written by a tester after some code has been written by a developer. There are many exceptions to this generalization but the key point is that unit testing is primarily a development related activity, and module testing is primarily a testing related activity. When a developer creates unit tests during development, this approach is sometimes called test driven development. So which is better, unit testing or module testing? The answer is that the two approaches are complementary, not exclusive. In most cases, neither unit testing nor module testing are sufficient by themselves, and both approaches should be used. Let me explain. Suppose you are creating a PokerLib library for some sort of card game. Imagine you design a Card object like:
     
    public class Card {
     private string rank; // A,2,3, . . T,J,Q,K
     private string suit; // c,d,h,s
     public Card(string c) {
      this.rank = c[0].ToString();
      this.suit = c[1].ToString();
     }
     // Rank property, etc.
    }
     
    An associated unit test might look like:
     
    [TestMethod()]
    public void CardConstructorTest()
    {
     Card card = new Card("Ac");
     Assert.IsTrue(card.Rank == "A");
    }
     
    This gives the developer confidence that he has written the Card constructor and Rank property correctly. However, there are a total of 52 legal inputs to the Card constructor (Ac, Ad, . . Kh, Ks) and all of them should be tested. In most cases, developers would not want to create 52 separate unit tests for such a simple constructor. But module testing could test all 52 legal inputs and might take the form of a separate test harness program and look like:

    static void Main(string[] args)
    {
     string[] testCases = { "001:Ac:A", "002:Ad:A", (etc.) "052:Ks:K" };
     foreach (string tc in testCases)
     {
      string[] tokens = tc.Split(':');
      string caseID = tokens[0];
      string input = tokens[1];
      string expected = tokens[2];
      CardLib.Card c = new CardLib.Card(tokens[1]);
      if (c.Rank == expected)
       Console.WriteLine(caseID + " Pass");
      else
       Console.WriteLine(caseID + " FAIL");
     }
     
    Module tessting is often used when the development environment includes dedicate software testers, such as most Microsoft product groups.
     
    UnitTestModuleTest-UnitTest
    August 21

    SQL Server 2008 and Testing - Sparse Columns

    SQL Server 2008 was just released a couple of weeks ago. I've been looking at it, trying to determine the implications SQL Server 2008 has for testing. The first step in such an analysis is to understand exactly what the new features of SQL Server 2008 are. One of the interesting new features is a new "sparse columns". In frequent situations, you have a table which has a column where the majority of values in that column are NULL. For example, consider a table of employee information which contains columns lastName, firstName, middleInitial, and suffix. The suffix column holds information like "Jr." (junior) or "III" (the third). In almost all cases, an employee's name does not contain a suffix. SQL Server 2008 allows you to identify a column with a sparse keyword, which in theory, will save disk space because null values will not be stored. Consider this demo script:

    -- Sparse columns demo
    use master
    go
     
    if exists(select name from sys.sysdatabases where name='dbSparseDemo')
     drop database dbSparseDemo
    go
     
    create database dbSparseDemo
    go
     
    use dbSparseDemo
    go
     
    -- "consider using a sparse column on very large tables where the
    -- column is NULL at least 2/3 of the time
    -- cannot apply to types geometry, geography, text, ntext, timestamp, image,
    -- or user-defined types"
    create table tblEmployees1 -- "regular" table
    (
    emp_id char(3) primary key,
    emp_lastName varchar(35) not null,
    emp_firstName varchar(20) null,
    emp_suffix varchar(5) null -- like 'Jr.' or 'III'; usually null
    )
    go
     
    create table tblEmployees2 -- using a sparse column
    (
    emp_id char(3) primary key,
    emp_lastName varchar(35) not null,
    emp_firstName varchar(20) null,
    emp_suffix varchar(5) sparse null -- like 'Jr.' or 'III'; usually null
    )
    go
     
    declare @i int
    set @i = 1
    while @i <= 1000 -- 1,000 * 10 = 10,000 rows per table
    begin
     insert into tblEmployees1 values('001','Anderson','Adam','Jr.')
     insert into tblEmployees1 values('002','Baker','Betty',null)
     insert into tblEmployees1 values('003','Collins','Chris',null)
     insert into tblEmployees1 values('004','Denevan','Doug',null)
     insert into tblEmployees1 values('005','Eagen','Edward',null)
     insert into tblEmployees1 values('006','Flynn','Fred',null)
     insert into tblEmployees1 values('007','Graham','Greg',null)
     insert into tblEmployees1 values('008','Humphrey','Harold',null)
     insert into tblEmployees1 values('009','Issacson','Ian',null)
     insert into tblEmployees1 values('010','Johnson','James',null)
     insert into tblEmployees2 values('001','Anderson','Adam','Jr.')
     insert into tblEmployees2 values('002','Baker','Betty',null)
     insert into tblEmployees2 values('003','Collins','Chris',null)
     insert into tblEmployees2 values('004','Denevan','Doug',null)
     insert into tblEmployees2 values('005','Eagen','Edward',null)
     insert into tblEmployees2 values('006','Flynn','Fred',null)
     insert into tblEmployees2 values('007','Graham','Greg',null)
     insert into tblEmployees2 values('008','Humphrey','Harold',null)
     insert into tblEmployees2 values('009','Issacson','Ian',null)
     insert into tblEmployees2 values('010','Johnson','James',null)
     set @i = @i + 1
    end
    go
     
    exec sp_spaceused tblEmployees1
    exec sp_spaceused tblEmployees2
    go
    -- end script
     
    In theory at least, the tblEmployees2 table which has a sparse column should be significantly smaller than the tblEmployees1 table. But in fact tblEmployees2 is larger than tblEmployees1, for reasons which are not at all clear to me. My point is, if you use the new sparse column feature in SQL Server 2008, you need to test to verify that you are in fact saving disk space.
     
    August 15

    Delegates, Anonymous Methods, Lambda Expressions, LINQ, and Testing

    Let's assume that the more a tester knows about coding, the better he will be able to test code. There is an interesting set of relationships between C# delegates, anonymous methods, lambda expressions, and LINQ. Each of these topics is relatively simple by itself but all of them are a bit tricky when considered together. First, in the vast majority of coding situations you write a method of some sort and then call it directly. For example, if a method SayHelloTo() is defined as:
     
    static void SayHelloTo(string s) {
      Console.WriteLine("Hi there " + s);
    }
     
    then you can directly call the method like:
     
    // call a method normally
    SayHelloTo("George");
     
    which would output "Hi there George" to the screen. Of course you don't need to code up a SayHelloTo() method -- you could have just placed the code directly inline -- so methods are just a way to make program code more readable, editable, and efficient. In .NET 1.x you can call a method indirectly using a delegate. For example:
     
    delegate void StringInVoidOutDelegate(string s); // declare a delegate
    static void Main(string[] args)
    {
      // delegate calls via explicit method, .NET 1.x
      StringInVoidOutDelegate d1 = new StringInVoidOutDelegate(SayHelloTo);
      d1("Steve");
    }
     
    Here the delegate named StringInVoidOutDelegate can act as an alias for any method which returns void and accepts a single string parameter. So if you had a second method, void SayGoodbyeTo(string t) { . . . }, you could call it using the same delegate like:
     
    StringInVoidOutDelegate d2 = new StringInVoidOutDelegate(SayGoodByeTo);
    d2("Bill");
     
    OK, but what's the point? It turns out that calling methods indirectly using delegates is very useful for certain programming scenarios including event-driven situations like those found in Windows application programming. Now in .NET 2.0 a new syntax called anonymous methods was introduced. Anonymous methods allow you to simplify your code a tiny bit, for example:
     
    // delegate call via anonymous method, .NET 2.0
    StringInVoidOutDelegate d3 =
      delegate(string s) { Console.WriteLine("Howdy " + s); };
    d3("James");
     
    Here instead of associating your delegate with some method defined elsewhere, you directly place the code into a delegate declaration. The only real advantage of anonymous methods is that they reduce the size of your source code somewhat. Now in .NET 3.0 a new syntax called lambda expressions was introduced. Lambda expressions allow you to simplify your code a tiny bit further, for example:
     
    // delegate call via lambda expression, .NET 3.x
    StringInVoidOutDelegate d4 =
      s => { Console.WriteLine("Aloha " + s); };
    d4("Kimo");
     
    The => operator identifies a lambda expression. The operator is read, "goes to." Notice that you don't declare the data type for the input parameter s. The data type will be inferred by the compiler. OK, but what's the point of all this? Lambda expressions are never necessary, they're just syntactic sugar coating. But lambda expressions make a nice companion to LINQ (Language Integrated Query) statements. LINQ is a set of ways to call into various data stores such as string arrays, XML, and SQL, in a unified way. For example:
     
    string[] animals = { "ant", "bat", "cow", "dog", "elk" };
    IEnumerable<string> query = animals
      .Where(a => a.Contains('o'))
      .Select(a => a.ToUpper());
    foreach (string s in query) {
      Console.WriteLine(s);
    }
     
    Whoa. Here you have a string collection and then use lambda syntax to extract just those strings which contain lower case 'o' and then convert those strings to all upper case. The output would be COW DOG. Notice that the "simple" lambda syntax has become a bit ugly, so there is yet another optional simplified form for lambda syntax which, confusingly, deletes the lambda operator, for example:
     
    IEnumerable<string> query = from a in animals
      where a.Contains('o')
      select a.ToUpper();
     
    To summarize, delegates are a way to call methods indirectly. Anonymous methods are a way to simply calling delegates. Lambda expressions are a way to further simplify calling delegates. Lambda expressions are most useful with LINQ queries, and have a shortcut syntax which eliminates the identifying '=>' operator.

     
    August 11

    SQL Server 2008 and Testing - Filtered Indexes

    SQL Server 2008, due to be released within a few months, has a ton of new features. I've been looking at RC0 (release candidate 0), trying to determine the implications SQL Server 2008 has for testing. The first step in such an analysis is to understand exactly what the new features of SQL Server 2008 are. One of the interesting new features is a new filtered index. A regular index on a column in a table greatly increases the speed of SELECT statements which search the indexed column in very much the same way that a book index greatly increases how quickly you can find a term in a book. The new filtered index feature of SQL allows you to create an index for a specified range of values for a particular column. This could be useful if the majority of column values fall into a particular range, and yet the column values can be greatly varied. For example, suppose you have a database of book information. And suppose that the vast majority of your book prices fall between $19.99 and $49.99 but you also have a few books that cost less than $19.99 as well as a few books that costs several hundred dollars. You can create a special filtered index that will be used for searches for book prices between $19.99 and $49.99 as the following demo shows.
     
    -- Filtered Indexes Demo
    use master
    go
     
    if exists(select name from sys.sysdatabases where name='dbBooks')
     drop database dbBooks
    go
     
    create database dbBooks
    go
     
    use dbBooks
    go
     
    create table tblBooks
    (
    b_id int primary key,
    b_title varchar (300) not null,
    b_price money null,
    b_pages int
    )
    go
     
    insert into tblBooks values(1111,'Amazing Atoms',29.95,387)
    insert into tblBooks values(2222,'Blazing Beakers',49.00,422)
    insert into tblBooks values(3333,'Cool Centrifuges',35.49,512)
    go
     
    select * from tblBooks
    go
     
    -- a normal index to increase performance on regular columns
    create nonclustered index ixBooksTitle on tblBooks(b_title)
    go
     
    -- a filtered index for columns where most data is in a range
    create nonclustered index ixBooksPrice on tblBooks(b_price)
    where (b_price >= 19.99 and b_price <= 49.99)
    go
     
    -- selects now use faster filtered indexes if appropriate??
    select b_title,b_price from tblBooks
    where b_price >= 30.00 and b_price <= 39.99
    go
    -- end script

    As you can see, a filtered index is simply a regular SQL index with an added WHERE clause. The real trick is knowing when the performance improvement of creating a filtered index will outweigh the overhead cost of creating and storing the filtered index. I see this as something that a software tester would have to investigate. Additionally, in my demo script above, the filtered index might not help because my SELECT references both the b_title and b_price columns, therefore the execution may have to perform a scan of the entire table. You'd have to examine the execution plan to be sure. (In fact, as far as I can tell the filtered index is not used for the query).
     
    August 01

    Module Testing with Python

    I really like the Python scripting language a lot. The most basic form of test automation is automated module testing. Whenever I am looking at a programming language with regards to its suitability for software testing, the first thing I do is investigate how well the language can do module testing. There are several versions of Python. CPython is most common on machines with Unix-like operating systems. IronPython is the most common on Windows machines. Check out the image below. I used IronPython to perform interactive, ad hoc module testing of a custom .NET library named TwoCardPokerLib.dll I made for demonstration purposes. IronPython is available from CodePlex, the Microsoft-sponsored open source Web site. You don't really install IronPython, you simply download a single zipped file, and extract its contents to any convenient directory. I use C:\IronPython. First I launch the IronPython interpreter (ipy.exe) and get the ">>>" Python prompt:
     
    C:\IronPython>ipy.exe -X:TabCompletion
    IronPython 1.1.1 (1.1.1) on .NET 2.0.50727.1434
    Copyright (c) Microsoft Corporation. All rights reserved.
    >>>
     
    Next I import the built-in sys module which has all sorts of system-related methods. I view the current Python (as opposed to the Windows system) path:
     
    >>> import sys
    >>> sys.path
    ['C:\\IronPython', 'C:\\IronPython\\Lib']
     
    Then I add the location of my module under test so IronPython can find it:
     
    >>> sys.path.append(r'C:\ModuleTestingWithPython\TwoCardPokerLib\bin\Debug')
     
    After verifying my system path was updated correctly, next I import a module named clr, which is short for Common Langague Runtime. This module allows me to load a custom .NET module using the AddReferenceToFile method:
     
    >>> import clr
    >>> clr.AddReferenceToFile("TwoCardPokerLib.dll")
     
    After I import my module, I instantiate two Card objects (representing an Ace of spades and a Nine of hearts):
     
    >>> import TwoCardPokerLib
    >>> c1 = TwoCardPokerLib.Card("As")
    >>> c2 = TwoCardPokerLib.Card("9c")
     
    Now I call a static Card.Beats() method to test its functionality:
     
    >>> print TwoCardPokerLib.Card.Beats(c1,c2)
    True
     
    My mini-test passes because an Ace beats a Nine. I'm a big fan of Python for lightweight test automation. Check it out sometime!
     
    InteractiveDotNETModuleTestingWithPython