But it works on my PC!

The random thoughts of Richard Fennell on technology and software development

TFS Test Agent cannot connect to Test Controller gives ‘No connection could be made because the target machine actively refused it 127.0.0.1:6910’

Updated 1st October – See the Part 2 post which provides more workaround solutions

Whilst setting up a  TFS 2012 Standard Lab Environment for an upcoming demo I hit a problem. Initially my environment had worked fine, I could deploy to my server VM in the environment without error. However, after a reboot of the TFS server (which has the build and test controllers on it) and the single server VM in the environment, the test agent on the VM could not connect to the test controller on the TFS SERVER. The VM’s event log showed

Unable to connect to the controller on 'tfsserver:6901'. The agent can connect to the controller but the controller cannot connect to the agent because of following reason: No connection could be made because the target machine actively refused it 127.0.0.1:6910. Make sure that the firewall on the test agent machine is not blocking the connection.

The key here was test controller was being told to call back to the test agent on 127.0.0.1 – which is obviously wrong being the loopback address.

So it seems the test agent was telling the test server the wrong IP address, not sure why it was resolving this address but I did find a workaround, on the test VM I edited 

‘C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE\QTAgentService.exe.config’

and added the BindTo line with the correct address for the controller to call back to the agent

<appSettings>
     // other bits …
      <add key="BindTo" value="10.0.0.1"/>
</appSettings>

Once I restarted the test agent it connected to the controller and I could run my builds.

For more details on this config file see http://msdn.microsoft.com/en-us/library/ff934571.aspx

Release of Typemock Isolator Basic edition

Some great news today from Typemock, there is now a free basic edition of Typemock Isolator. The addresses a key historic problem with Isolator, that of its cost when you don’t need the advanced features of Isolator all the time.

Now if you need the cool advanced mocking features of Isolator, such as mocking sealed private classes, then the cost is not really a factor, you buy the product or don’t get the features. However what do you do if you just want to do just do ‘normal mocking’ in a project ? e.g. mock out an interfaces. Do you use Typemock as you already have it, or swap to a different mocking framework, only using Typemock when you have to use its advanced features?

This is a particular problem for consulting/bespoke development companies such as mine, we write code for clients that in the future they will have to maintain themselves, they are not that happy with us passing over code with a dependency on a licensed mocking framework unless it is essential to their project. This means in the past I have tended to use other mocking frameworks, usually FakeItEasy as its syntax is very similar to Typemock Isolator, unless I need its advanced features of Typemock such as in SharePoint projects.

However with this new basic edition release from Typemock this is no longer an issue. I can use Typemock in all my projects. If a client need to run the tests, as long as they are ‘normal mocking’ ones, all they need to do is install this new free version of Typemock and the project builds and the tests run. There is only a need to purchase a license if the advanced features of Typemock are required.

So longer do I need to swap mocking framework for only licensing reasons, hence reducing the friction I have had in the past changing mocking syntax.

Experiences upgrading an MVC 1 application to MVC 3

I have recently had to do some work on a MVC 1 application and thought it sensible to bring it up to MVC 3, you don’t want to be left behind if you can avoid it. This was a simple data capture application written in MVC1 in Visual Studio 2008 and never needed to be touched since. A user fills in a form, the controller then takes the form contents and stores it. The key point to note here is that it was using the Controller.UpdateModel<TModel> Method (TModel, IValueProvider) method, so most of the controller actions look like

[AcceptVerbs(HttpVerbs.Post)]
       public ActionResult PostDataToApplication(FormCollection form)
       {
           NewApplication data = new NewApplication();
           try
           {
               UpdateModel(data, form.ToValueProvider());
              
               // process data object
               bool success = DoSomething(data);

               if (success)
               {
                   return RedirectToAction("FormSuccess", "Home");
               }
               else
               {
                   return RedirectToAction("FormUnsuccessful", "Home");
               }
           }
           catch (InvalidOperationException)
           {
               return View();
           }
       }

This worked fine on MVC 1 on VS2008, but I wanted to move it onto MVC3 on VS2012 if possible; with a little changes as possible as this is a small web site that has very few changes so not worth a major investment in time to keep updated to current frameworks. So these were steps I took and gotcha’s I found

The upgrade itself

First I opened the VS2008 solution in VS2010 and it automatically upgraded to MVC2, a good start!

I then used the MVC2 to MVC3 tool on Codeplex, this initially failed and it took me a while to spot that you can only use this tool if your MVC2 application targets .NET 4. Once I changed the MVC2 to target .NET 4 as opposed to 3.5 this upgrade tool worked fine.

I could now load my MVC3 application in either VS2010 or VS2012.

Using the Web Site

At this point I though I had better test it, and instantly saw a problem. Pages that did not submit data worked fine, but submitting a data capture forms failed with Null Exception errors. Turns out the problem was a change in default behaviour of the models between MVC releases. On MVC1 empty fields on the form were passed as empty strings, with MVC 2(?) and later they are passed as nulls.

Luckily the fix was simple. Previously my model had been

public class SomeModel: IDataErrorInfo
{
     public string AgentNumber { get; set; }
     …..
}

I needed to add a  [DisplayFormat(ConvertEmptyStringToNull = false)] attribute on  each string property to get back to the previous behaviour my controller expected

public class SomeModel: IDataErrorInfo
{
    [DisplayFormat(ConvertEmptyStringToNull = false)]
     public string AgentNumber { get; set; }
     …..
}

Now my web site ran as I had expected.

Unit Tests

I had previously noticed my unit tests were failing. I had expected the change to the model would fix this too, but it did not. On the web there a good many posts as to how unit testing of MVC2 and later fails unless you mock out the controller.context. You see errors in the form

Test method Website.Tests.Controllers.HomeControllerTest.DetailsValidation_AlphaInValidAccountID_ErrorMessage threw exception:
System.ArgumentNullException: Value cannot be null.
Parameter name: controllerContext
Result StackTrace:   
at System.Web.Mvc.ModelValidator..ctor(ModelMetadata metadata, ControllerContext controllerContext)
   at System.Web.Mvc.ModelValidator.CompositeModelValidator..ctor(ModelMetadata metadata, ControllerContext controllerContext)
   at System.Web.Mvc.ModelValidator.GetModelValidator(ModelMetadata metadata, ControllerContext context)
   at System.Web.Mvc.DefaultModelBinder.OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at System.Web.Mvc.DefaultModelBinder.BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Object model)
   at System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at System.Web.Mvc.Controller.TryUpdateModel[TModel](TModel model, String prefix, String[] includeProperties, String[] excludeProperties, IValueProvider valueProvider)
   at System.Web.Mvc.Controller.UpdateModel[TModel](TModel model, String prefix, String[] includeProperties, String[] excludeProperties, IValueProvider valueProvider)
   at System.Web.Mvc.Controller.UpdateModel[TModel](TModel model, IValueProvider valueProvider)
   at Website.Controllers.HomeController.Details(FormCollection form)

The fix is to not just new up a controller in your unit tests like this

HomeController controller = new HomeController();

But to have a helper method to mock it all out (which is created for MVC associated test projects for you, so it is easy)

private static HomeController GetHomeController()
{
   IFormsAuthentication formsAuth = new MockFormsAuthenticationService();     
   MembershipProvider membershipProvider = new MockMembershipProvider();
   RoleProvider roleProvider = new MockRoleProvider();

   AccountMembershipService membershipService = new AccountMembershipService(membershipProvider, roleProvider);
   HomeController controller = new HomeController(formsAuth, membershipService);
   MockHttpContext mockHttpContext = new MockHttpContext();

   ControllerContext controllerContext = new ControllerContext(mockHttpContext, new RouteData(), controller);
   controller.ControllerContext = controllerContext;
   return controller;
}

However, this problem with a missing context was not my problem, I was already doing this. The error my test runner was showing did not mention the context, rather binding errors.

