Monday 17 October 2011

The Lazy Unit Tester

My take on unit tests? Essential!

Never mind that SalesForce.com forces 75% code coverage on any code written. Even if this mechanism wasn't there, I would still insist on them being present on any solution implemented by myself or my team.

A unit test is, at its most basic level, code that tests code! It's not a replacement for smoke, functional or system testing; it's a mechanism which helps improve the quality of code written by developers.

If you’re building a car, it’s the sum of all its parts. The parts are manufactured individually, then put together and at the end of it you have a working car! Quite a simple process, like a jigsaw puzzle, but a lot can go wrong in this process.

Let's take the wheels on the car. For the car to run you need four wheels, each with tyres. They all need to be the correct size, shape and inflated to the correct pressure to be useful. If one of those wheels is square, the wrong size or is flat then this will affect the operation of the car and make it less than desirable to drive or own.

When buying a new car, you hope the manufacturer tests the individual components (in this example the wheels), as well as the overall function of the car.

Now, onto the geekery, let's take this code example

public class Wheel {
    public static String defaultBrandName = 'Default Tyres Inc';
    public static int defaultSize = 14;
    public static double defaultPressure = 33;

    public String brandName;
    public int size;
    public double pressure;
 

    public Wheel() {
        this.brandName = defaultBrandName;
        this.size = defaultSize; 
        this.pressure = defaultPressure;
    }

    public Wheel(String brandName, int size, double pressure) {
       this.brandName = brandName;
       this.size = size;
       this.pressure = pressure; 
    } 

    // Some Accessors and Mutators for the instance variables
}

public class WheelFactory {
    public static Wheel getWheel() {
        return new Wheel();
    }

