Igor's Blog

"... no matter what they tell you, it's always a people problem!"

Thursday, November 17, 2005

Interactive Tests – Mocks to the Extreme!

If you do Behavior Driven Development (BDD) or in less extend applicable if you do Test Driven Development (TDD), you will definitely get to a situation where you need to know that a certain method has been call but you are not interested in what is going on inside of this method. You don’t want to check whether the state of an object has been changed. It is responsibility to another unit test – “Try to use only one assert per test” – (Behavior Driven Development)

So, Nothing new here. I can use a mock object and specify the expected behavior I need. However, the complication comes when the method that I want to mock is specified in the same class I am testing. Example of this would be some service object that has method to retrieve data from a database and another method in the same class that filters this collection. How do we mock the database access method in order to verify the behavior of the filter method?



public class SomeClass {
Collection retrieveSomeData(){ // goes to db.}

Collection filterBySomeCriteria(){
Collection all = retrieveSomeData();
// apply some rules to filter
return filteredCollection;
}
}

If it is not easy to verify the behavioral specification of a specific context, then it should be refactor via Template, Strategy or some other form of collaboration. This is definitely very good principle that I follow a lot. One of the primary reasons for me to use BDD/TDD is to be able to design easily testable and verifiable behavior as well as a simple overall abstraction of a problem.

  1. Another solution is to create something like a proxy object, which overrides the method with some stub implementation. This is definitely possible and it is a technique that I used to do a lot in the past when testing methods that use Singletons objects. (Service Locator Patter - thankfully not any more with DI). However, it is as unnatural as to try to set specific data to db and then retrieved before verifying.

  1. Sometimes however, you probably don’t want to refactor similar implementation to another collaborating object (because of “Cohesion”, “Completeness”, “Convenience”, “Consistency” or some other reason). In situation like that, we can very easily create a proxy of the object under test and stub/mock the method that we want. We can create such an object by ourselves, similar to 2). However, much more convenient is to use some framework like EasyMock.

In EasyMock, we can mock not only against interfaces (much preferable) but against actual classes and even the class under test. The example above would be:






public void testShouldFilterBySomeCriteriaWhenThereAreMoreThanOneElements() {
// create mock control for this class
MockControl someClassControl = createClassControl(SomeClass.class, new Method[] {
SomeClass.
class.getMethod("retrieveSomeData", new Class[] {}) });
// create the actual class under test.
SomeClass someClass = (SomeClass) someClassControl.getMock();
// expected returned result for the mocked method.
Collection retrievedData = Arrays.asList(new Object[] { object1, object2 });
// set the expectations
someClassControl.expectAndReturn(someClass.retriveSomeData(), retrievedData);

classControl.reply();

assertEquals("One entry should be filtered", 1, someClass.filterBySomeCriteria().iterator.size());

// since we need this more as a stub rather than a mock,
// it is not advisable to verify the expected behavior
// anyway, if you really need to
classControl.verify();
}

As everything else, we should be careful not to abuse this kind of testing.
|| Igor, Thursday, November 17, 2005 || link || (11) comments | links to this post

Monday, November 14, 2005

Learning Ruby and Ruby on Rails

Why:
After all the talk about Ruby/Rails. Also, so many highly respectful people in Java community raved so much about Ruby/Rails. Probably, there is something.

Background:
I am Java guy. I have one-two year experience with .NET as well but I don’t want to go back there (at least not voluntarily). Why? I like a little more methodical oriented Java community (many could read this as unnecessary complex but still). Also, it turned out that I am not so much Java developer rather I am Eclipse/IntelliJ developer. However, I like the simplicity in design, so I thought I could learn many valuable things from Ruby/Rails and probably become very passionate about them.

When:
When I found out a comparably decent (for dynamic language) plugging for Eclipse – (the same debugging as in Java, some auto-completions as well) and when I find recommendation for good books – “Programming Ruby” and “Agile Web Development with Rails”. Why so late? I have 1-2 years with Python and Plone. I didn’t think that there could be something so much new or interesting.

Progress – Learning by comparison:
I am learning Ruby/Rails with by reading various sources like blogs, books, articles, example applications and various tutorials. I can’t help but compare with all of my previous experience in web development. However, my experience with Ruby/Rails continues to be somehow mixed. Ruby so far is very comparable to Python (fortunately without the indentation stuff). I am still not so much after dynamic languages but I kind of like many features in Ruby and so far, Ruby is more than I expected.

Rails framework, on the other hand was a little disappointing for me (at least for now).
On the positive side, I never saw something even close in Java to the controller/listeners implementation in Rails. I was really impressed by this simplicity.

Next
Overall, I am still excited to continue learning Ruby and Ruby on Rails. I see some benefits in creating not to complex websites, creating fast prototypes, huge potential in testing and even in the integration where we need more “relax” and definitely more simple way of doing things. Besides, if Ruby turns out to be big and there are exciting projects to work on, I don’t want to miss them.
|| Igor, Monday, November 14, 2005 || link || (0) comments | links to this post