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

10 Comments:

"As everything else, we should be careful not to abuse this kind of testing."

Probably you want to stress this a lot when it comes to Mocks
Blogger Siva Jagadeesan, at November 18, 2005 12:24 AM  
Move the behaviour of retrieving data from database/some other resouce into a DataLoader or whatever it might be. Then you Mock that interface. You should not be Mocking methods in the Class you are testing. Use Mocks for specifying interactions with other Actors. If you are in a DataLoaderImpl then really go to the database or web service.
Anonymous Anonymous, at November 18, 2005 1:40 AM  
Anonymous said... "Move the behavior of retrieving data from database/some other resource into a DataLoader or whatever it might be."

Sam said... "This surely seems like a classic case of the need for two seperate objects."

I completely agree that we need two objects in this case. However, there are rare cases, when it is not appropriate to create another class and setting up a fixture is too painful. Example like that however, are more complicated to demonstrate but I will try again:

class SomeClass{
void add(Object obj){/*save/update to db*/}

void delete(Object obj){
//some condtions that we want to test in our unit test
if(conditions evaluates to true){
obj.markAsDeleted();
add(obj);
}
}
}

In this example, we will have already test the add() method. Also, once we mark object as deleted, we can't retrieve it anymore. It is saved for data warehouse purposes or something like that. So, if we want to test this method, the one way to know if the conditions evaluated to true and add() method was called is to mock the add() method and verify it. Of course, we can make both methods return boolean, and then just verify the return value. However, this will go unnecessary to db.

Anonymous said... "You should not be Mocking methods in the Class you are testing."
I still don't see what the problem with that is. I believe that a unit should be even smaller in granularity than testing a single method, not a class. So, if my unit of testing is the method, why not mock other methods in the same class. They are not part of the unit I am testing.
Blogger Igor, at November 18, 2005 8:23 AM  
EasyMock seems to me to be more complicated than I would like.
(Disclaimer: I haven't used EasyMock for mocking classes, only interfaces)

I would write a private class ShuntedSomeClass, overriding the add method to log it to a string, then assert against the string.

// Shunted object
private class ShuntedSomeClass extends SomeClass {
public StringBuffer log = new StringBuffer()
void add(Object obj) {
log.append("add(),");
}
}

//Test that add was called.
assertEquals("add(),", shuntedClass.log.toString());
Anonymous Anonymous, at November 18, 2005 8:58 AM  
Anonymous said... "I would write a private class ShuntedSomeClass, overriding the add method to log it to a string, then assert against the string."

This is another way of doing it as I pointed out in the post:

2.) Another solution is to create something like a proxy object, which overrides the method with some stub implementation.

However, although it could be simpler to do it the first time, it could lead to a lot of repeating code. There is no one solution for every case, so I do sometimes use this approach when it is more appropriate or easy to create.
Blogger Igor, at November 18, 2005 9:43 AM  
Good discussion for dynamic mocks vs static ones could be read here:

http://butunclebob.com/ArticleS.DavidChelimsky.StopMockingMe

and here:

http://butunclebob.com/ArticleS.MicahMartin.PreludeToTheMockOff
Blogger Igor, at November 18, 2005 9:50 AM  
Igor,
In the example you gave it looks like you need to separate out what Eric Evans would call a repository from your data access object.

The fact that you want to mock out part of an object should tell you that there are at least 2 roles interacting here.
The add method is persistence and should go on a DAO whilst the code in delete is clearly business logic and belongs on a repository.

By adopting that approach you can mock out the DAO whilst writing _unit_ tests for the features of the repository layer and then you can write functional tests for the DAO layer.

