Designing for unit tests (by )

I'm writing a unit test for a bit of code that connects to a server. The bit of code has parameters - hostname, username, password, and details of what to fetch from the server.

The thing is, the infrastructure it's placed in expects it to store the password in encrypted form. It has to use a separate component that contains a hidden master key to decrypt the password when it's required, and to encrypt a new password given to it.

Also, since the application will be fetching lots of different things from different servers, it quite reasonably has a 'host' abstraction that can be referred to by lots of data collection modules; it keeps a central list of hosts, and my component is passed a Host object.

But when writing a unit test for my component, this is a pain. The way the central list of hosts and the crypto component are accessed involves going through a 'session' object that also has lots of things like listening sockets, database connections, and much other complexity afoot.

I don't want to have to write pages of code to set up whatever minimal subset of the environment is required just for me to access the crypto module and to create a stub Host object, that need only implement enough of itself to correctly return a hostname.

So, instead, what I've done is to give my component a setTestMode (String hostname) method, which sets flags within itself to use a specified hostname rather than talking to the Host object (which can then be set to 'null'), and also skips the crypto, so the module ends up storing the password in plaintext. I then used this from my unit test, and it works happily.

But it's a kludge. It doesn't check whether I'm correctly calling the crypto module - in real use, my code might corrupt passwords, for example if my cut-and-paste coding has led me to call the encrypt function when I should be decrypting. And I may not correctly handle the Host object, perhaps a typo in my setHost method such as the dreaded:

    void setFoo (SomeClass fooo) {
        this.foo = foo;
    }

...a method that will compile fine, but will do nothing when invoked, due to misspelling foo in one place.

What's the solution? Really, the application itself could have been better designed from a testing perspective - Host should be an interface that can be implemented equally well by the heavyweight host database entry class, full of property change listeners and communications with a backend SQL database of hosts, or by a lightweight testing host class that just returns constant values and has no dependencies. And the crypto module should be passed as a parameter when my class is instantiated, rather than being acessed as Application.THE_APPLICATION.getCrypto() (which is, basically, a global variable reference - Application is a class, THE_APPLICATION is a static field of the class pointing to its singleton instance, getCrypto() is a method of that task. Tsk, tsk.), so I can provide a dummy testing implementation that either uses a hardcoded key, or perhaps more usefully for testing, encrypts *password* to ENCRYPTED:*password*, which can be decrypted by the human eye.

2 Comments

  • By Alex B, Thu 14th Sep 2006 @ 12:04 am

    Don't understand the "dreaded" setFoo() method. The RHS of the assignment is untypeable, since 'foo' is not a local variable or a formal parameter, and the method won't compile.

  • By alaric, Thu 14th Sep 2006 @ 3:52 pm

    If there's a field called 'foo' in the class, then the line:

      this.foo = foo;
    

    ...will do NOTHING unless there's a more local (re-)definition of foo.

    So one might write:

      void setFoo (FooClass foo) {
         this.foo = foo;
      }
    

    ...and it'd set this.foo to the local parameter foo.

    But if you mistype the parameter name:

      void setFoo (FooClass fooo) {
         this.foo = foo;
      }
    

    ...then you're just assigning this.foo to this.foo again and ignoring the fooo param 🙁

Other Links to this Post

RSS feed for comments on this post.

Leave a comment

WordPress Themes

Creative Commons Attribution-NonCommercial-ShareAlike 2.0 UK: England & Wales
Creative Commons Attribution-NonCommercial-ShareAlike 2.0 UK: England & Wales