Showing posts with label Unit Tests. Show all posts
Showing posts with label Unit Tests. Show all posts

Saturday, April 9, 2011

Unit Tests: Introduction

This post was moved to my real blog: Unit Tests: Introduction

Wednesday, January 26, 2011

Address Locator Style: Testing

Well since I don’t have ArcDesktop, and can’t do this.

I will do the easy thing and test my Address Locator Style by code, fun fun fun, NOT!

What do I want to achieve?

1. Basic: Create an Address Locator in a GeoDatabase

2. Optional: Delete an Address Locator in a GeoDatabase

3. Advanced: Use an Address Locator in a GeoDatabase

 

Well I decided to start from the ESRI example, that creates an Address Locator in a Personal GeoDatabase (after refactoring):

The main class:

  1. public class AddressLocatorUtils
  2. {
  3.     public void Create(string styleName, string mdbFilePath, string featureClassName, string locatorName)
  4.     {
  5.         var locatorStyle = LocatorWorkspaceUtils.Instance.GetLocatorStyle(styleName);
  6.         var dataset = AccessWorkspaceUtils.Instance.GetDataset(mdbFilePath, featureClassName);
  7.         LocatorWorkspaceUtils.Instance.SetLocatorDatasetName(dataset.FullName, locatorStyle);
  8.  
  9.         if (LocatorWorkspaceUtils.Instance.IsLocatorValidForSave(locatorStyle))
  10.         {
  11.             LocatorWorkspaceUtils.Instance.AddLocatorStyle(locatorName,mdbFilePath,(ILocator)locatorStyle);
  12.         }
  13.         else
  14.         {
  15.             throw new ApplicationException("Something went wrong...");
  16.         }
  17.     }
  18. }

The utils:

  1. public class LocatorWorkspaceUtils
  2. {
  3.     private readonly ILocatorManager2 _locatorManager;
  4.  
  5.     #region Singleton
  6.  
  7.     private static readonly LocatorWorkspaceUtils instance = new LocatorWorkspaceUtils();
  8.  
  9.     // Explicit static constructor to tell C# compiler
  10.     // not to mark type as beforefieldinit
  11.     static LocatorWorkspaceUtils()
  12.     {
  13.     }
  14.  
  15.     private LocatorWorkspaceUtils()
  16.     {
  17.         EsriInitilization.Start();
  18.         _locatorManager =
  19.             (ILocatorManager2)Activator.CreateInstance(Type.GetTypeFromProgID("esriLocation.LocatorManager"));
  20.     }
  21.  
  22.     public static LocatorWorkspaceUtils Instance
  23.     {
  24.         get { return instance; }
  25.     }
  26.  
  27.     #endregion
  28.  
  29.     public ILocatorStyle GetLocatorStyle(string styleName)
  30.     {
  31.         return GetLocatorStyle(styleName, "");
  32.     }
  33.  
  34.     public ILocatorStyle GetLocatorStyle(string styleName, string fileLocation)
  35.     {
  36.         var locatorWorkspace = GetLocatorWorkspace(fileLocation);
  37.  
  38.         // Get the locator style to base the new locator.
  39.         return locatorWorkspace.GetLocatorStyle(styleName);
  40.     }
  41.  
  42.     public void SetLocatorDatasetName(IName datasetName, ILocatorStyle locatorStyle)
  43.     {
  44.         var referenceDataTables = (IReferenceDataTables)locatorStyle;
  45.  
  46.         var enumReferenceDataTable = referenceDataTables.Tables;
  47.         enumReferenceDataTable.Reset();
  48.  
  49.         var referenceDataTableEdit = (IReferenceDataTableEdit)enumReferenceDataTable.Next();
  50.  
  51.         referenceDataTableEdit.Name_2 = (ITableName)datasetName;
  52.     }
  53.  
  54.     public bool IsLocatorValidForSave(ILocatorStyle locatorStyle)
  55.     {
  56.         return ((IReferenceDataTables)locatorStyle).HasEnoughInfo;
  57.     }
  58.  
  59.     public void AddLocatorStyle(string styleName, string fileLocation, ILocator locatorStyle)
  60.     {
  61.         var locatorWorkspace = GetLocatorWorkspace(fileLocation);
  62.         locatorWorkspace.AddLocator(styleName, locatorStyle, "", null);
  63.     }
  64.  
  65.     private ILocatorWorkspace GetLocatorWorkspace(string fileLocation)
  66.     {
  67.         return _locatorManager.GetLocatorWorkspaceFromPath(fileLocation);
  68.     }
  69. }

