Showing posts with label TDD. Show all posts
Showing posts with label TDD. Show all posts

Monday, July 11, 2011

Mocking LDAP Servers for JUnit

As I have gone over before, I am a fan of unit testing. The list of benefits is quite extensive. However, one of the chief hurdles of unit testing is "I don't know how to test that...". I ran into just such a problem the other day. I have a project that utilizes an LDAP server for it's back end datastore. While support for databases in unit tests is a fairly flushed out field, support for LDAP servers is not-so-much.

Luckily, though, the good people over at UnboundID have a good solution to help us out. Their LDAP SDK for Java has been a very handy tool I have used for quite a while for handling all of my LDAP connections. Recently, with their 2.1.0 release, they unveiled a new feature specifically for this issue - the in-memory directory server. However, attempting to implement this in-memory server was a little bit more challenging than the documentation would make it seem. There are just a few gotchas to be aware of.

The documentation does contain an example that shows the java code you need to get going on this. The part that is tricky, though, is this line:
server.initializeFromLDIF(true, "/tmp/test.ldif");
There are a couple of problems here. First off, it is, well, wrong. The actual method name is not initializeFromLDIF, but rather importFromLDIF. So it should look like:
server.importFromLDIF(true, "/tmp/test.ldif");
The second, and slightly more significant problem, is that there is no documentation on exactly what needs to go into test.ldif. You are left to figure that one out on your own.

The ldif file that you use to import MUST contain the definition for whatever value you specify as the base DN in the InMemoryDirectoryServerConfig constructor. It may not contain any definitions of the parents of the base DN (which makes sense, being the BASE DN), and it may not specify the definition of further children without specifying the definition of the base DN.

So, to go along with the example, here is a sample LDIF you can use to get you started:
dn: dc=example,dc=com
objectClass: top
objectClass: domain
dc: example
That is the minimum required definition of the base DN. Note that the following LDIF does NOT work:
dn: dc=com
objectClass: top
objectClass: domain
dc: com

dn: dc=example,dc=com
objectClass: top
objectClass: domain
dc: example

If you want to specify any further branches, that may be done in the same LDIF file, so long as you still include the top portion as above. For example, if you have a People and a Groups branch, your initial LDIF may look something like this:
dn: dc=example,dc=com
objectClass: top
objectClass: domain
dc: example

dn: ou=People,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: People

dn: ou=Groups,dc=example,dc=com
objectClass: top
objectClass: organizationalUnit
ou: Groups

So, that covers the simple example test case, but I'm guessing that is not enough for most people. More than likely, you already have a real LDAP server out there, and you are wanting to mimic that server in your In Memory instance, so you can test in an environment that matches your production environment.

That is not necessarily apparently easy right off the bat. You need to replicate your server's schema into the in memory one, replicate all of the branches that you use, and pull in some real data to test with.

As it turns out, replicating your existing server is not too terribly complicated. If you happen to have an LDIF file that defines your server's schema on hand, you can include that into your project, and the following code should load it up:
InputStream schemaLdif = this.getClass().getClassLoader().getResourceAsStream("schema.ldif");
Entry schemaEntry = new Entry(IOUtils.toString(schemaLdif, "UTF-8"));
Schema newSchema = new Schema(schemaEntry);
config.setSchema(newSchema);
The above lines would be inserted into the sample code from the UnboundID website, before the call to the InMemoryDirectoryServer constructor. Note that I have not actually performed the above method myself, as I do not have my server's schema LDIF handy.

For me, and many others out there, you may not have access to the schema LDIF file of your server. Luckily, their is an easier way. First you must actually make a connection to your server, so that you have an LDAPConnection object pointing to it. Then you use the following code snippet:
LDAPConnection connection = //however you get your connection object
newSchema = Schema.getSchema(connection);
config.setSchema(newSchema);
The advantages to this method are that you always have the most up to date server schema, and you don't have to worry about tracking down and storing a hard copy of your server's current schema. The down sides to this method are the dependence on your server being up and available, and it is a little slow - my testing took about 6-7 seconds to retrieve the schema.

Storing the schema you retrieve from the server in a static variable can help with the performance bite, as it would only have that 6-7 second delay for your first test. However, there is nothing you can do about the dependence on the external system when utilizing this method, so that is a personal choice you have to make.

Once you have the schema loaded, you still need to import the above LDIF file for your base DN, as well as any test data you might have. All of that can be accomplished with the above importFromLDIF call. Since you should be matching your server's schema exactly, you should be able to just export records directly from your server into an LDIF file, and import them directly into your in memory server.

Once you do that, you should be good to go, ready to execute your tests against a clean, save, local environment.

Wednesday, January 26, 2011

Unit Testing

Unit testing is one of my favorite time saving techniques. I am amazed at the number of people that I talk to that don't write unit tests, because it takes too long, or it feels like writing it twice, or a handful of other reasons that I have heard.

Plain and simple, if used right, unit tests can save you a whole bunch of time. It is one of those spend-a-little-to-save-a-lot techniques. I find this to be particularly true in web development.

I get rather sick of manually testing my stuff for a web application. Each little change is a complete chore. You have to
  1. Make the change
  2. Deploy the new app
  3. Possibly restart the web server
  4. If necessary, log in
  5. Navigate to the page in question
  6. Perform activity under examination (potentially a lot of clicks involved here)
  7. Evaluate the displayed results
Not only is this time consuming, but it is also incredibly prone to error conditions. What if something didn't deploy right? What if the web server is bugging out? Maybe the displaying of the results is incorrect. What if you hit the wrong link, or typed in the wrong value, or whatever. You get the idea.

Another one I see people do sometimes is that they will write little main() functions in their code, in order to test their functionality. Guess what - that is basically what a unit test is! Only now you are adding more code, and getting less support by your IDE.

Unit tests are basically just little code snippets that most modern IDEs know how to support natively. They can be automated, they can provide reports, and better yet, they can be rerun time and time again, at just a single click of a button.

Unit tests really are not all that scary, once you get into them. Here is an example of a very simple test that will always pass:

public class TestMyClass
{
     @Test
    public void testMyClass() {
        assertTrue(1 == 1); 
    }
}

Not too bad, right? This will always tell you, every time you run the tests, that 1 does in fact equal 1. Not all that helpful, but it is a start. So, let's go with a more real-world scenario:

public class TestMailService
{
     @Test
    public void testSetForwarding() {
        MailService mailService = new MailService();
        mailService.setForwarding("fromaddress@domain.com",
            "toAddress@domain.com");


        assertTrue(
            mailService.getForwardingAddress( "fromaddress@domain.com" ).
            equals( "toAddress@domain.com" ));

    }

}

So, this is a slightly meatier example that tests a mail service that sets up forwarding. With this little snippet in place, you can determine that your mail service library is successfully setting and retrieving the forwarding address correctly. If you or somebody else does something that breaks that functionality, you do not need to experience that through your web browser in order to become aware of the problem.

This is by no means an exhaustive study on unit tests. It really only scratches the surface. If you have not used them before, though, I highly recommend you look into it. Save yourself some time, and make your coding world just that much better.