    public static Wheel getCustomWheel(String brandName, int size, double pressure) {
        return new Wheel(brandName, size, pressure);
    } 

}
It's quite clear how this now works. We go to the wheel factory and either ask for a default tyre or we specify what kind of tyre we want, as the meerkat says 'Simples'. but what would be the best way to test this to enable me to deploy it to a SDFC production org? Not much I hear you say, but here is an example of the type of thing I have seen.
@isTest
public Class WheelFactoryTest {

    public static testMethod void myUnitTest() {
        Wheel w = WheelFactory.getWheel();
        Wheel w1 = WheelFactory.getWheel('Pirelli', 20, 33);
    }
}
And there is it, as far as Force.com is concerned, a perfectly valid unit test! It makes the 75% test coverage requirement and took about 30 seconds to write. Job done! Let me ask you this though, what is my unit 'TEST' actually testing? The simple answer is that it's testing that the WheelFactory and Wheel classes compile and can be called. It's quite frankly a waste of 30 seconds and this test is useless! Is a Wheel being returned? or is it returning a null? What brand is it? What size? What pressure? We just don't know from this test! Below is what I would consider a better test of this:
@isTest
public Class WheelFactoryTest {

    public static testMethod void wheelFactoryGetWheelNoParamsTest() {
        Wheel w = WheelFactory.getWheel();
        System.assert(w != null);
        System.assert(w.getBrandName() == Wheel.defaultBrandName);
        System.assert(w.getSize() == Wheel.defaultSize);
        System.assert(w.getPressure() == Wheel.defaultPressure);
    }

    public static testMethod void wheelFactoryGetWheelWithParamsTest() {
        String brand = "My Brand";
        int size = 17;
        double pressure = 35;
        Wheel w = WheelFactory.getCustomWheel(brand, size, pressure);
        System.assert(w.getBrandName() == brand);
        System.assert(w.getSize() == size);
        System.assert(w.getPressure() == pressure);
        
}
Hopefully you can see the improvements here?
  1. We are testing each method in a clearly separate way so if one method fails we know instantly which one is broken
  2. We are using asserts to guarantee that we are getting the correct results! When you see a unit test with no asserts you need to change that! 95% of the time there is no excuse for a unit test with 0 assertions this is testing more than just coverage!
  3. We are protected from change! Because we are using the static default variables from the WheelFactory class in our tests, if those defaults change our unit test does not need modifying, this is basic coding 101, don't use magic numbers!
  4. I am actually testing that the tyres are the correct size, pressure and brand! Wouldn't you do that in real life if you were buying tyres for your own car?
Hopefully people will find this useful and when they get given a unit test to write will do the right thing and do it properly and understand the importance of a well constructed unit test!

Friday 7 October 2011

Who Needs a new Server

My current project has me again working on my platform of choice Force.com solving business problems for a massive multi-national enterprise company. The problem the client has is that they have developed an excellent suite of 'sweet' sales tools to assist their sales people out in the field. The technologies are many and various and all have one slight weakness from a business perspective:

The Tools that they use all capture client information and details of sales orders and proposals. Again, as a toolkit given to their Sales representatives it's an excellent package, videos demonstrating their products and simple forms to allow them to capture all the information to both identify pain points for the prospective customer and also identify appropriate solutions that they can offer to solve them, this is where the problems begin for the business.

The tools all reside locally on the Sales representatives own laptop and there is currently no way for the business to extract this information and get critical data out to view in a federated way. At first impression the long rendundant IBM consultant within me started thinking of the servers that would be needed to support this, a big expensive middleware infrastructure, an appliance such as DataPower, or software (MQ, and ESB) deployed to a 6 figure cost server that would need to be housed in an even more expensive data centre. This "essential" piece of middleware would then connect to a big central database (DB2, Oracle etc...) and then we can look at deploying Business Objects or Crystal Reports to allow management to get the MIS they require out of the system. Ouch, a big expensive implementation with 5+ consultants and a lead time as much as 6 months before you even start to think about the OpEx of maintaing such a mammoth implementation!!!
Luckily for me and the customer, the year 2011 came knocking and told me that I was living in the past and that those old fashioned architectures are just expensive to deploy in a resilient fashion, unreliable unless you deploy in a resilient fashion and require specialist infrastructure and application teams just to keep the lights on so a more modern, agile and rapid solution was required.

I then lifted the lid on SalesForce after a few hours it became quite obvious that the data they were capturing was essentially Accounts, Contacts, Opportunities and some ancilliary data to support each of those objects! So we had it, a clean mapping between the core SalesForce.com CRM offering and the toolkits developed, now all we needed was a mechanism to get the data from the remote silos into the SalesForce.com org and start reporting on it. The answer was so simple and that was to make use of the excellent WebServices API that you get from the Force.com platform. After an initial workshop, an interface with core services was defined, the applications, which already have the functionality to operate in an off-line capacity were compatible with the delivery mechanism, http/https, which was considered rightly, to be a reliable and resilient mechansim and therefore removed the need for some IBM or BEA/Oracle bloatware (middleware to the layman) and away we all went.

Now to repeat the problem, business critical data was residing in unreachable, remote silos and needed bringing together to provide senior business leaded with mission critical data and reports to allow for forecast and pipeline analysis. How long do you think it would have take? I couldn't even begin to estimate the cost, lapse time for going the 'old' way all the unknowns, server procurement, highly paid product specific consultants, licensing costs, server costs, data centre costs, security considerations, the list goes on and on and on, as does the delivery date and the zeros on the end of the purchase order the customer would need to raise. Luckily for them, the modern Cloud / SalesForce.com route was chosen, and here we are, 12 days of consultant effort later, ready to plug the solution in. The platform is in place, it's resilient and secure, it doesn't cost us to maintain and only one person was needed for all the configuration and coding of the SFDC solution.

If you are reading this, have a think about your own company! How many servers do you have? How many people do you employ exclusively to keep the lights on? How much does one server cost you as a company? How much does it cost doing failover and DR simulations?

Just keep in mind that SalesForce frees you from:

  1. Data Centre Considerations
    1. Rack Space
    2. Power Consumption
    3. Air Conditioning
    4. Networks
    5. Firewalls
    6. Bandwidth
  2. Server Considerations
    1. Security Patches
    2. OS upgrades
  3. Hardware Considerations
    1. Maintenance Contracts
    2. Component failure
    3. Redundant infrastructure (Warm, Hot, Cold Stand-bys)
  4. Skills Considerations
    1. System Administrators
    2. Networks and Firewalls teams
    3. Security specialists
All of this is included in your SaaS licensing model, one user, one license and all of the above comes for free!

With this in mind, when you consider your next implementation, think about the costs, the lead times, the procurement processes, the on-going maintenance cost, the legion of expensive consultants you will need and then think again, why should I do/pay for all of this when the "True Cloud" frees your hands and give you all of this in the price?