pk software dev blog

custom microsoft .net application development

14. May 2007 16:38
by Paul

Pending Release of Mini SQL Query

14. May 2007 16:38 by Paul

NOTE - Mini SQL Query has been released now - see http://www.pksoftware.net/devblog/post/Beta-Release-of-Mini-SQL-Query.aspx.

 

This is not a product release, but a notice of release (!) for my latest pet project, "Mini SQL Query".

What is Mini SQL Query?...

"Mini SQL Query from PK Software is a minimalist SQL query tool for multiple providers (MSSQL, Oracle, OLEDB, MS Access files etc). The goal of the Mini SQL Query tool is to allow a developer or trouble-shooter to quickly diagnose issues or make changes to a database using a tool with a small footprint, that is fast, expandable and easy to use."
Some Features:
  • Multiple database type connections (e.g. MSSQL, Oracle, Access etc)
  • Syntax Highlighting
  • Object Inspector (Browse the tables, columns etc for the connection)
  • Easy to utilize Plug-In system that has access to all the applications internals
Sample Screenshot - The Mini SQL Query tool in use against the Northwind Sample DB:



I call it "mini" because I wanted to keep it simple and fast. I uses a straight forward but powerful plugin architecture that makes adding menu or toolbar options as simple as adding a reference in a DLL project and implementing a few functions from the IPlugIn interface. I will be pushing out a few posts about the service and command style of coding soon due to its implicit focus on issues such as dependency injection (DIP) and (dare I say it) service-oriented architecture (and no I am not talking about web services!) These techniques in turn improve code quality, testing and in turn maintenance (and again in turn our sanity as programmers...)

I will publish a core product and then make other plugins available for download.

Plugin Example 1...

Here is a simple example - display connection...



Below is the example C# plugin code that displays the current connection string in the editor window.

namespace MiniSqlQuery.Plugin.Example
{
    /// <summary>
    /// This example command inserts the current connection string details into the editor text.
    /// </summary>
    public class DisplayConnectionCommand : CommandBase
    {
        public DisplayConnectionCommand(IServiceContainer services)
            : base(services, "Display Connection Example")
        {
        }
        public override void Execute()
        {
            IEditor editorService = this.ServiceManager.CurrentEditor;
            editorService.Query = string.Format(
                "-- Connection: {0}\r\n\r\n{1}",
                this.ServiceManager.DatabaseConfigurationManager.ConnectionString,
                editorService.Query);
        }
    }
}

Running the menu command:



Simple I know but I wanted to show the command execution approach using services.

Plugin Example 2...

OK - not that exciting! Here is another example where a business object is generated from the result set...

Public Class MakeBusinessObjectFromResultsCommand
    Inherits CommandBase

    Sub New(ByVal services As IServiceContainer)
        MyBase.New(services, "Make BO from Results")
    End Sub

    Public Overrides Sub Execute()
        Dim editor As IEditor = Me.ServiceManager.CurrentEditor

        If Not editor.Result Is Nothing AndAlso _
         editor.Result.Tables.Count > 0 Then
            ' create some simple code gen using the results
            editor.Messages = GenerateClass(editor.Result.Tables(0))
        Else
            editor.Messages = "No results to generate code from."
        End If

    End Sub

    ''' <summary>
    ''' Given a DataTable (<paramref name="dt"/>), a basic VB.NET class is generated.
    ''' </summary>
    ''' <param name="dt">A DataTabel to generate a class from.</param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function GenerateClass(ByVal dt As DataTable) As String
        Dim code As New Text.StringBuilder()
        Dim fieldName As String
        Dim typeName As String

        code.AppendFormat("Public Class {0}{1}", dt.TableName, vbCrLf)
        code.AppendLine()
        code.AppendFormat(" Public Sub New{0}", vbCrLf)
        code.AppendFormat(" End Sub{0}", vbCrLf)
        code.AppendLine()
        For Each column As DataColumn In dt.Columns
            fieldName = "_" + column.ColumnName
            typeName = column.DataType.FullName
            code.AppendFormat(" Private {0} As {1}{2}", fieldName, column.DataType.FullName, vbCrLf)
            code.AppendLine()
            code.AppendFormat(" Public Property {0}() As {1}{2}", column.ColumnName, typeName, vbCrLf)
            code.AppendFormat(" Get{0}", vbCrLf)
            code.AppendFormat(" Return {0}{1}", fieldName, vbCrLf)
            code.AppendFormat(" End Get{0}", vbCrLf)
            code.AppendFormat(" Set(ByVal value As {0}){1}", typeName, vbCrLf)
            code.AppendFormat(" {0} = value{1}", fieldName, vbCrLf)
            code.AppendFormat(" End Set{0}", vbCrLf)
            code.AppendFormat(" End Property{0}", vbCrLf)
            code.AppendLine()
        Next

        code.AppendFormat("End Class")

        Return code.ToString()
    End Function

