Pages

Tuesday, October 20, 2009

What is the true value of experience?

I recently had an encounter with a fellow software developer that quickly escalated into a philosophical discussion where neither of us was right or wrong yet I could sense both of us digging our heels in preparing to obliterate the other with our hulk-like rage.  From his perspective, I was questioning, second-guessing and disagreeing with his philosophy - which coincidentally has been adopted as the company's philosophy since his assention into the "architect" position.  For me, I felt like my perspective was being dismissed out-of-hand.  Hell, I'd never even been asked before the company adopted these (flawed, imo) philosophies despite having spent years honing my beliefs while holding that same position in other companies.

That same night I had a long drive during which to assess if my feelings were simple jealousy over not getting the position or rooted elsewhere.  After a quick debate, I came to the conclusion that I most certainly did resent not having the position and having philosophies so contrary to my own dictated to me.  My thoughts wandered until they finally settled on one question:

How could I not be the better choice for the position?


Let's say you are hiring a new developer for your team.  One candidate is right out of college with a Computer Engineering degree, has had all of the standard courses and kicks butt in Halo.  The other has been a professional developer for 20+ years working for a few large names in your industry but has very little practical experience with the tools and languages you are using.  Do you dismiss the second for lack of experience with the toolset?  Or maybe you eliminate the first because he doesn't have any real-world experience?

What if you add a third candidate?  This one also has 20+ years of experience but has bounced around a bit with some years developing manufacturing applications, some with ISVs, others as a consultant working on anything the customer will pay for and some doing government contract work.  Any preferences now?


I challenge you that any real assessment is not possible without some additional context.  For instance, are we hiring a UI developer or an Architect?  Candidates 2 and 3 would (should) never consider a junior position and would be disgruntled quickly if they did.  This wouldn't be good for the cohesiveness and morale of the team and you'd most likely be looking to fill the same position again relatively soon.  The first candidate, on the other hand, could no more direct the efforts of your development team than my dog.

So, if we were looking for an Architect, Manager or Senior Developer, how do we distinguish the last two candidates?

By understanding the true value of experience.

Simply putting in the years isn't enough.  It's what you do with that time and what you are exposed to that counts.

The second candidate in our little example has put in the time but spent that time working for a handful of companies thereby limiting his exposure to what those companies allowed him to see and do.  Their tools, their techniques, their processes, etc.  He may be very good at those things, but his experiences have been limited and, as a result, so is his vision.

When first glancing at the third candidate's resume, you might wonder why he/she didn't stay in one place for very long and lean towards the second candidate because they demonstrated longevity.  I will offer to you that longevity can be perceived both ways as well.  Instead of representing dedication and loyalty, perhaps it shows that the developer didn't have the confidence or courage to test the market.  Changing jobs is hard, we all know that.  Unless the third candidate was forced to change, maybe we should see this as a positive trait - something that reflects a strong character, someone who welcomes a challenge and doesn't settle for what's convenient or status quo.

Also consider that with such a background comes diversity and exposure to many different approaches, etc. - all of the things that were limited with candidate 2.  Because the last candidate has worked in different industries, he/she has been exposed to different pespectives.  Working as a consultant means that he/she has worked closely with customers which, as well all know, means they've dealt with change.  And, if the customer-base was industry-agnostic, their exposure is even greater.

So, what is the true value of experience?  Great.  But you have to actually accumulate true experience to achieve this value.  As I said earlier, simply putting in the time doesn't get you there.


Btw, can you guess which candidate I am in my little story? ;-)

Tuesday, October 6, 2009

Using Moq with Spring.NET

Not long into my evaluation of Moq as a mocking framework for our unit testing needs, I had to test a class that used the Spring.NET framework to instantiate a dependant object. Per the rigid definition of a unit test, I wanted to eliminate Spring.NET from the equation. My unit test shouldn't fail because I didn't configure Spring.NET correctly. Besides that, I needed to test the class under different circumstances and couldn't do that without making changes to the configuration file - another TDD no-no.

This part was easy enough by refactoring the method that uses Spring.NET so that I could override it in my test code. I believe I saw this technique referred to as "Inherit and Override". To do this, I simply subclassed the class under test to create a "testable" version with an override for the method that called Spring.NET for the dependant object. In my "testable" class, I return a mock object instead. Nice and simple!

Where things became a bit more complicated is when I wanted to put together a system test for the same functionality. In this case, I do want to use Spring.NET to instantiate the dependant object but still want to mock the object used for testing purposes. Short version is that it handles file I/O which I never want to execute in my tests.

Fortunately, the Moq framework comes with a MockFactory class that can be used in the Spring.NET configuration file to create the mock object we desire for the test. Here is how I setup the configuration file for my test assembly:

<objects xmlns="http://www.springframework.net">
    <object name="MockFactory" type="Moq.MockFactory, Moq">
        <constructor-arg type="Moq.MockBehavior" value="Default" />
    </object>

    <object name="MyObject"
               factory-object="MockFactory"
               factory-method="Create<IMyObject>" />
    </objects>


Now, when running my test, the class calls into the Spring.NET framework for the object which uses the configuration file to call the Create method on the MockFactory class, returning a mock object instead of the concrete class in our application.

There is one catch, however. The object returned from the MockFactory.Create method is of the type Mock<T>. Unless you code your class to accept this type, which would be poor practice, you'll get an InvalidCastException. To work around this, I created my own MockFactory class that delegates to the Moq class but unwraps the returned object so that mocked instance is returned as follows:

