Click here to Skip to main content
15,890,186 members
Articles / Programming Languages / C#
Tip/Trick

Testing and Branching via Implementations of Core Functionality Interfaces

Rate me:
Please Sign up or sign in to vote.
4.73/5 (8 votes)
19 Jan 2015CPOL5 min read 22.9K   9   3
Confirm what really needs to be built using mockup implementations before investing time in the real implementations (including "Poor Man's Branching" as a side benefit)

When Software Meets Wetware, Things Can Get Squishy

Arguably (no pun intended), the biggest challenge in software development is communication; not just communication between machines and methods and such, but communication with and among the wetware (human "beans") for whom the software is being created: the stakeholders (the client, the end users, management, and, of course, the stars of the show, the coders).

We developers often misunderstand, for various reasons, what we are supposed to be building. For that reason, we first try this and then, when it is found to be not what the client and/or management had in mind, we try that (or maybe the first iteration was what they wanted, but they have since changed their mind). This can recur ad nauseum, ad infinitum, seemingly in an infinite loop, until the code base looks like a rat's nest, and it's hard to tell at a glance which code is dead, which is alive, and which is just not feeling all that chipper.

It may be better to start off with a "mockup" of the app's functionality, but one that is embedded within the primary project itself, not a separate test project within the solution; one that can be "turned on and off" with the flick of a switch, so to speak, once everyone agrees on what the app should really do, be, and look like.

One way to accomplish this is by creating interfaces that represent the major categories of functionality of the app (that much should be known at least before beginning the project). Then, you can create 2..N implementations of that interface - the "real" (production) implementation (left unimplemented at first) and the test implementation.

Which implementation is in use (real or test) can be set globally.

Show Me the Code[s]!

For example, you may have an interface like this:

C#
public interface IFetcher
{
    ArrayList FetchSlippers(string voiceTone);
    ArrayList FetchThePaper(string voiceTone);
    ArrayList FetchTheBall(string voiceTone);
    ArrayList FetchThatPeskyVarmint(string voiceTone);
}

You can implement that interface in a production class (but leave the methods unimplemented for now):

C#
public class ProductionFetcher : IFetcher
{

    public ArrayList FetchSlippers(string voiceTone)
    {
        throw new NotImplementedException();
    }
	
    . . .

...and in a test class (this assumes you have a class "Slipper" with string members "Material" and "Color"):

C#
public class TestFetcher : IFetcher
{
        