And AccessWorkspaceUtils contains:

  1. public IDataset GetDataset(string mdbFilePath, string featureClassName)
  2. {
  3.     // Open the feature class to use as reference data.
  4.     IWorkspaceFactory2 workspaceFactory2 = new AccessWorkspaceFactoryClass();
  5.     var featureWorkspace = (IFeatureWorkspace)workspaceFactory2.OpenFromFile(mdbFilePath, 0);
  6.  
  7.     // Set the feature class as the primary reference data table for the locator.
  8.     return (IDataset)featureWorkspace.OpenFeatureClass(featureClassName);
  9. }

My test:

  1. [TestMethod]
  2. public void Create_DllShepherdAddressLocator_AddressLocatorCreated()
  3. {
  4.     const string styleFileName = "Dll Shepherd Address";
  5.     const string referenceDataStyleName = "ZIP 5-Digit";
  6.     var styleName = String.Format("{0} - {1}", styleFileName,
  7.                                   referenceDataStyleName);
  8.     _addressLocatorUtils.Create(styleName, @"C:\Projects\Testings\Esri.AddressLocator\Esri.AddressLocator.CoreTests\bin\Debug\Israel_for_Roy.mdb",
  9.                                 "Streets", "New Dll Shepherd Locator");
  10. }

And it works, it create a style with the name.

Before:

Access-before-creating-address-locator

After:

access-after-creating-address-locator

Well we passed 1.

And then I tested it on something I created and it failed on:

  1. var referenceDataTables = (IReferenceDataTables)locatorStyle;

With this Exception:

System.InvalidCastException was unhandled by user code
  Message=Unable to cast COM object of type 'System.__ComObject' to interface type 'ESRI.ArcGIS.Location.IReferenceDataTables'. This operation failed because the QueryInterface call on the COM component for the interface with IID '{600A5898-DDC1-11D3-9F74-00C04F8ED1C4}' failed due to the following error: No such interface supported (Exception from HRESULT: 0x80004002 (E_NOINTERFACE)).

I decided that though ESRI help might be not very helpful here, their help in ArcCatalog might be better. So I asked Vered to test it and it just asked Vered if she wanted to send the Error to ESRI after closing her ArcCatalog – she almost killed me!

 

The exception is thrown simply because locatorStyle does not implements the IReferenceDataTables interface. Which is weird because locatorStyle can be converted to ESRIGen2AddressLocatorStyle:

Esri-location-model-diagram-locator-style

As you can see ESRIGen2AddressLocatorStyle inherits from ESRIReferenceDataAddressLocatorStyle which implements IReferenceDataTables. The weirdness doesn’t stop here looking at the source for ESRIGen2AddressLocatorStyle:

  1. namespace ESRI.ArcGIS.Location
  2. {
  3.     [CoClass(typeof (ESRIGen2AddressLocatorStyleClass))]
  4.     [Guid("655C5C62-6478-11D3-9F57-00C04F6BDF06")]
  5.     [ComImport]
  6.     public interface ESRIGen2AddressLocatorStyle : ILocator
  7.     {
  8.     }
  9. }

It only implements ILocator and I can’t even find ESRIReferenceDataAddressLocatorStyle, I started to think I can’t read this diagrams but:

ESRI-class-diagram-help

According to this both ESRIReferenceDataAddressLocatorStyle and ESRIGen2AddressLocatorStyle are classes and one inherits from the other – exactly like I thought!

 

 

TODO: add link in “after refactoring” to the post about refactoring ESRI part 1

Resources:

 

Keywords: ESRI, ArcObjects, Access, unit test, address locator

Sunday, January 23, 2011

IntelliTrace:

 

So on Thursday I had a problem with one of the Unit Tests, but it was already late so I decided to go home and get a fresh start on the problem on Sunday. I started the the Unit Test and there indeed was a problem but it was with loading data from an a file GeoDatabase – I don’t have any program that can open such a file…

So I decided to debug the problem and usually it takes a while to reach the problem. My preferred method is to let the program run the first time in debug just to get an idea of where the Exception is located. This time I noticed something, on the right side of the screen a new screen that I haven’t noticed before appeared:

Intellitrace-first-look

Selecting the first Exception navigated me to where the exception poped:

IntelliTrace-exception

  1. public static void InsertFeature(this IFeatureBuffer buffer, IFeature feature)
  2. {
  3.     buffer.Shape = feature.Shape;
  4.  
  5.     for (int i = 0; i < feature.Fields.FieldCount; i++)
  6.     {
  7.         IField field = feature.Fields.get_Field(i);
  8.         if (!field.Required)
  9.             buffer.SetValue(field.Name, feature.get_Value(i));
  10.     }
  11. }

Now this in it self was huge but I still had to know from what the exception occoured, looking at all the variables shown:

intellitrace-debug-information

So I changed the options:

Intellitrace-options

And now for some odd reason some values do show (i doesn’t):

intellitrace-shown-value

Even Locals show some data:

intellitrace-locals-data-watch

The problem was the field Status was in the file but not in the DB. So I changed the code to look at only fields that are both in the buffer and the feature – run all my framework unit test –> they passes. And then run the migration tests and they also passed.

 

//TODO: study intelliTrace more…

 

Keywords: vs2010, intelliTrace, Exception, unit test

Tuesday, January 11, 2011

Unit Tests: What to Test?

Have you read the book Pragmatic Unit Testing in c# with NUnit?
No, read it (it's a must):

 

Well the book has this poster at the back:

Unit-Tests-what-to-test

(click here to get a PDF version directly from their site)


 

Well what I like to test are these things:

1. Right: given good parameters is the function returning good results?

2. Boundary Conditions: given parameters such as null values, empty string, empty list, negative/zero index, out of bound indexes is the method returning the right results (either returns an error, throws an exception or does nothing).

3. Inverse: if you can write some code that returns your parameters back, test that you are getting them back. For example 3++ = 4 , 4-- = 3

4. Cross-Check: if you can write some code that runs the method in a different way, test that for the same input you get the same results. For example: 3++ = 4 , 3+1 = 4

5. Error Condition: test for exceptions – for the right parameters the method throws an exception of the right type. For example when dividing by zero an exception of type DivideByZeroException is thrown.

6. Performance: test for the time/memory/CPU the method took to run



My preferred way of writing Unit tests is:

  1. public class TestedClassTests
  2. {
  3.     public void TestedMethod_WhatIsTested_ExpectedResult(){...}
  4. }

If you tried that and are using Resharper you will get a warning that the naming convention is wrong but there is a solution.

 

Now I usually use a Live Template from Resharper that automatically creates regions with test methods for all the test cases. I have put the template in my Codeplex Project: ResharperTemplates\TestTemplate_ResharperLive.xml



//TODO: Add VS Snippets
Resources:
How to change the ReSharper naming style for test methods

http://stackoverflow.com/questions/463595/poll-c-apps-memory-usage-at-runtime

http://stackoverflow.com/questions/275957/can-a-c-program-measure-its-own-cpu-usage-somehow


Keywords: Unit test, beginner, ExpectException, Exception, Resharper

Tuesday, January 4, 2011

Coded UI testing for Silverlight application

 

So I have started by looking at:

http://timheuer.com/blog/archive/2010/11/24/coded-ui-available-for-silverlight-4.aspx

I have manually created my testing project (wanted a different name)

Tried the application and received this error:

image

 

So it seems the video forgot a step…

So I added a reference to the dll to my Silverlight project :

C:\Program Files (x86)\Common Files\microsoft shared\VSTT\10.0\UITestExtensionPackages\SilverlightUIAutomationHelper\Microsoft.VisualStudio.TestTools.UITest.Extension.SilverlightUIAutomationHelper.dll

 

C:\Program Files\Common Files\microsoft shared\VSTT\10.0\UITestExtensionPackages\SilverlightUIAutomationHelper\Microsoft.VisualStudio.TestTools.UITest.Extension.SilverlightUIAutomationHelper.dll

 

Found this tip: http://blogs.microsoft.co.il/blogs/shair/archive/2010/07/22/coded-ui-test-tip-3-locate-controls-using-win-i.aspx

About using Window+I to locate controls – didn't work for me…

 

For coded UI I need to add ids to each element that I want to test but in Silverlight best practices it says not to!

 

Silverlight Best Practices:

http://www.kunal-chowdhury.com/2010/08/some-best-practices-for-silverlight.html

//TODO: add a link to the best practices

//TODO: too raw…

Simplify and the ArcEngine license

 

So I have tried to make my code simplify the IPolyline result I had by:

        public static IPolyline CreatePolyline(params MapPoint[] points)

        {

            IPolyline polyline = new PolylineClass() { SpatialReference = CreateWgsSpatialReference() };

 

            foreach (var point in points)

            {

                polyline.AddPoint(CreatePoint(point));

            }

            ((ITopologicalOperator)polyline).Simplify();

 

            return polyline;

        }

 

 

The Unit Test that test this part of code won't pass, a pop up pops up:

image

---------------------------

Initialization Error

---------------------------

The runtime application type must be specified before license initialization.

---------------------------

OK  

---------------------------

 

And the different frameworks for unit tests show the same kind of error:

Resharper shows that test as Aborted:

image

VSTS shows that test as Error:

image

image

I think this means that Simplify won't work with ArcEngine license but tell me what you think.

 

Rename Polyline layer

 

Why does it work?

You know that question?

For a long long time I had one test in DeploymentWorkspaceUtils that just didn't want to pass, it was called:

Deployment_RenameLayer_GeometryPolylineOldLayerHasData_GetData

 

The test was simple:

1.       Create a layer of type polyline

2.       Put a feature in the layer

3.       Rename the layer

4.       Get the data back from the renamed layer

I had two more tests identical to this test but with point and polygon data.

But in the part of renaming the layer I got the following error:

System.Runtime.InteropServices.COMException: The number of points is less than required for feature

 

Today I had some time and got back to this test, I thought to myself that since the error is of the number of points I figured the trouble was with reading the data.

My second thought was that I didn't clone the geometry returned into my entity, I placed a breakpoint in my entity and waited and waited some more…

Then I tried to slam my head to the wall – I didn't use entity in rename since I didn't have any entity.

I looked again and in the rename I simply used a List<IFeature>, than I thought about the most annoying Boolean in IWorkspace – IsRecycling mine was of course set to true.

Now the real question is why didn't the Polygon fail as well???

 

Then I thought to myself maybe the geometry in the DB for the polyline is not simplified lets try to simplify it! The result was the same old error => The number of points is less than required for feature

 

So I changed my test to be with two features in step 2 and added an assert that the geometry length (for IPolygon and IPolyline) is greater than zero. For the point I added an  assert on the X and Y of the point.

And still the point and the polygon tests pass, the polygon's length was ~40 which is to say greater than zero, WHY???

 

I decided to add a test that verifies that the 2 entities are different and now the point and polygon tests fail and the polyline test (still) throws an exception.

 

 

In the end I decided to just fix the damn test and try not to think logically with ESRI…

 

 

Mocking ESRI (testing wise)

 

Not that the other mocking is wrong…

 

//TODO: write this post, write about the beginner stuff first

//TODO: write about integration unit test for ESRI first

 

Resources:

http://gis.stackexchange.com/questions/1393/how-to-unit-test-arcobjects-with-mocking

http://www.esri.com/news/arcuser/0110/noyles.html

http://svn2.assembla.com/svn/arcdeveloper/TestingUtilities/

 

del.icio.us Tags: ,,,
IceRocket Tags: ,,,

Unit Test Code Coverage

 

//TODO: Write this post

//TODO: Add print screens

 

Resources:

Ignore Code for coverage:

http://stackoverflow.com/questions/1602898/how-to-decorate-a-class-as-untestable-for-code-coverage

http://www.richard-banks.org/2007/12/how-to-exclude-method-from-code.html

 

To Rhino or not to Rhino?

 

I mean Rhino Mocks of course… (but you can change it to any mocking framework of your choice).

I believe one of the objective in writing good code is writing readable code, and our coded unit test are in code so that as well should be readable.

In many cases I found the readability of tests using Rhino Mocks for everything is slim to none.

Lets look at the following code example:

When a book is added to the system it uses BookDal to add the book, AuthorDal to get the author id and if he doesn't exist to add him, PublicationDal similar to  AuthorDal but with publications.

BookBL's method Add(Book book) looks something like:

Public void Add(string name, string author, MapPoint currentLocation)

{

                var  author =

}

Using Rhino Mocks to test everything here will look like:

[TestMethod]

Public void Add_ValidBook_BookAddedToDb()

{

                Using(_mock.Record())

                {

                               

                }

}

So where do I believe you should use Rhino Mocks:

1.       When you don't care about the outcome of the method.

2.       When you have tests covering the operation of the method.

3.       When you receive data from an outside source in the method (such as MSMQ).

4.       When the using(_mocks.Record()) statement is unreadable – contains too many methods with too many constraints.

5.       When if you change the code you don't need to change the recording.

After all this rules I believe you cannot use Rhino Mocks to mock any work with the DB, because:

1.       You do care about the outcome, this is one of the end result of calling your method.

2.       If the method update/selects/insert/delete to many tables you will end up with a monster that is both unreadable and any changes to your tests will cause your tests not to pass.

For example:

If my code instead of updating did a:

Get+change => delete from Db => insert the changed data

And then I refactored it to simply:

Update

The end result in the DB will be the same but the recording of the mocks will have to change.

 

What do I propose?

If you wrote/use a Dal framework than:

1.       You must write integration coded unit tests to that framework.

2.       You must write a Dal framework that works with an in memory DB or make sure the Dal framework works with in memory DBs.

 

The above example with such a framework:

 

 

//TODO: Add Resources

//TODO: Complete the examples

//TODO: Post the Examples before this post