End Class


Running this command against the "select * from customers" query produces the following:

Public Class Table

    Public Sub New()
    End Sub

    Private _CustomerID As System.String

    Public Property CustomerID() As System.String
        Get
            Return _CustomerID
        End Get
        Set(ByVal value As System.String)
            _CustomerID = value
        End Set
    End Property

    Private _CompanyName As System.String

    Public Property CompanyName() As System.String
        Get
            Return _CompanyName
        End Get
        Set(ByVal value As System.String)
            _CompanyName = value
        End Set
    End Property

    Private _ContactName As System.String

    Public Property ContactName() As System.String
        Get
            Return _ContactName
        End Get
        Set(ByVal value As System.String)
            _ContactName = value
        End Set
    End Property

    Private _ContactTitle As System.String

    Public Property ContactTitle() As System.String
        Get
            Return _ContactTitle
        End Get
        Set(ByVal value As System.String)
            _ContactTitle = value
        End Set
    End Property

    Private _Address As System.String

    Public Property Address() As System.String
        Get
            Return _Address
        End Get
        Set(ByVal value As System.String)
            _Address = value
        End Set
    End Property

    Private _City As System.String

    Public Property City() As System.String
        Get
            Return _City
        End Get
        Set(ByVal value As System.String)
            _City = value
        End Set
    End Property

    Private _Region As System.String

    Public Property Region() As System.String
        Get
            Return _Region
        End Get
        Set(ByVal value As System.String)
            _Region = value
        End Set
    End Property

    Private _PostalCode As System.String

    Public Property PostalCode() As System.String
        Get
            Return _PostalCode
        End Get
        Set(ByVal value As System.String)
            _PostalCode = value
        End Set
    End Property

    Private _Country As System.String

    Public Property Country() As System.String
        Get
            Return _Country
        End Get
        Set(ByVal value As System.String)
            _Country = value
        End Set
    End Property

    Private _Phone As System.String

    Public Property Phone() As System.String
        Get
            Return _Phone
        End Get
        Set(ByVal value As System.String)
            _Phone = value
        End Set
    End Property

    Private _Fax As System.String

    Public Property Fax() As System.String
        Get
            Return _Fax
        End Get
        Set(ByVal value As System.String)
            _Fax = value
        End Set
    End Property

End Class


 

Motivation

My main motivation was the lack of speed I experience (no matter how fast my dev box) in using the "default" tools for SQL development. No way of customizing things easily further frustrated me. With a tool such as Mini SQL Query I can quickly add extras as I need them. For example, one plugin I plan of adding is focused on Access databases, I want to be able to write a query in an editor and push the .Net code to execute that query straight into my development project.

I will but publishing a first cut within the next couple of weeks and from there get some feedback etc.

21. March 2007 02:32
by Paul

Working with unsupported controls in NUnitForms

21. March 2007 02:32 by Paul

Another sequel to the entry on "Getting Started with NUnitForms"

  http://www.pksoftware.net/devblog/post/2007/02/Getting-Started-With-NUnitForms.aspx

This post is focused on compiling the latest NUnitForms code from sourceforge.

