Thursday, January 28, 2010

UnitTesting made easy with the power of TypeMock

I want to test the persistence layer of my code. One of the recently added requirements for the persistence layer is that it accepts a domain class that wraps around the user name to ensure consistent handling throughout the application of a username. This is because currently the decision is to strip the Windows Domain name off of a userName before using it or storing it. Should the decision be reversed, there's a central configuration alteration to make, without having to recompile the application. How nice are changes that require a simple text file to be changed?

So the call would be:

var result=repository.GetAssociate(new UserName(DependencyContainer.GetAssociate()));
Which you can't call from the persistence layer because
  • The Dependency container is defined in an assembly not referenced by the persistence layer 
  • The UserName constructor is internal to the domain assembly
So to make the method repository.GetAssociate testable, we would have to alter the OOP design of the code to make it testable in most frameworks( because there is no public constructor for UserName).

You could of course messy up your code by using dependency injection on everything that needs testing. This concern does partly fall down if you centralize the coupling between your domain and external dependencies, but remains when you try to test individual methods inside a class, or anything internal, private, or that accesses static methods(most testing frameworks can not handle mocking out static methods, constructors, or factories, as I understand it TypeMock can)

You could make your code messier and more prone to issues in dependent code by changing username to a public interface and changing the persistence layer to accept that interface, but then automatic business logic enforcement goes out the window. If an object outside of the domain assembly can not create a copy of a domain object it has no reason to create, then it's tougher to accidentally not pass things through the proper domain classes/methods.

So how do I test this method?


 Enter TypeMock.

I have the following code in the persistence assembly's test project which does not compile:
                [TestMethod]
        public void GetAssociate_InvalidUser_IsNull()
        {
            
var repository = new AssociateRepository();
            
var result=repository.GetAssociate(new UserName("badUser"));
            
Assert.IsNull(result);
        }
 Notice I said the persistence assembly, which makes the possibility of

[IInternalsVisibleTo] a poor choice.
 I delete the `new UserName("badUser")` call and TypeMock springs up with a tooltip that says Alt+/ to Fake UserName.

Which immediately produces the following:

        [Isolated]
        [
TestMethod]
        
public void GetAssociate_InvalidUser_IsNull()
        {
           
UserName fakeUserName = Isolate.Fake.Instance<UserName>();
            
var repository = new AssociateRepository();
            
var result=repository.GetAssociate(fakeUserName);
            
Assert.IsNull(result);

        }
and my cursor is sitting at the fakeUserName parameter with a new tooltip Set Object Behavior Alt+/
I hit that keysequence and it reflectively I assume shows me options for what this object could do, and what methods I might want to produce a value for. Since this is my first time, I don't know if it's automatically pulling in only the methods that the method I'm testing will make use of. I had only the method I wanted to fake in a drop down menu, going into that produced 3 options: Ignore Call, Return Value, Throw Exception. I enter return value and this is produced:

        [Isolated]
        [
TestMethod]
        
public void GetAssociate_InvalidUser_IsNull()
        {
           
UserName fakeUserName = Isolate.Fake.Instance<UserName>();
            
var repository = new AssociateRepository();
            
Isolate.WhenCalled(() => fakeUserName.ToString()).WillReturn();
            
var result=repository.GetAssociate(fakeUserName);
            
Assert.IsNull(result);

        }
My cursor is already sitting in the WillReturn waiting for me to type in the parameter. I type in "badUser", run the test, and it passes. that's terrific. 

I did not have to alter my code to fit the tests. I did not have to think about testability when writing the code. I did not need any special assembly attributes. I want TypeMock. It makes life so much better.
 

1 comment:

  1. "This is so exciting I am getting emotional. This is exactly the vision we had in mind to lower the barrier for people who want to unit test with our tools.
    Great job, team!"
    By Doron Peretz - Development Manager @ Typemock

    ReplyDelete