Monday, April 27, 2009

Visual Studio Unit Testing – System.TypeLoadException

So I was trying to write unit tests today and I ran into quite a weird issue.  My test failed on a simple constructor test.  When I looked at the error message I noticed something odd: 

VersionErrorMessage

The referenced assembly version in the picture is x.x.x.91, but the current version it should be is a later version.  What gives?  I tried creating a new unit test project because I thought maybe our unit test project got messed up somehow, but the same error message kept on happening.  Another frustrating issue was that when I ran the unit test in debug mode, it passed correctly. 

After some searching on the web I ran into this page, which led me to check the project’s code coverage instrumentation section.  Sure enough, the project was pointing to an assembly that no longer existed.  The red one was the non-existent assembly, the green one is the correct one.

WrongArtifact

My guess as to why this happened was because initially, the solution was setup to build against Any CPU and this was changed to x86 somewhere along the way.  The LocalTestRun.testrunconfig probably never got updated.  After pointing to the correct assembly, the test passes and no odd version issues exist. 

Incidentally, I asked Greg if his laptop ran into some slowness lately because my hard drive would thrash and adding unit tests literally took me longer than 5 minutes via right-click.  Correcting the instrumented assembly corrected this issue as well.  One would think that Visual Studio would let the user know that something was up :)

Tuesday, April 21, 2009

MS Tech Ed 2009

I’ll be attending Tech Ed this year, which is the first conference of this kind for me. So here’s my badge:

TENA_blgr1_imattending

You can get your badges for Tech Ed hereGreg will also be there, so stop by his booth to say hi and take a picture with him or something.

Tuesday, March 24, 2009

Excel and CSV files

Have you ever tried to open a comma delimited .csv file in MS Excel and gotten these dialogs?

ExcelCSV

ExcelCSV2

There might actually be nothing wrong with your file.  If the first item happens to be ID (case sensitive), you will get this dialog in Excel (I’ve had this happen to me in 2003 and 2007).  If you don’t want this dialog to keep coming up, you’ll have to rename the first item or just change the casing of ID to id, Id, or iD.

Tuesday, December 9, 2008

Visual Studio Unit Testing – Reducing Redundant Tests

When I first started unit testing, I tested as many methods as I could.  These included public and private ones.  A bit of searching on the web about public versus private method testing will yield mixed results.  I personally test both since I aim for at least 70% code coverage.  Visual Studio creates Accessor classes for private methods and properties for you so testing private methods is easy.

I used to write at least one test per method (some require more than one to test conditional code paths), but this can get redundant.  The steps I take to reduce redundancy today are as follows.  First, I turn on code coverage.  This will give a visual indicator of what has been covered in my classes. 

SetCodeCoverage

SetCodeCoverage2

When I run my tests, code that has been covered is in blue and the code that hasn’t been covered is in red.

bluered

I test my constructors first, then public methods, and finally private methods.  Doing it in this order gives me a better idea of what private methods need to be tested.  Most of the time public methods will call private methods so writing a single test will cover those methods as well.  I collapse whatever has been covered each time I create and run a test.  Doing my tests this way has greatly reduced the number of redundant tests written and has kept the same amount of code coverage.

Thursday, September 4, 2008

ADO.NET Asynchronous Transactions

Searching on the web and on the MSDN forums for Asynchronous Transactions didn’t give me what I was looking for.  I knew a bit about both, so I decided trying to combine the two to get the result I wanted.  This was the general outline of what I came up with.

Public Sub DoLongSQLOperation()
    Dim ConnectionStringBuilder As New SqlClient.SqlConnectionStringBuilder

    ConnectionStringBuilder.IntegratedSecurity = True
    ConnectionStringBuilder.DataSource = "SQLSERVER"
    ConnectionStringBuilder.InitialCatalog = "DATABASE"
    ConnectionStringBuilder.AsynchronousProcessing = True

    Dim MySQLConnection As New SqlConnection(ConnectionStringBuilder.ToString)
    MySQLConnection.Open()

    Dim LongSQLCommand As New SqlCommand("sp_LongOperation", MySQLConnection)

    Dim MySQLTrans As SqlTransaction = MySQLConnection.BeginTransaction

    LongSQLCommand.Transaction = MySQLTrans
    LongSQLCommand.CommandType = CommandType.StoredProcedure

    Dim Callback As New AsyncCallback(AddressOf CallbackMethod)

    Dim Result As IAsyncResult = LongSQLCommand.BeginExecuteNonQuery(Callback, LongSQLCommand)

    While Not Result.IsCompleted
      'do something if needed
    End While

  End Sub

  Private Sub CallbackMethod(ByVal result As IAsyncResult)
    Dim LongSQLCommand As SqlCommand

    LongSQLCommand = DirectCast(result.AsyncState, SqlCommand)
    LongSQLCommand.EndExecuteNonQuery(result)

    Dim MyTransaction As SqlTransaction = LongSQLCommand.Transaction

    Try
      MyTransaction.Commit()
    Catch ex As Exception
      'Try to rollback on a commit exception
      Try
        MyTransaction.Rollback()
      Catch exRollback As Exception
        'Rollback failed
      End Try
    End Try

    'Dispose of your objects
    MyTransaction.Dispose()
    LongSQLCommand.Dispose()
    LongSQLCommand.Connection.Dispose()

  End Sub