    public ArrayList FetchSlippers(string voiceTone)
    {
        // TODO: do something with/respond to voiceTone arg
        Slipper slip = new Slipper();
        slip.Material = "suede";
        slip.Color = "blue";
        . . . // other members
        List<string> slips = new List<string>();
        slips.Add(slip.Material);
        slips.Add(slip.Color);
        . . .
        // slip in another slipper
        slip.Material = "lead";
        slip.Color = "burnt sienna and hospital green polka dots";
        slips.Add(slip.Material);
        slips.Add(slip.Color);
        ArrayList slipList = new ArrayList();
        slipList.AddRange(slips);
        return slipList;
    }

Set it up so that you can toggle your app to use any of the implementations like so:

Create an enum that holds the implementations, perhaps in a file named "MyConsts":

C#
public enum FetchTypes
{
    Production,
    // any other possibilities ,
    Test 
} 

In the same MyConsts.cs file, put code like this to set the implementation being used:

C#
private const FetchTypes implementation = FetchTypes.Test;

Add a method like this:

C#
public static IFetcher GetFetcherImplementation()
{
    IFetcher impl;
    switch (implementation)
    {
        case FetchTypes.Production:
            impl = ProductionFetcher.Instance;
            break;
        case FetchTypes.Test:
            impl = TestFetcher.Instance;
            break;
        default:
            impl = TestFetcher.Instance;
            break;
    }
    return impl;
}

This assumes that your concreate classes implement the singleton pattern, and their "Instance" method returns an instance of the class. Otherwise, you can use "new" and omit the ".Instance" bit.

Now you need to add code to the forms that will call the implementations of the interface. First, this:

C#
public partial class FrmDelivery : Form
{
        private IFetcher fetcher { get; set; }
        . . .

...and then, where the methods are called:

C#
fetcher = MyConsts.GetFetcherImplementation();

That will return whichever implementation of the IFetcher interface you assigned to the "implementation" variable. Now, you can call any of the methods:

C#
ArrayList arrlst = fetcher.FetchSlippers("suave");

Note: There's a beature, or a fug (combination bug and feature) in how this switch code compiles. Since the compiler knows which implementation you have globally selected, it grays out the case statements that are currently moot. IOW, if you have set the test instance of the implementation like so:

C#
private const DBTypes implementation = DBTypes.Test;

...the switch code only "lights up" that particular case:

Image 1

This makes it obvious which implementation is live (a feature!), but asphyxiates, or at least anesthetizes, all but one of the case candidates (a bug, of sorts).

If the "buggy" portion of this beature irks you too much, you can refactor the code to circumvent the grayification of the switch statement. Me, I don't mind making the compiler pull its weight and work another one one trillionth of a second to gray up the currently impossible cases. YOMV (Your Obsessiveness May Vary).

Another Usage: Poor Man's Branching

A related benefit of this approach is that you can effectively have multiple "branches" of your code base without having to literally create branches in some source control. IOW, it's "poor man's branching."

For example, you can use your test implementation to explore alternate approaches; if these work, you can update the production code to reflect these changes. This keeps the code cleaner as you're investigating various possibilities (rather than commenting out existing code, adding comments to explain what you're trying, etc., you simply keep the tried-and-true-blue code in production (for now, anyway) and the I've-got-an-idea-that-might-work code in the test implementation.

So, using the test implementation has more value than simply as a mockup prior to rolling up your sleeves and getting busy with the production code - you can continue to use your test implementation even after diving into production code to try out new ideas before "taking the plunge" and using that code in the production implementation.

Put the Pedal to the Metal (or is it Petal to the Medal?)!

After you provide fake data or functionality for all the Test methods, you can set the app to use that test implementation, and let the stakeholders play around with it. Once it has gone through the requisite 47,000 iterations and changes and flip flops, and everybody has agreed what type of data they want, etc. etc. you can implement the Production version of it in a much smoother fashion, knowing precisely (or as precise as can be expected when various styles and flavors of wetware collide) what should be implemented.

This should be enough to get you started having fun, making money, winning friends, influencing people, and making all your wildest dreams come true in the wild and wonderful world of test implementations.

Where, Oh Where, Can My Platypup Be?

Speaking of soft and wet, where in tarnation is my earnestly yearned-for Choctypus (Chocolate-covered Duckbilled Platypus)?!?

Note: A platypup is a baby platypus - really! No foolin'! Ask any Mammalogist, falls Sie duda mis palabras.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder Across Time & Space
United States United States
I am in the process of morphing from a software developer into a portrayer of Mark Twain. My monologue (or one-man play, entitled "The Adventures of Mark Twain: As Told By Himself" and set in 1896) features Twain giving an overview of his life up till then. The performance includes the relating of interesting experiences and humorous anecdotes from Twain's boyhood and youth, his time as a riverboat pilot, his wild and woolly adventures in the Territory of Nevada and California, and experiences as a writer and world traveler, including recollections of meetings with many of the famous and powerful of the 19th century - royalty, business magnates, fellow authors, as well as intimate glimpses into his home life (his parents, siblings, wife, and children).

Peripatetic and picaresque, I have lived in eight states; specifically, besides my native California (where I was born and where I now again reside) in chronological order: New York, Montana, Alaska, Oklahoma, Wisconsin, Idaho, and Missouri.

I am also a writer of both fiction (for which I use a nom de plume, "Blackbird Crow Raven", as a nod to my Native American heritage - I am "½ Cowboy, ½ Indian") and nonfiction, including a two-volume social and cultural history of the U.S. which covers important events from 1620-2006: http://www.lulu.com/spotlight/blackbirdcraven

Comments and Discussions

 
QuestionChoctypus Pin
Kyle A.B.15-Jan-15 8:26
professionalKyle A.B.15-Jan-15 8:26 
AnswerRe: Choctypus Pin
B. Clay Shannon15-Jan-15 8:41
professionalB. Clay Shannon15-Jan-15 8:41 
Questionnot sure about this Pin
Sacha Barber14-Jan-15 18:47
Sacha Barber14-Jan-15 18:47 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.