I have seen/heard/had a few questions about testing windows forms controls that are not currently supported by NUnitForms (specifically ".Net 2.0 NUnitForms alpha 5 release" - http://sourceforge.net/project/showfiles.php?group_id=95656).

There are 2 answers...

  1. Cut your own (see the "How to add Control Testers" section at http://nunitforms.sourceforge.net/docs.html), or
  2. Get the latest source out of the subversion repository and use the generic tester class...
The subversion connection details are here: http://sourceforge.net/svn/?group_id=95656

You will need TortoiseSVN (http://tortoisesvn.net/) or similar to get the files.

 



Note that all related files for the build are also downloaded (nant, nunit, ncover and ndoc etc) so there won’t be any messing around trying to find the correct library dependencies (ahhh!) Of course, because of this the download is about 8.5mb...

Now... from here on in things could get a little messy. You could have problems with key containters, there may be a duplicate "ButtonTestser.cs" file... The list was getting a little long - the short answer to the "problems" were to compile the NUnitForms project after modifying the signing method of the 2 projects "NUnitForms" and "NUnitForms.ScreenCapture". You will also need to delete the AssemblyKeyName code references in the relevent AssemblyInfo.cs files.



Keep in mind that when you get the latest out of a code repository this sort of thing is not unexpected or "bad". Its a work in progress...

Now the latest build of the NUnitForms DLL will give you access to the generic control tester class. When you need to test a control that does not have its own tester class (e.g. ButtonTester) you can use the generic declaration and create your own tester class, e.g. for a picture box:


public class PictureBoxTester : ControlTester<PictureBox, PictureBoxTester>
{
  // Now implement each overloaded constructor calling the base class
  public PictureBoxTester() {}
  public PictureBoxTester(string name, Form form) : base(name, form) {}
  public PictureBoxTester(string name, string formName) : base(name, formName) {}
  public PictureBoxTester(string name) : base(name) {}
  public PictureBoxTester(ControlTester tester, int index) : base(tester, index) {}
}



Now in the tests you can create tester objects and access all the properties and perform clicks etc:

[Test]
public void ExamplePropertyCheckAndDoubleClickTest()
{
  PictureBoxTester picTester = new PictureBoxTester("pictureBox1");
  Assert.AreEqual(@"C:\dir\somePic.bmp", picTester.Properties.ImageLocation);
  picTester.DoubleClick();
}


Easy as pie right?! Well sort of. But much easier than writing your own windows forms GUI testing framework!!

PK  ;-)

13. February 2007 02:19
by Paul

Getting Started with NUnitForms - GUI Testing with Message Boxes

13. February 2007 02:19 by Paul

A sequel to the entry on "Getting Started with NUnitForms"

  http://www.pksoftware.net/devblog/post/2007/02/Getting-Started-With-NUnitForms.aspx

This post is focused on handling the testing of message boxes in an application.

Another common test requirement that you will probably come across in the GUI world is the use of message boxes. To handle a message box with NUnitForms (i.e. simulate a user click or similar), use a "message box handler" method. Firstly set up the test to "expect" a message box and then supply the name of the method to handle the reaction:

[Test]
public void MessageBoxTest()
{
   base.ExpectModal("Info", "MessageBoxTestHandler");
  ButtonTester runButton = new ButtonTester("RunButton");
  runButton.Click();
}

This tells the test sub-system that a message box is expected and the title should be "Info". Also supplied is the name of a handling method - in this case "MessageBoxTestHandler". This method should create a "MessageBoxTester" and (most likely) click the OK button:

public void MessageBoxTestHandler()
{
  MessageBoxTester messageBox = new MessageBoxTester("No Item Selected");
  messageBox.ClickOk();
}

Other useful methods of the MessageBoxTester class are "ClickCancel" and "SendCommand(cmd)" where "cmd" is an enum value of type MessageBoxTester.Command:
  • OK
  • Cancel
  • Abort
  • Retry
  • Ignore
  • Yes
  • No
  • Close
  • Help
For example:

public void MessageBoxTestHandler()
{
  MessageBoxTester messageBox = new MessageBoxTester("Cancel, are you sure?");
  messageBox.SendCommand(Command.Yes);
}

A practical application of this could be tests where for example if search criteria is not supplied a "no criteria" message box is displayed. Boundary checks in methods are common places for bugs to occur so make sure you perform the same boundary check on the GUI layer!

7. February 2007 04:30
by Paul

Getting Started with NUnitForms

7. February 2007 04:30 by Paul

An introduction to NUnitForms with a basic example of automated Microsoft .Net Windows Forms Testing using NUnit.

NUnitForms is a lesser known extension to the well known NUnit testing framework. See http://nunitforms.sourceforge.net/ for details and downloads. It is currently in the process of being upgraded from .Net Framework V1.1 to V2.0 and is only being distributed as an MSI with no source code (except via CVS as Adam pointed out below).

I have found it quite useful but the documentation was lacking and the learning curve a bit painful, hence this post! I will assume that you have NUnit and NUnitForms installed for the following code samples to run.

How can NUnitForms be used?

NUnitForms gives a developer the ability to approach forms development from a test first perspective, or to simply provide a reliable set of automated regression tests for a user interface. (For the record, I am not getting into concepts such as model-view-controller/presenter, interface coding or the like - all I am demonstrating is how to use NUnitForms for testing.)

NUnitForms replaces (or supplements) the traditional “GUI test harness” method commonly used for GUI development. Sometime the use of even a test harness is bypassed (because of time restrictions of course!) The problem with manual test harnesses are that the typically become a mess that only the original developer can even make sense of and, all the testing is done manually. NunitForms alleviates these issues by making things automated in the first place. I am not against the use of a manual GUI test harness - they still have their benefits - but compared to automated testing they lose out big time. A useful GUI test harness example that would be hard to test in an automated fashion is control resize behaviour and positioning.

How does it work?

Basically there is a lot of reflection and forms/controls parsing to locate controls and invoke the methods that are normally performed by the user, such as pushing a button. For example, a test uses NUnitForms code that fires the “OnClick” event for a button. You put together a series of these events simulating a user and you have an automated script. That’s putting it really simply!

A Simple Example

Get a new instance of VS.Net going. I created a basic form as below:

The textbox and button are named “NameTextbox” and “RunButon” respectively (more on that later).

Add a new project for the unit testing - add references to:

  • The windows application we are testing
  • NUnit
  • NUnitForms
  • System.Windows.Forms

See sample solution setup below:

Now define a test fixture as normal (with the addition of the NUnit.Extensions.Forms using directive) but there are 2 main differences:

  • the fixture needs to inherit from “NUnitFormTest” and to get the test to work at all.
  • we need to override the “Setup” method. In this setup method we create an instance of the form and show it.

Make sure you show it or you will go crazy trying to debug the problem (yes I forgot!) The setup method is called before each test so we start with a clean slate.

Now for a test. This is a dumb test, but remember we are looking at the concept!

When I push the “Run” button, I want whatever test is in the textbox to become the form title, sample test:

We need a TextBoxTester and a ButtonTester. This will give us access to the button and textbox on the form. Assign a value to the textbox and invoke a “click” on the button. Now for the test, we have a reference to the test form at the fixture level so we can get the form title from there.

using System;
using NUnit.Framework;
using NUnit.Extensions.Forms;

namespace NUnitFormsDemo1.UnitTests
{
    [TestFixture]
    public class SimpleFormTests : NUnitFormTest
    {
        SimpleForm _simpleForm;

        public override void Setup()
        {
            _simpleForm = new SimpleForm();
            _simpleForm.Show();
        }

        [Test]
        public void FillTextboxWithData()
        {
            TextBoxTester nameTextbox = new TextBoxTester("NameTextbox");
            ButtonTester runButton = new ButtonTester("RunButton");
            string expected = "From automated test.";
            
            Assert.AreEqual("Simple Form", _simpleForm.Text, "Initial value incorrect.");
            nameTextbox["Text"] = expected;
            runButton.Click();
            Assert.AreEqual(expected, _simpleForm.Text, "Title should be that textbox value.");
        }
    }
}

The test failed.

TestCase 'NUnitFormsDemo1.UnitTests.SimpleFormTests.FillTextboxWithData'
failed: Title should be that textbox value.
String lengths differ. Expected length=20, but was length=11.
Strings differ at index 0.
expected: <"From automated test.">
but was: <"Simple Form">

Now implement the code to pass the test. Here is the rocket science code back in the form:

private void RunButton_Click(object sender, EventArgs e)
{
    this.Text = NameTextbox.Text;
}

Hey - I can do that without NUnitForms!

That particular test yes - with some changes to the sample form itself. The controls would need to be exposed either via a property or similar and the button click can be simulated with the “PerformClick” method. However that calls for exposing controls via properties etc which means alot more coding and to be honest we are just scratching the surface of what NUnitForms can do. The use of NUnitForms allows us to avoid that sort of "coding simply to support testing".

I hope that can get you started, more to come... PK :-)

Demo Source: NUnitFormsDemo1.zip (12.83 KB)


More Forms Testing: