Wednesday, December 29, 2010

Twitter.com/JimHolmes == “Sorry, that page doesn’t exist!”

In case you’re looking for me on Twitter, I’m not.

Twitter turned in to a massive time sink for me (and I didn’t have time to sink in the first place), and I was letting myself vent way too many frustrations there. It was time to take my own advice: “Enough griping, enough bitching already. Roll up your sleeves already and get to work.”

‘Nuff said.

WinDirStat–Nice Disk Space Usage Visualizer

Hard disks are cheap and greatly ease the past pain of having to manage free space, but that’s not the case for me: I have a small-ish Crucial C300 128GB Solid State Drive as my C: volume in my work laptop. It’s been absolutely awesome for working with the various VMWare virtual servers I deal with on a daily basis. The speed on the SSD goes to 11.

The only downside is I have to be very careful about free space on the SSD. SSDs need breathing room to deal with various low-level things (go read Wikipedia’s article on them for a great overview), so I try to keep things fairly tidy and clean up unneeded files regularly.

One of the best tools I’ve found to help me with this is WinDirStat, a free GPL tool. No installation, just download and throw in a util folder somewhere. Launch it via SlickRun to be really cool.

You get an easy to understand UI with clear indicators where your space pain points are.

Besides, I’m a tool whore and this is yet another great thing for me to play with.

Wednesday, December 22, 2010

HTTP 500 Errors With Invalid Request Format After Upgrading Site to .NET 4.0

Problem: After upgrading an ASP.NET site to 4.0 you get HTTP 500 errors with exceptions when Javascript is making web service calls: “System.InvalidOperationException: Request format is invalid: application/json; charset=utf-8.” You’ve used IIS Manager to change the application pool to specify .NET 4.0 but the problem persists.

Solution: Change to the 4.0 framework folder (C:\Windows\Microsoft.NET\Framework64\v4.0.30319 in my case) and run aspnet_regiis.exe. You’ll need to determine which option you want – check the help before running and determine what you need.

For whatever reason, simply changing the application pool’s version value in IIS Manager didn’t work. Using the big hammer of aspnet_regiis.exe fixed things up.

Duplicated Setup Code in Your Tests? Refactor to a Factory!

Your test code is production code. Treat it the same way. Don’t let cruft build up, and follow good software engineering principles – like Don’t Repeat Yourself (DRY).

I was looking through some test code the other day and found a great opportunity to refactor out some common behavior to a factory. I do this quite often in the test frameworks I build – shove off responsibility for data and object creation to a factory so I can keep things much clearer in my tests.

In this case, we’re testing a parser class that creates reports based on an XML file. A number of tests check the parser’s handling of malformed XML or missing elements. The basic pattern below is repeated across eight or nine tests: hardwire an XML string in the test, feed that to the parser, then check if appropriate errors and messages are generated by the parser.

   1: var reportXml =
   2:     @"<report version='4.0'>
   3:             <key>FiscalYearByBrowser</key>
   4:             <description>Shows cool stuff.</description>
   5:             <connectionString>TelligentAnalyticsAnalysisServer</connectionString>
   6:             <query>
   7:                select some SQL-fu from a magic place and return unicorns
   8:             </query>
   9:       </report>";
  10:  
  11: _report = ReportParser.Parse(reportXml);

This is repetitious, duplicated, copied cruft. Small joke there. Moreover, it’s not readily apparent what the XML snippet’s intent is. I have to read the entire XML, then mentally unwind what I know of the structure to figure out that, oh, this snippet is actually missing the title element.

Compare the above with this section of refactored code:

   1: _report = ReportParser.Parse(TestReportFactory.GetWithoutTitle());

The implementation of the TestReportFactory isn’t overly important [1]; what’s important here is that duplicated behavior should be pushed out to a common area, regardless of whether it’s in the system under test or your test code itself.

Your tests are production code. Treat them as such!

(NOTE: Some folks, particularly the Really Smart Guy Jeremy D. Miller, make the absolutely solid case that you should never sacrifice readability in your tests for the sake of cutting duplication. It’s good to push duplicated behavior out if you can keep your tests completely clear. It’s not ok to push behavior up to base classes or elsewhere if your test turns into a vague skeleton. Readability trumps everything else.)