internal class MockFactory
{
    private static Moq.MockFactory _moqFactory;

    static MockFactory()
    {
        _moqFactory = new Moq.MockFactory(Moq.MockBehavior.Default);
    }

    public T Create()
        where T : class
    {
        return _moqFactory.Create().Object;
    }
}


I then changed the configuration file to use this class.

<objects xmlns="http://www.springframework.net">
    <object name="MockFactory"
               type="Testing.MockFactory, Testing" />

    <object name="MyObject"
               factory-object="MockFactory"
               factory-method="Create<IMyObject>"/>
</objects>


In the end, getting Moq to play will Spring.NET turned out to be relatively easy. Sure, I had some setup work to do, but now that I've done it once...

Thursday, September 24, 2009

My First Foray Into Unit Testing with Moq

I’m sure we’ve all read and can agree to the value of having code-based testing as a standard part of our development process. By including unit tests, etc. we can automate the testing process and obtain a higher degree of confidence in our code before we release it for formal QA testing. By diligently creating tests when defects are found, we get regression protected in the hopes that we can catch problems before they are released.


This is, of course, easier said than done in most cases. Even after nearly 30 years as a developer, I am just as guilty as the next person when it comes to developing real tests that provide not only code coverage but true substance.


I know, for instance, that I should be testing each possible path through my code to make sure that each behaves properly. But that’s just plain hard! Or, I guess, to be more accurate – that takes way too much time!


Unfortunately, as I’ve moved from developer to lead to manager and expanded my portfolio, experience and knowledge along the way, I have to admit that’s not a good enough reason not to do it. So, I find myself in a forced sense of discipline on my new projects to make sure that every type has a matching set of unit tests and that each “sub-system” in my solution has integration/system tests and so on. My goal is to achieve that confidence in my code with one click of a button.


I have a few… conditions, shall we say, on how my solution is structured that cannot be sacrificed for the sake of testability. These are:
  • Separating all tests in satellite assemblies and not embed them within the same code projects (assemblies). My reasoning is simple and common: I don’t want to deploy test assemblies with my production application. And, even though we can use compiler directives, etc. so that the actual test classes aren’t compiled into the release version of the assembly, those references still exist and all dependencies will be included in the final product. Not good.
  • We have to be able to run our tests as part of an automated build process with or without true Continuous Integration. Doing this means that we have a high level of assurance that our tests are actually being run. You’d be surprised how often I’ve seen companies that spend the time and effort to develop unit tests then leave then to a developer’s discretion, discipline and memory to run them. Nope, this needs to be a standard part of the build process.
  • Scope all types defensively to protect our Intellectual Property (IP). This means that everything starts out internal (Friend in Visual Basic). Only when we deliberately expose a type as part of a public API will the scope be changed to public. To support this, we sometimes have to use the InternalsVisibleToAttribute so that these internal types are accessible from other assemblies in our solution. This is how we expose our internal types to our testing assemblies.
There are those that will jump in and point out that simply scoping a type internal doesn’t protect our IP – and they are right. Tools like Reflector have no problem opening up even your internal types for public viewing. However, if you add obfuscation into the mix, you do get to lock away your proprietary code. Obfuscation tools, like the Dotfuscator utility provided with Visual Studio, are much more affective when they know they can safely obfuscate a given type. Sure, there are rules built-in that allow the tool to determine if a type can be obfuscated, but scoping something internal tells the tool it is safe. And even Reflector can’t get around the results.


One thing to note is that obfuscation will not work even when a type if scoped internally if that type is being used in another assembly. This, by its very nature, means that the type is actually public in practice. So, we only include obfuscation as a step when building a release version of the product.


So what does this have to do with Moq as indicated in the post’s title? Well, it turns out that there are a few extra steps and a couple “gotcha’s” we have to know about and keep in mind when trying to use a mocking framework, such as Moq, in our unit tests.


Because we want our tests to have access to our internal types, we have to include an InternalsVisibleTo attribute in the source assembly (the assembly containing our internal types under test) granting access to the test assembly. That seems pretty obvious. But, the gotcha here is that Moq, and other similar mocking frameworks like NMock and RhinoMocks, use the Castle Project's DynamicProxy library to generate a temporary assembly that contain proxies for your types created on-the-fly at runtime. Unfortunately, this means that the dynamic, temporary assembly ALSO needs to be able to reference your internal types. So, we have to include another InternalsVisibleTo attribute in our source assembly as follows:


[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]

But there is still another gotcha. The above statement is perfectly valid if, and only if, your source assembly is not strongly-named. If it is, then you will need to include the PublicKey parameter in the statement. In fact, your project won’t even compile without it. So, if you are strong-naming your source assembly, use this statement instead:


[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

(No line breaks!)


Final gotcha – You can’t use the PublicKey if your source assembly is not strong-named. It would certainly make life easier if you could simply use the second version of the attribute all the time but alas, that’s not the case. So be aware and use the right syntax to fit your situation or you might end up with an error message like this:


DynamicProxyGenAssembly2, Version=0.0.0.0, Culture=neutral, PublicKeyToken=a621a9e7e5c32e69' is attempting to implement an inaccessible interface.

The good news is once the setup and configuration is complete, working with Moq has been a snap! I’ll add future posts about my experience and how well Moq works out. Stay tuned…