Test method CollectorWebsite.Tests.Controllers.HomeControllerTest.CardValidation_AlphaInValidAccountID_ErrorMessage threw exception:
System.NullReferenceException: Object reference not set to an instance of an object.
Result StackTrace:   
at CollectorWebsite.Models.CardRecovery.get_Item(String columnName) 
   at System.Web.Mvc.DataErrorInfoModelValidatorProvider.DataErrorInfoPropertyModelValidator.Validate(Object container)
   at System.Web.Mvc.ModelValidator.CompositeModelValidator.<Validate>d__5.MoveNext()
   at System.Web.Mvc.DefaultModelBinder.OnModelUpdated(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at System.Web.Mvc.DefaultModelBinder.BindComplexElementalModel(ControllerContext controllerContext, ModelBindingContext bindingContext, Object model)
   at System.Web.Mvc.DefaultModelBinder.BindComplexModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at System.Web.Mvc.DefaultModelBinder.BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
   at System.Web.Mvc.Controller.TryUpdateModel[TModel](TModel model, String prefix, String[] includeProperties, String[] excludeProperties, IValueProvider valueProvider)
   at System.Web.Mvc.Controller.UpdateModel[TModel](TModel model, String prefix, String[] includeProperties, String[] excludeProperties, IValueProvider valueProvider)
   at System.Web.Mvc.Controller.UpdateModel[TModel](TModel model, IValueProvider valueProvider)
   at CollectorWebsite.Controllers.HomeController.CardRecovery(FormCollection form)

I got stuck here for a good while………

Then it occurred to me if the behaviour has changed such that on the web site I see nulls when I expect empty strings, I bet the same is happening in unit tests. It is trying to iterate though what was a collection of strings and is now at best a collection of nulls or just an empty collection. The bind failed as it could not match the form to the data.

The fix was to make sure in my unit tests I passed in a FormCollection that had all the expected fields (with suitable empty values e.g string.empty). This meant my unit tests changed from

[TestMethod, Isolated]
public void ApplicationValidation_CurrentPostcodeNumbersOnly_ErrorMessage()
{

           // Arrange
           HomeController controller = GetHomeController();
      
    FormCollection form = new FormCollection();
           form.Add("CurrentPostcode", "12345");

           // Act
           ViewResult result = controller.Application(form) as ViewResult;

           // Assert
           Assert.IsNotNull(result);
           Assert.AreEqual("Please provide a valid postcode", result.ViewData.ModelState["CurrentPostcode"].Errors[0].ErrorMessage);

       }

To

[TestMethod, Isolated]
public void ApplicationValidation_CurrentPostcodeNumbersOnly_ErrorMessage()
{

           // Arrange
           HomeController controller = GetHomeController();
          
FormCollection form = GetEmptyApplicationFormCollection();
           form.Set("CurrentPostcode", "12345");

           // Act
           ViewResult result = controller.Application(form) as ViewResult;

           // Assert
           Assert.IsNotNull(result);
           Assert.AreEqual("Please provide a valid postcode", result.ViewData.ModelState["CurrentPostcode"].Errors[0].ErrorMessage);

       }

where the GetEmptyApplicationFormCollection() helper method just creates a FormCollection with all the forms fields.

Once this was done my unit test passed.

Summary

So I now have an MVC3 application that works and passes unit tests. You could argue I should do more work so it does not need these special fixes, but it meets my needs for now.

Update on my experiences with Lenovo W520 Drivers and Windows 8

After I installed Windows 8 RTM I still had two devices missing drivers. I have made a little progress

  • Base System Device - was the Ricoh PCIe SDXC/MMC Host controller I used this Win 8 beta driver
  • Unknown driver – seems to be the Lenovo Power Management devices. However the  Win 8 Beta driver  fails to install. I had to use the Windows 7 driver, installed OK and seems to show the right information in the tool tray, but in Device Manager it still says the unknown driver.

I guess I really need to wait until Lenovo ship their release drivers

Its events time again

if you missed yesterdays DDD10, as I did, then don’t worry. You can come to DDD North in Bradford at the University Management Centre on the 13th of October. There is another great set of speakers, I was lucky enough to get my session on Unit Testing and Fakes in VS2012 accepted. Hope to see you there

Also my company Black Marble as announced our free autumn  and winter events, check them out on our events page. There is a good varied selection this year, something for everyone.