# Tuesday, June 22, 2004

Ever wondered how you classes you look like in a PropertyGrid control, now it is possible directly from Reflector with my upcoming Reflector Addin:

Ps: As always, a big thanks to Jamie Cansdale (NUnitAddIn) for his help on this one.

posted on Tuesday, June 22, 2004 4:19:00 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [5]

XsdTidy is a refactoring tool to overcomes some silly limitations of the exceptional Xsd.exe  tool provided with the .NET framework. More specifically, XsdTidy addresses the following problems:

  • Name normalization: if your XSD schema is using lower case names or more generally non ".NET" normalized names, you will end up with types that will make the FxCop spit out hundreds of infractions,
  • Fields not properties: xsd.exe creates fields and does not "hide" them in properties which is bad OO design.
  • Fixed Array Sizes: xsd.exe handles multiple elements by creating an array. There is no problem when you are loading the data, but unfortunately this is not convenient if you want to populate a document since arrays do not support Add or Remove. XsdTidy provides strongly-typed collection that support Add, Remove, etc...
  • Default Constructor: Xsd.exe does not care about providing a default constructor that initializes the fields with the proper values. If the object structure is getting big, it becomes very difficult to properly initializes fields,
  • Serializable: Xsd.exe does not tag classes with serializable

Note that XsdTidy uses Refly for building the source code and is also maintained by Marcus Mac Innes.

What does XsdTidy fix ?

Name conversion

The .NET standards define specific naming convention for all types of data: arguments should be camel case, function names capitalized, etc... This is really helpful to keep the framework consistent. Tools like FxCop help us stay on the "normalized" side.

This problem is tackled the dumb way: given a dictionary of "common" words, the class NameConformer tries to split a name in separate words, after that it renders it to the needed convention. Of couse, this feature can be disabled.

FixedArraySize and "Multi" Strongly-Typed Collections

Arrays are replaced by inner strongly-type collections which are much more flexible to use. Moreover, array fields are created by default using their default constructor. This is to economize you the hassle of creating a collection before using it. If an array can support multiple object type, the generate collection will be "multi" strongly typed:

public class TestClass
{
    [XmlArray("values")]
    [XmlArrayItem("car",typeof(Car));
    [XmlArrayItem("car",typeof(Bike));
    public Object[] values;
}

becomes

public class TestClass
{
    private ValueCollection values = new ValueCollection();

    [XmlArray("values")]
    [XmlArrayItem("car",typeof(Car));
    [XmlArrayItem("car",typeof(Bike));
    public ValueCollection Values
    {
        get { return this.values;}
    }

    public class ValueCollection : CollectionBase
    {
        public void AddCar(Car car)
        {
            this.List.Add(car);
        }
        public void AddBike(Bike bike)
        {
            this.List.Add(bike);
        }
        ... // the implementation of the collection
    }
}

Properties

Fields are hidden in properties, which is more convenient to use. Moreover, collection fields do not have set property according to FxCop rule.

public class testclass
{
    [XmlAttribute("values")]
    public String values;
}

becomes:

public class TestClass
{
    private String values;

    [XmlAttribute("values")]
    public String Values
    {
        get
        {
            return this.values;
        }
        set
        {
            this.values = value;
        }
    }
}

Serializable

The output classes are tagged with the Serializable attribute to make them usable using Remoting.

XsdTidy history

I have first started to build XsdTidy using System.Reflection.Emit. It was a titanic job and very error prone. The difficulty of using Emit pushed me to use CodeDom which was also heavy to use. So finally, Refly was designed and XsdTidy became much easier to impement.

Download:

Download XsdTidy and Refly at http://blog.dotnetwiki.org/downloads/Refly.zip

Screenshot

posted on Tuesday, June 22, 2004 9:33:00 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [6]
# Monday, June 21, 2004

Reflector.Graph was becoming messy because of external dependencies so I decided to remove some parts of it (TypeGraph, TreeMap) in order to keep funcitonal.

Reflector version: tested for 4.0.6.0 and 4.0.7.0

Installation procedure:

  • Download the Reflector.Graph Binaries  and unzip it in the Reflector directory (it may be unzipped in a Release directory, copy the file aside to Reflector)
  • In Reflector, Tools -> Addin... -> Add -> Reflector.Graph.dll
  • That's it!

In this release, you do not need to create a Reflector.exe.config.

What's in Reflector.Graph:

  • MethodRank (Assembly),
  • TypeRank (Assembly),
  • Unit Test Generator (Type),
  • Mock Geneator (Type),
  • IL Graph (Method),
  • AssemblyGraph (Tools)

What's not in Reflector.Graph:

  • TypeGraph will be release separately because it needs to be compiled agains .Net v1.1
  • TypeTreeMap will be released separately because it has a dependency on the Microsoft TreeMap

Bugs and suggestions:

 

posted on Monday, June 21, 2004 8:56:00 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [2]
# Sunday, June 20, 2004

The Database Populator Framework  follows the same idea of DbMonster (or at least, was partially inspired from it). The purpose of the framework is to provide a flexible (and smart) set of random data generators to populate your database.

Why ?

If you plan to do database testing, the solution are not much: use transaction, create/kill database after each unit test, clean up after each test or use the new technique using Enterprise Services.

The objective of DPF is not to test the database but provide "food" for your unit test. By generating data randomly (satisfying the constraint), you do not need to clean up and you will avoid test clashing on each other. Moreover, you can also use the DPF to test your database under load.

Random but no dumb

In order to generate data for testing database, you need to take into accounts the constraints on the columns and between the tables. By providing a DataSet representing you database, the framework analyses it and create a set of generators. The data generated satisfies all the constraint. Of course, each row generator can be customized for your own needs, but in general, the information contained in the dataset is enough.

The ideas

The idea are rather simple:

  • Each DataColumn has its corresponding IDataGenerator instance,
  • Each DataTable has its corresponding ITablePopulator instance,
  • Each UniqueConstraint has its corresponding IUniqueValidator instance which takes care of ensuring the all unique constraint are validated,
  • Each ForeignKeyConstraint has its corresponding IForeignKeyConstraint instance which will fetch valid foreign key values

The generation of a new row is made inside a ITablePopulator as follows:

  1. Create a row,
  2. Call each IDataGenerator and fill the row,
  3. For each foreign key, get relevant values,
  4. Verify unique constraints, if violated go to 1,
  5. return row

The first bits

The first bits of the framework is located in the MbUnit.Framework.Data namespace. Althout the framework still needs work, the first example is running and is quite promising.

I have built a simple database containing User - Order - Product - OrderProduct:

this.dataSet=new DataSet();

DataTable users=dataSet.Tables.Add("Users");
DataColumn userID = users.Columns.Add("UserID",typeof(int));
DataColumn userName=Users.Columns.Add("UserName",typeof(string));
DataColumn userName.AllowDBNull=false;

DataTable orders=dataSet.Tables.Add("Orders");
DataColumn orderID=orders.Columns.Add("OrderID",typeof(int));
DataColumn orderDate = orders.Columns.Add("OrderDate",typeof(DateTime));
DataColumn oUserID = orders.Columns.Add("UserID",typeof(int));

DataTable products=dataSet.Tables.Add("Products");
DataColumn productID=products.Columns.Add("ProductID",typeof(int));
DataColumn productName = products.Columns.Add("ProductName",typeof(string));
DataColumn productPrice = products.Columns.Add("ProductPrice",typeof(decimal));

DataTable orderProducts=dataSet.Tables.Add("OrderProducts");
DataColumn opOrderID=orderProducts.Columns.Add("OrderID",typeof(int));
DataColumn opProductID=orderProducts.Columns.Add("ProductID",typeof(int));
DataColumn quantity=orderProducts.Columns.Add("Quantity",typeof(int));
// pks
users.Constraints.Add("PK_Users",userID,true);
orders.Constraints.Add("PK_Orders",orderID,true);
products.Constraints.Add("PK_Products",productID,true);
orderProducts.Constraints.Add("PK_OrderProducts",
new DataColumn[]{ opOrderID, opProductID}
,true);
// fks
orders.Constraints.Add("FK_Orders_Users",userID,oUserID);
orderProducts.Constraints.Add("FK_OrderProducts_Orders",orderID,opOrderID);
orderProducts.Constraints.Add("FK_OrderProducts_Products",productID,opProductID);

This database is easily populated using the DPF:

// creating populator
IDatabasePopulator pop=new DatabasePopulator();
// anaylising internal structure
this.pop.Populate(this.db.DataSet);

After that, you can customize the behavior of every data generator. Once this is done, you are ready to "feed" your database:

//getting the users table populator
ITablePopulator userPop = pop.Tables[users];
// adding new row
users.Rows.Add( userPop.Generate() );
posted on Sunday, June 20, 2004 11:03:00 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [8]

Refly is the library that I have developped to make CodeDom usable. (See my CodeProject article here, or the classic Hello World example). Note that Refly comes with XsdTidy, an application that "pretify" the output of the Xml.exe tool.

Download Now

 

posted on Sunday, June 20, 2004 10:17:00 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [3]

If you are doing database testing, you should read this great post by Roy:

Simplified Database Unit testing using Enterprise Services

posted on Sunday, June 20, 2004 8:31:00 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [1]
# Friday, June 18, 2004

The Production Grammar Framework is finally fully integrated into MbUnit as a new Fixture (GrammarFixture).

An example:

I'm currently building a new framework for generating "relevant" data for randomly populating database (similar ideas to DbMonster). As you may have noticed, I don't practive dogfood much so I decided to start now. I'm writing the test code along with the framework (of course, I'm testing myself here so it's a bit twisted). Anyway, I had to test the following collection:

public interface IDataGeneratorCollection : ICollection
{
    IDataGenerator this[DataColumn column]{get;}
    IDataGenerator this[String columnName]{get;}
    void Add(IDataGenerator dataGenerator);
    void Remove(IDataGenerator dataGenerator);
    void Remove(DataColumn column);
    void Remove(String columnName);
    bool Contains(IDataGenerator dataGenerator);
    bool Contains(DataColumn column);
    bool Contains(String columnName);
    void Clear();
}

At first, I decided to write a TypeFixture and produce a bunch of unit tests for that, but I quickly got bored, so I turned myself to production grammars which are much more fun. The grammar for this collection can be summarized as follows (see the stack example):

-- dg is a IDataGenerator
add := Add(dg)
remove := Remove(dg)
contains := Contains(dg)

guardedRemove := guard(InvalidOperatoinException, remove)

empty := add,guardedRemove,contains
nonEmpty := add,remove,contains

startRule := if(collection.IsEmpty) { empty } else { nonEmpty }

MbUnit Attributes

Now that we have built a grammar we need to tell MbUnit to load the grammar it feed it with seeds:  GrammarFixtureAttribute describes a production grammar fixture, GrammarAttribute describes a method that return a IGrammar instance, SeedAttribute returns an object that is feeded into the production constructor.

[GrammarFixture]
public class DataGeneratorCollectionGrammar : Grammar
{
    ... // rules creation etc...

    [Grammar]
    public Grammar This()
    {
        return this;
    }
    [Seed]
    public int Seed10()
    {
        return 10;
    }
    [Seed]
    public int Seed20()
    {
        return 20;
    }
    [Seed]
    public int Seed50()
    {
        return 50;
    }
    [Seed]
    public int Seed200()
    {
        return 200;
    }
}

Screenshot: the grammar test case + the console output

posted on Friday, June 18, 2004 3:51:00 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [3]

Aaron has written a "Hello World" sample using Refly.

Refly is a helper wrapper around the CodeDom namespace that makes it much less "noisy". Refly is used in the Unit Test Generator Addin. If you are using CodeDom regularly, maybe you should have a look at this.

posted on Friday, June 18, 2004 1:14:00 PM (Pacific Daylight Time, UTC-07:00)  #    Comments [2]

This is a quick recap on the available features of MbUnit 2.15.1:

Fixtures:

Fixture type define how MbUnit is going to explore a class and build the test cases.

Fixture can be organized in a number of was. Automatically, they are organized by Namespace and Authors, but you can also tag a http://blog.dotnetwiki.org/archive/2004/05/29/277.aspx, and put them into multiple categories

Decorators:

Decorators are used on the "method" level to modify the behavior of a given test. Decorators can be chained to combine their effect.

Assertion classes

Aside from the classic Assert class, there are a number of specialized assertion classes:

 

posted on Friday, June 18, 2004 6:39:00 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [5]
# Thursday, June 17, 2004

I'm preparing a little Reflector Addin to be able to load and execute MbUnit fixtures inside Reflector. Since the fixture tree contains all the functionalities, it was just a matter of injecting it into Reflector.

Screenshot:

The source code is so short that I'm also publishing (note that I'm using the helper classes described here)

using System;
using System.Windows.Forms;
using Reflector.CodeModel;
using MbUnit.Forms;
namespace Reflector.Graph.Faulty
{ 
    // Reflector package
    public class MbUnitPackage : BasePackage
    {
        [ReflectorWindow("MbUnit")]
        [ReflectorCommandBar(CommandBarTarget.Assembly)]
        private MbUnitWindow MbUnit=new MbUnitWindow(); 
    }

    // Tree View
    public class MbUnitWindow : ReflectorTreeView
    {
        private ReflectorServices services =null;
        public MbUnitWindow()
        {
            this.Dock =DockStyle.Fill;
        }
        // needed to get reflector current element
        public ReflectorServices Services
        {
            get
            {
                return this.services;
            }
            set
            {
                if (this.services!=null)
                {
                    this.services.AssemblyBrowser.ActiveItemChanged-=new EventHandler(this.activeItem_Changed); 
                }
                this.services=value;
                if (this.services!=null)
                {
                    this.services.AssemblyBrowser.ActiveItemChanged+=new EventHandler(this.activeItem_Changed); 
                }
            }
        }
        private void activeItem_Changed(Object sender, EventArgs args)
        {
            this.RemoveAssemblies();
            IAssembly assembly = this.Services.ActiveAssembly;
            if (assembly!=null)
                this.Translate();
        }

        // populate tree with current assembly
        public void Translate()
        {
            IAssembly assembly =this.Services.ActiveAssembly;
            if (assembly==null)
                return;
            this.RemoveAssemblies();
            this.AddAssembly(assembly.Location);
            this.ThreadedPopulateTree();
        }
    }
}
posted on Thursday, June 17, 2004 11:06:00 AM (Pacific Daylight Time, UTC-07:00)  #    Comments [3]