I used the Asynchronous Callback method of doing what I needed.  CallbackMethod gets called when the asynchronous operation completes.  Within my callback method is where I commit my transaction and dispose of any data resources.  I’ve kept the error handling to a minimum in my example for brevity, but you’ll definitely want to add them where they’re needed. 

Thursday, July 24, 2008

Visual Studio and Data Driven Unit Tests

Unit testing can be tedious when you have a battery of data to test against.  If you’re just testing against a small number of different data, then using Data Driven Unit Tests might be a bit overkill.  However, Data Driven Unit Tests gives you is a single location of data that can be used throughout your unit testing project.  Instead of modifying the data you typed in your unit test code, you can just modify it in your data file.  I used this MSDN entry as a starting point.

The method we’ll be testing is shown below.

Public Function SomeMethod(ByVal data As String) As Integer
   Return data.Length
End Function

You create the unit test for SomeMethod as usual.  Now what about our data file?  I’ll use an XML data file that just holds a few strings named test.xml.

<Samples>
    <Sample0>My Sample</Sample0>
    <Sample1>This is another sample</Sample1>
</Samples>

Now you have your simple data file.  To hook it into your unit test project, open up the Test List Editor in Visual Studio.  Find the test that you want to use test.xml with and click on that test.

testlisteditor

In the Properties, find the Data Connection String entry and press the ellipses button.  This will bring up the New Test Data Source Wizard.

properties

wizard1

Select XML File and press Next to bring up the next screen.  Press the ellipses button and find test.xml.  This should fill in Table and Preview data for you.

wizard2

Pressing Next will bring you to the final page of the wizard.  Highlight the table and press Finish.

wizard3

You’ll see a dialog after the previous step.  Press Yes and Visual Studio will add test.xml to your unit test project.

wizarddialog

dataadded

If you look at your Data Connection String property, it should now point to test.xml within your unit test project.  The next step is using the data in your unit test.  Here’s a simple example of how to use your data.

<DataSource("Microsoft.VisualStudio.TestTools.DataSource.XML", "|DataDirectory|\test.xml", "Samples", DataAccessMethod.Sequential)> _
<DeploymentItem("TestProject1\test.xml")> _
<TestMethod()> _
Public Sub SomeMethodTest()
  Dim target As Form1 = New Form1
  Dim data As String = String.Empty

  Assert.IsTrue(target.SomeMethod(TestContext.DataRow("Sample0")) = TestContext.DataRow("Sample0").ToString.Length)
End Sub

The attributes for SomeMethodTest get automatically added when you add test.xml to your Data Connection String property.  The main point here is that TestContext.DataRow() is used to access your data.

One issue that I’ve run into was that I wanted to move my data into a sub folder called Data within my unit test project.  This will work, but the path to test.xml is a hard path.  Why does this matter?  I’m part of a team and having a hard path breaks the unit test since not all of our workspace paths are the same.  If you look at the Data Connection String property, you’ll notice that |DataDirectory| is at the beginning before the xml file location.  This transfers fine over different workspaces, however you’ll have to leave the data file where it gets inserted into the project by default.

Wednesday, July 16, 2008

TeamBuild and WiX

Have you ever run into this error using TeamBuild to build a WiX setup project?

light.exe : error LGHT0217: An unexpected external UI message was received: The Windows Installer Service could not be accessed. This can occur if you are running Windows in safe mode, or if the Windows Installer is not correctly installed. Contact your support personnel for assistance.

Done building project "Setup.wixproj" -- FAILED.

It seems this problem is rampant on Vista build machines.  The solution usually had something to do with the vbscript engine, but I’ve tried all of those solutions and we were still failing WiX builds.  Besides, we’re using XP for our build machines.

I decided to login to our build machine using our build service account and manually build the WiX project through the IDE.  The project compiled fine so I kicked off another build.  That didn’t fix anything.  I then decided to compile the project using devenv.exe from the command line.  That compiled fine, but I noticed an ICE## warning.  At first I didn’t think anything of it because after this second step, our builds started to work.

Greg, being the manager that he is, told me to repeat the steps on our other build machine to see if that was indeed the fix to our problem.  I was hopeful, but it didn’t seem to work on the other machine.  Then he said to reboot the original build machine and repeat the steps.  Again, I was hopeful, but it looked like the steps I took before wasn’t the solution.

Later on in the day I tried to login to our build machine again using our service account to repeat the steps from before.  Firing a build didn’t work again, but this time that ICE## warning really caught my eye.  I remembered that the WiX project property page had some settings for ICE validation:

wix

Although the ICE## message being returned by the build process was a warning, it was causing our build to fail.  Checking Supress ICE validation was the key and we now have an automated build for our WiX setup project.