Maybe I'm wrong but it looks like you want to test branches of code within a method? This implies that you're testing in order to get maximum coverage (and so you're forced to write tests for every piece of logic in every method) rather than writing tests to specify coarse-grained behaviour on a feature by feature basis.

Remember that this is not traditional unit testing where we seek to achieve 100% line or branch coverage. Our unit of testing is the feature _not_ the method or the line of code. Testing things that are more fine grained than features leads to incredibly brittle code that prevents refactoring.
Blogger Thoughtblogs Administrator, at November 19, 2005 4:46 AM  
Ade, thanks for the very good comments.

ade said... In the example you gave it looks like you need to separate out what Eric Evans would call a repository from your data access object.

Very good suggestion. I didn't think of that. However, in most business applications implementing "Repository layer" can't be justified very easily and could complicate the code in some ways. Anyway, I completely agree that using repository is good way to solve the problem.

ade said... Maybe I'm wrong but it looks like you want to test branches of code within a method? This implies that you're testing in order to get maximum coverage (and so you're forced to write tests for every piece of logic in every method) rather than writing tests to specify coarse-grained behaviour on a feature by feature basis.

Absolutely, absolutely correct except that I am very, very opposed to test coverage measures. I have much different concerns here. Actually, I am not trying to do Behavior Driven Development rather than Test Driven Development. I talked about this in previous posts, but the best reference could be found in David Astel's paper BDD - http://blog.daveastels.com/?p=53

This small excerpt to explain why I am specifying very FINE grained verifications:

"- So way back when, they were talking about writing tests. And that's probably why it has the testing centric vocabulary, and that is why people think it's about testing!
- people think that they have to test methods or classes
- For example, having a text classes and production classes in a 1-1 relationship.
That's not what we want... we want behavioral divisions... we want to work at a level of granularity much smaller than that of the typical unit test
….. Does that mean you write tests? No. It means you write specifications of what your code will have to do. It means you specify the behavior of your code ahead of time
………
When you realize that it's all about specifying behavior and not writing tests, your point of view shifts.
Suddenly the idea of having a Test class for each of your production classes is ridiculously limiting.
And the thought of testing each of your methods with its own test method (in a 1-1 relationship) will be laughable."


ade said… Testing things that are more fine grained than features leads to incredibly brittle code that prevents refactoring.

Again, it could be found in the article above or in my previous posts but if we should try to write very fine grained test about any behavior specification which would imply only one assert per test. If we have only one assert per test, than refactoring would not be a problem rather it would be helpful to see what exactly failed.
Blogger Igor, at November 19, 2005 11:18 AM  
I see what you mean now about fine-grained specifications.

Could you elaborate on:
However, in most business applications implementing "Repository layer" can't be justified very easily and could complicate the code in some ways

It seems to me that Repository layers can radically simplify business applications by making it possible to unit test or specify more of your application without having to touch a database. The Repository layer gives us a much more stringent separation between domain logic and persistence logic. If you look at the SomeClass example you can see that domain logic (the evaluation of certain conditions to decide whether we should really delete this object) is being mixed with persistence logic (the marking of an object as deleted and then propagating that deletion into the database).

This stringent separation also has additional benefits if you need to migrate persistence technologies as you know that the repository layer can remain unchanged and only the data access layer needs changing.

See http://abc.truemesh.com/archives/000464.html and http://abc.truemesh.com/archives/000466.html for some examples Chris Matts and I worked out last year.
Blogger Thoughtblogs Administrator, at November 20, 2005 4:37 AM  
Ade,

It is very hard to distinguished data mapper patter from repository pattern in the examples provided above. However, I have some doubts that reading and parsing xml file is faster than connecting to real database. Anyway, if this is needed by the application, then it is good solution.

Anyhow, I will step back a little to evaluate more on my opinion that repository layer is not always appropriate.

First, I start with this examples using db access because it was much simpler to show the need for mocking. Having said that, I have never used this approach of mocking with database access classes. I believe that unit test should not go to db. Also, a project should have unit, integration (could only be persistence in some cases) and functional tests. Only in integration and functional tests we should connect to db. My opinion on that is if we mock database (substitute with in memory db or something like that) we lie ourselves that we are testing something. So, we go to database only when we need it, all other time (everything in unit tests) should mock the db access.

Further, I needed to mock some of the method in the same class in very complex payment calculation, when the result of the first method was used by a second to calculate something else. In situation like that, it turns out that it is much more productive to mock a class method then to do the very, very complex set up to all of the objects involved in the calculation.

Also, in situation like my example, we usually have dao which are invoked in a service layer. The service object will call some method on a domain object to define whether it should be deleted. If this evaluate to true than we call dao to delete the object. In situation like that, we can very easily mock the dao object. So, I accept that this is business functionality and should be somewhere else but I try to make the examples simpler. I will know for the next time not to use database if I want to demonstrate mocks :-).

Anyway, let’s go back to repository pattern. According to Martin Fowler definition, Repository pattern provides the following benefits:
-Presents a simple object oriented query interface with criteria objects.
-Ability to swap different databases (including in-memory ones).
-Having simple in-memory data store (mostly used in unite test!).

Most of the projects in Java have DAO and use Hibernate. When we have this combination, we have very easily all of the benefits that Repository provides:

- Hibernate has very powerful object oriented query language using criteria objects:
http://www.hibernate.org/hib_docs/v3/reference/en/html/querycriteria.html
- You can swap with almost no efforts between any kinds of databases (including in-memory dbs)
- As I mention above, I don’t consider having in-memory data store for unit test is valuable since you should mock all db access in unit tests. Anyway, if we really want to, Hibernate has two levels of caches. Using caches together with longer opened session and transaction rollback, you can practically have in-memory data store without too much efforts.

I hope this could clarify a little my position about Repository patter.
Blogger Igor, at November 27, 2005 9:57 PM  

Add a comment