February, 2011

Posts
  • Eric Gunnerson's Compendium

    Default parameters in C#

    • 4 Comments

    From an internal discussion we're having on the advisability of using default parameters in C#:

    Currently, the pain and limitation of doing overloads forces you to rethink how a method should work. Consider the following:

     Process(int a);

    Process(int a, float b);

    Process(int a, float b, string c);

     

    If I now need to change how that works in some situations, I could add a boolean to control that behavior, but it’s not obvious how to add it to the current overload scheme. I can do something like:

     

    Process(int a);

    Process(int a, bool doBackground);

    Process(int a, float b);

    Process(int a, float b, bool doBackground);

    Process(int a, float b, string c);

    Process(int a, float b, string c, bool doBackground);

     

    But not only is that a bit hard to write, it’s a bit confusing, and if I need to add another parameter in the future, I’m pretty much SOL. So, that forces me to consider the alternatives; should I go with a “Settings” class (like XmlWriterSettings), should I live with it, or should I think about refactoring the process to simplify things? That forced stop gives me the chance to think about better ways to approach things.

     

    With default parameters, it’s going to be really tempting to just add a default, and it’s more likely you’ll end up with methods like this:

     

    Process(int a, float b, string c, bool doBackground = false, bool writeToLog = true, string database=null, string method=”jumble”);

     

    That is bad not only for the caller of the api, but it suggests that the process method is pretty complex internally.

     

    On the other hand, I can’t count how many times I’ve written a well-constrained series of overloads that purely added in default values and had to write nearly duplicate xml docs for each of them, and I’d be really happy to save that time and not have those methods clutter up the code. 

     

    Or, to put it another way, default parameters are great if you use them to simplify scenarios that you would have written with overloads. If you start doing things that would be hard to express in overloads, I’d look harder at the overall design.

     

  • Eric Gunnerson's Compendium

    Writing Tests for HealthVault Applications

    • 0 Comments

    We have added some useful new functionality designed to make it easier to test a HealthVault application.

    The existing HealthVault SDKs didn’t make it easy if you wanted to write isolated (or method-level) unit tests for features that talked to the HealthVault Platform. You could usually do it by designing your own layer that could return simulated results (sometimes called “mocking” in agile methodologies), but that was sometimes difficult because of the way the SDK worked.

    In this release, we’ve made some changes that should make this a whole lot easier.

    For the sake of discussion, consider a bit of application code that fetches medications from HealthVault and filters them:

             HealthRecordItemCollection GetNewMedications(HealthRecordAccessor record)
             {
                 HealthRecordSearcher searcher = record.CreateSearcher(Medication.TypeId);
                 HealthRecordItemCollection items = searcher.GetMatchingItems()[0];
                 // filter items here... 
     
                 return  items;
             }
     

    We want to write a test that verifies that the “filter items here” part of the method works correctly, and we’d like the test to run without talking to the HealthVault platform. That’s not easy to do in the current SDK, because there’s no way to control what comes back from GetMatchingItems().

    In the new SDK, there is a way to do that. If we debug down we will find that the call to GetMatchingItems ends up in the following method in a new class named HealthVaultPlatform.

     

             public  static  ReadOnlyCollection <HealthRecordItemCollection > GetMatchingItems(
                 ApplicationConnection  connection,
                 HealthRecordAccessor  accessor,
                 HealthRecordSearcher  searcher)
             {
                 return  HealthVaultPlatformItem .Current.GetMatchingItems(connection, accessor, searcher);
             }
     

    The HealthVaultPlatform class centralizes all operations (except for one exception I’ll cover later) in a single class – if the SDK needs to talk to HealthVault it goes through that class. You can call into that class directly if you wish, or just troll through to see what operations can be performed.

    To create our test, we are going to be hooking in underneath that level. The method above just forwards into a method in the HealthVaultPlatformItem class, and that class provides a way for us to override the behavior.

    To get started, we need to create a class that derives from HealthVaultPlatformItem and overrides the GetMatchingItems() method. The first version looks like this:

         public  class  HealthVaultPlatformItemMock  : HealthVaultPlatformItem
         {
             HealthRecordItemCollection _itemsToReturn;
     
             public  HealthVaultPlatformItemMock(params  HealthRecordItem[] items)
             {
                 _itemsToReturn = new  HealthRecordItemCollection(items);
             }
     
             public  override  ReadOnlyCollection <HealthRecordItemCollection> GetMatchingItems(
                 ApplicationConnection connection, 
                 HealthRecordAccessor accessor, 
                 HealthRecordSearcher searcher)
             {
                 List <HealthRecordItemCollection> collections = 
                     new List <HealthRecordItemCollection>();
                 collections.Add(_itemsToReturn);
     
                 return  new  ReadOnlyCollection <HealthRecordItemCollection>(collections);
             }
         }
     

    We can use it like this (this is an NUnit test):

             [Test]
             public  void  GetMatchingItems()
             {
                 Medication medication = new  Medication(new  CodableValue("Ibuprofen" ));
                 Medication medication2 = new  Medication(new  CodableValue("Vitamin C" ));
     
                 HealthRecordItemCollection newItems = null ;
                 HealthVaultPlatformItemMock mock = new  HealthVaultPlatformItemMock(medication, medication2);
                 HealthVaultPlatformItem.EnableMock(mock);
                 ApplicationConnection connection = new  ApplicationConnection(Guid .NewGuid());
                 HealthRecordAccessor accessor = new  HealthRecordAccessor(connection, Guid .NewGuid());
                 newItems = GetNewMedications(accessor);
                 HealthVaultPlatformItem.DisableMock(mock);
     
                 Assert.AreEqual(2, newItems.Count);
                 Assert.AreEqual("Ibuprofen" , ((Medication)newItems[0]).Name.Text);
                 Assert.AreEqual("Vitamin C" , ((Medication)newItems[1]).Name.Text);
             }
     

    When the call to GetMatchingItems() gets down to HealthVaultPlatformItems, it will end up calling our mocked method rather than the built-in one.

    The code requires us to do a few things:

    1. Create an instance of the mock class.
    2. Enable the mock.
    3. Disable the mock.

    We can make it nicer by having the mock class itself handle enabling and disabling the mock, using the following:

         public  class  HealthVaultPlatformItemMock  : HealthVaultPlatformItem, IDisposable 
         {
             HealthRecordItemCollection _itemsToReturn;
     
             public  HealthVaultPlatformItemMock(params  HealthRecordItem[] items)
             {
                 _itemsToReturn = new  HealthRecordItemCollection(items);
                 HealthVaultPlatformItem.EnableMock(this );
             }
     
             public  override  ReadOnlyCollection <HealthRecordItemCollection> GetMatchingItems(
                 ApplicationConnection connection, 
                 HealthRecordAccessor accessor, 
                 HealthRecordSearcher searcher)
             {
                 List <HealthRecordItemCollection> collections = new  List <HealthRecordItemCollection>();
                 collections.Add(_itemsToReturn);
     
                 return  new  ReadOnlyCollection <HealthRecordItemCollection>(collections);
             }
     
             #region  IDisposable
             ~HealthVaultPlatformItemMock()
             {
                 Dispose(false );
             }
     
             /// <summary> 
             /// Disposes the request. 
             /// </summary> 
             ///  
             public  void  Dispose()
             {
                 Dispose(true );
                 GC.SuppressFinalize(this );
             }
     
             /// <summary> 
             /// Disables the mocking. 
             /// </summary> 
             ///  
             /// <param name="disposing"></param> 
             ///  
             protected  void  Dispose(bool  disposing)
             {
                 HealthVaultPlatformItem.DisableMock();
             }
     
             #endregion  IDisposable
      
         }
     
    That allows us to simplify our test code to this:
     
             [Test]
             public  void  GetMatchingItems()
             {
                 Medication medication = new  Medication(new  CodableValue("Ibuprofen" ));
                 Medication medication2 = new  Medication(new  CodableValue("Vitamin C" ));
     
                 HealthRecordItemCollection newItems = null ;
                 using  (HealthVaultPlatformItemMock mock = new HealthVaultPlatformItemMock(medication, medication2))
                 {
                     ApplicationConnection connection = new  ApplicationConnection(Guid .NewGuid());
                     HealthRecordAccessor accessor = new  HealthRecordAccessor(connection, Guid .NewGuid());
                     newItems = GetNewMedications(accessor);
                 }
     
                 Assert.AreEqual(2, newItems.Count);
                 Assert.AreEqual("Ibuprofen" , ((Medication) newItems[0]).Name.Text);
                 Assert.AreEqual("Vitamin C" , ((Medication) newItems[1]).Name.Text);
             }
     

    Special Classes

    There are a few classes where it’s not straightforward to create the class. For classes such as ServiceInfo, we don’t provide a way to create and modify them directly. To create an instance of those classes, you need to derive a new class and use that:

         public  class  ServiceInfoTest  : ServiceInfo
         {
             public  ServiceInfoTest(string  version)
             {
                 Version  = version;
             }
         }
     

    and the associated test uses this class instead of ServiceInfo:

             [Test]
             public  void  GetServiceInfoTest()
             {
                 ApplicationConnection connection = new  ApplicationConnection(Guid .NewGuid());
     
                 ServiceInfoTest serviceInfo = new  ServiceInfoTest("V2.x" );
     
                 ServiceInfo serviceInfoBack = null ;
                 using  (HealthVaultPlatformInformationMock mock = new  HealthVaultPlatformInformationMock(serviceInfo))
                 {
                     serviceInfoBack = connection.GetServiceDefinition();
                 }
     
                 Assert.AreEqual("V2.x" , serviceInfoBack.Version);
             }
     

     

    Limitations

    We currently don’t have mockable interfaces for blob operations. We hope to do that in a future release.

Page 1 of 1 (2 items)