[1] The TestReportFactory simply builds up a string of XML, creates an XElement from that, then uses XElement.remove() or XAttribute.remove() to drop elements or attributes as needed. The entire class is pasted below. Yes, there are a number of other ways to skin this cat. I could make things a bit more dynamic, but this worked for what I needed right now. And yes, I drove design and implementation of this factory with a separate set of tests I wrote first…

   1: internal class TestReportFactory
   2: {
   3:     private static string _reportOpen = @"<report version='4.0'>";
   4:     private static string _key = @"<key>FiscalYearByBrowser</key>";
   5:     private static string _title = @"<title>Fiscal Year by Browser</title>";
   6:  
   7:     private static string _description =
   8:         @"<description>Shows page views per browser (IE, FireFox, etc..)
   9:          for each fiscal year.</description>";
  10:  
  11:     private static string _connectionString =
  12:         @"<connectionString>TelligentAnalyticsAnalysisServer</connectionString>";
  13:  
  14:     private static string _query =
  15:         @"<query>
  16:             select non empty [Time].[Fiscal Year].Members on columns,
  17:             non empty [Dim Browser].[Browser Name].Members on rows
  18:             from [Evolution Reporting]
  19:          </query>";
  20:  
  21:     private static string _chartTypes =
  22:         @"<chartTypes>
  23:                                 <add type=""fancy 3d chart"" />
  24:                                 <add type=""plain 2d chart"" />
  25:                               </chartTypes>";
  26:  
  27:     private static string _reportClose = @"</report>";
  28:  
  29:  
  30:     public static string GetValidXml()
  31:     {
  32:         XElement doc = CreateValidReportXElement();
  33:         return doc.ToString();
  34:     }
  35:  
  36:     private static XElement CreateValidReportXElement()
  37:     {
  38:         var _outgoing = new StringBuilder();
  39:         _outgoing.Append(_reportOpen);
  40:         _outgoing.Append(_key);
  41:         _outgoing.Append(_title);
  42:         _outgoing.Append(_description);
  43:         _outgoing.Append(_connectionString);
  44:         _outgoing.Append(_query);
  45:         _outgoing.Append(_chartTypes);
  46:         _outgoing.Append(_reportClose);
  47:         XElement doc = XElement.Parse(_outgoing.ToString());
  48:         return doc;
  49:     }
  50:  
  51:  
  52:     internal static string GetWithoutQuery()
  53:     {
  54:         return CreateXmlStringWithoutElement("query");
  55:     }
  56:  
  57:     private static string CreateXmlStringWithoutElement(string elementName)
  58:     {
  59:         XElement doc = CreateValidReportXElement();
  60:         XElement element = doc.Element(elementName);
  61:         element.Remove();
  62:         return doc.ToString();
  63:     }
  64:  
  65:  
  66:     internal static string GetWithoutKey()
  67:     {
  68:         return CreateXmlStringWithoutElement("key");
  69:     }
  70:  
  71:     internal static string GetWithoutDescription()
  72:     {
  73:         return CreateXmlStringWithoutElement("description");
  74:     }
  75:  
  76:     internal static string GetWithoutConnectionString()
  77:     {
  78:         return CreateXmlStringWithoutElement("connectionString");
  79:     }
  80:  
  81:     internal static string GetWithoutChartTypes()
  82:     {
  83:         return CreateXmlStringWithoutElement("chartTypes");
  84:     }
  85:  
  86:     internal static string GetWithoutTitle()
  87:     {
  88:         return CreateXmlStringWithoutElement("title");
  89:     }
  90:  
  91:     internal static string GetWithoutVersion()
  92:     {
  93:         XElement doc = CreateValidReportXElement();
  94:         XAttribute ver = doc.Attribute("version");
  95:         ver.Remove();
  96:         return doc.ToString();
  97:     }
  98:  
  99:     internal static string GetInvalidXml()
 100:     {
 101:         return "<report>";
 102:     }
 103: }
 104:  

Wednesday, December 15, 2010

I’m Looking for A Great QA Team Member!

Want to be part of a team that’s deeply involved in fundamental changes to how a company builds its software? Don’t mind hollering “Sancho! My armor!” because with some regularity you defeat those windmills?[1]  Passionate about learning and continually improving how you do work? Care more about enabling the team’s success than ticking boxes on your own resume? Think that lots of collaboration and communication with developers and stakeholders is more important than pages of requirements documents and specifications?

Got your interest yet? If so, open up a conversation with me about joining my QA team at Telligent. We’re going to be opening up a new position in short order, and I’m looking for someone who can help us continue driving some amazing, fundamental transformations[2] at the company.

Check out my post from last year describing the job and a bit about the environment. Most everything there still applies.

While the general skillset and duties in that other post are still applicable, there are a few tweaks to responsibilities and skills for this new position. Our new team member will be responsible for

  • Writing automated acceptance and integration tests
  • Helping drive our Section 508 and WCAG 2 accessibility efforts
  • Assisting ongoing efforts to improve our overall development processes

If you read my blog or follow me on Twitter you know I’m much more interested in what you’ve got in your head and heart than what you’ve got on your resume. That means I’m not overly concerned if you’re not an expert in C#, Selenium, or even .NET. I am concerned that you’re driven to learn new things. I am concerned that you’re passionate about driving cultural change. I am concerned that you think it’s better to talk frequently with folks at the start of the development lifecycle rather than wait until a dev-complete feature lands in your lap near the end of the lifecycle. (Dear fans of Six Sigma, Rational ClearQuest, and Waterfall: you likely will not thrive working with me.)

Interested? Drop me a line! Use the contact link on this blog’s sidebar, or mail me directly: JHolmes AT Telligent DOT com.

[1] Don’t mind working for a boss who (likely far too often) throws out weird references to books, movies, and oddball stories?

[2]“Transformations” is lousy, over-used blabberspeak marketing crap. But you know what? The entire group my team fits in is making amazing changes to how we create our software. In this case it fits.

Subscribe (RSS)

The Leadership Journey