Mocks are evil! Ok that is kind of rude; mocks are useful when we are doing interactive based testing. The question whether Interactive based testing is good think to do. Interactive based testing is to check whether a particular method is calling another object with some particular parameters x number of times. So many bad things are happening here.
1) We are testing the implementation rather than verifying behavior.
2) As our interactive based tests are tied to implementation, refactoring becomes a pain. Refactoring will take more time as we need to fix interactive based tests.
3) It puts us in wrong perspective. Instead of thinking in terms of behaviors in our unit (tests / contexts) we end up thinking about implementation like whether this method calls that method.
11 comments:
This could be seen in two wasy:
1) We are testing the implementation rather than verifying behavior.
And this is exactly what we do. Example, my method should call dao in order to persist to db. I don’t want to know if we actually persist the object, I just want to now whether particular method of the INTERFACE of the dao is called. Interface identify behavior. That’s why I want to verify this behavior.
2) As our interactive based tests are tied to implementation, refactoring becomes a pain. Refactoring will take more time as we need to fix interactive based tests.
Since we don’t care about the actual implementation of the method, this could save us a lot of troubles when we change, refractor or substitute different objects (implementation) for the tested interface method. If we have problems with the mocks when we refractor, this means, the behavior has changed and that is what we are testing. If we don’t care about the behavior then we are probably better off with stubs.
3) It puts us in wrong perspective. Instead of thinking in terms of behaviors in our unit (tests / contexts) we end up thinking about implementation like whether this method calls that method.
If you use real object, then you think about the state of this object and not for their behavior, which is no any different than thinking about db tables.
Otherwise, I feel your pain. Probably, we should used them more rationally.
I don’t want to know if we actually persist the object, I just want to now whether particular method of the INTERFACE of the dao is called.
Igor I accept with u. This above scenario is a good example for interactive based testing.
Take this code snippet for example.
public void doSomethingAndSave(){
doSomething();
getDao().save();
}
For the above method, we might have these following behaviors to verify.
Behavior 1) shouldCallDaoSave
Behavior 2) shouldDoSomething
Behavior 1 is perfect for using interactive based testing. By using mock we could avoid going to database.
In behavior 2, we do not care whether it is calling getDao().save(), all we want to verify is whether it is doing “something" that we specify. In this situation state based testing is better than interactive based testing. By using dummy stubs, we do not have to change this behavior even if we decide not save anything after doing “something”. But if we used interactive based testing for this behavior, then we would have to fix this behavior (even though nothing has changed in behavior that it specifies) when we refactor this method not to save anything.
In 1) you are interested in how the dao will behave. That’s when you need a mock and verify its behavior as you pointed out.
In 2) you have behavior specification that tests exactly “doSomethingElse”. So, in this case you don’t need to mock dao because you don’t verify its behavior. You should have one assert/verification per test, so you testing/verifying the dao at all. You just need to stub it. However, I don’t think that state testing has anything to do with this situation. If you stub it, you don’t check for the state at all.
I believe that state testing is not good approach to OO design. It makes us think in terms of data holders (aka db tables) instead of thinking about object behavior and object collaboration. Since, one of the principles of OOP is hiding the state of the object and providing specific behavior, we should avoid thinking of the state of the object. That’s one of the major reason why most of the object that we see are data holders with public gettter and setters. I would prefer to see object without any public getter or setter rather with methods giving me some behavior.
So in summary, I completely agree that we should use mock with great deal of consideration. Also, most of the times stubs would be more appropriate. Yet, don’t confuse using stubs with state testing. I completely disagree that state testing can be any good for OOP.
Igor I agree with you :)
Quote from Martin’s article about state based testing,
“…kick your primary object with the behavior you want to test. Once it's done you then need to find out if everything went well. This you probe by using a bunch of assert statements, both against the primary object…”
http://www.martinfowler.com/articles/mocksArentStubs.html#State-basedTesting
In my article I am not advocating State based testing… I am more concerned about verifying behavior of an object. How we verify behavior depends on framework we use. If we use Junit, unfortunately we have to verify behavior by asserting the state of object.
I have not looked at JBehave, it will be interesting to see how this framework enables us to verify the behavior of object.
Martin’s article is classic but it is a little outdated already. As I pointed out, I don’t think that state base testing brings value for creating good OOD. (Probably for QA testing yes, but not designing).
You can use JUnit with EasyMock or JMock and verify behavior without any problem. It is not design to verify behavior but with a little more effort you can achieve Behavior Driven Design with JUnit as well.
I agree on everything else besides the testing of the state of an object. Since the state should be encapsulated inside of the object, I don’t think that trying to test it will evolve to a good Object Oriented Design. You shouldn’t be allowed to ask an object for its state; rather you should delegate some responsibility to an object and verify whether it perform it correctly.
You can use JUnit with EasyMock or JMock and verify behavior without any problem. It is not design to verify behavior but with a little more effort you can achieve Behavior Driven Design with JUnit as well.
Using EasyMock or JMock with Junit , we will be still testing implementation not the behavior.
I agree on everything else besides the testing of the state of an object. Since the state should be encapsulated inside of the object, I don’t think that trying to test it will evolve to a good Object Oriented Design. You shouldn’t be allowed to ask an object for its state; rather you should delegate some responsibility to an object and verify whether it perform it correctly.
Lets take classic object Lamp.
public class Lamp {
private boolean on;
public void switchOn(){
setOn(true);
}
public boolean isOn() {
return on;
}
private void setOn(boolean on) {
this.on = on;
}
}
Might be I am missing something here. How can we make sure “switchingOn” behavior worked correctly without verifying the state (on/off) of Lamp?
This example is very similar to the problem that they describe in famous article about OOD - “The Mark IV Special Coffee Maker”. http://www.objectmentor.com/resources/articles//resources/resources/articles/CoffeeMaker.pdf
We are writing a program here, and programs are about behavior! We should think about behavior. What is the behavior in class Light? It is the behavior of a system that is the first clue to how the software should be partitioned.
Clearly, from you design the Light object just wants to be turned on or turned off. Thus we might put an void switchOn() and switchOff() method in class Light.
I think that the return value of these methods is the problem here.
So, if the behavior that you want is to swtichOn the light and then verify that everything went successfully:
public interface Switchable{
boolean switchOn();
}
public class Lamp implements Switchable {
public boolean switchOn(){
implementation that we don’t care
}
}
Behavior Specification will look like this.
public void shouldSitchOnTheLightSuccesfullyWhenEverythingIsOk(){
Ligh light = new Light();
shouldBeSuccessfull(ligh.switchOn());
}
That’s it. I don’t need to check the state of the light in order to verify that the behavior specification of this object.
Later on, if you need additional behavior to check whether the light is on you can add the other method:
public boolean isOn();
However, do you really want to know that? I would try to avoid this method since “One of the core tenets of object-oriented programming is encapsulation. It’s one of OO’s most powerful ideas. More powerful than inheritance. Unfortunately it’s also one of the most ignored.” From Dave Astels’s blog: http://blog.daveastels.com/?p=55.
If you put isOn() method just to test switchOn() without actually needing it then you reveal to much from your code without needing it. From the pragmatic guys http://www.pragmaticprogrammer.com/articles/may_04_oo1.pdf:
“The best code is very shy. Like a four-year old hiding behind a mother’s skirt, code shouldn’t reveal too much of itself and shouldn’t be too nosy into others affairs.
But you might find that your shy code grows up too fast, shedding its demure shyness in favor of wild promiscuity. When code isn’t shy, you’ll get unwanted coupling.”
Your testing and behavior specification are tightly coupled to the implementation of Light object. “This is a classic approach from the old days or procedural programming. It is NOT OO. This is just one example of the approach of treating objects like new age data structures: Ask an object for information about its state, process that, make decisions based on that, then do something to/with the object.” (Astel’s)
All we shouldn’t now is how to ask the light to turn on and whether it went successfully.
The proper approach is to ask the Light to turn itself on. Let me know if you were able to do that. We don’t care about it state at all.
This is a little aside but:
“As a side note, writing code this way makes it much more understandable.” (Astel’s)
Also, from Dave Astele’s brilliant article about Behavior Driven Design (BDD) http://daveastels.com/files/sdbp2005/BDD%20Intro.pdf:
“First stop thinking in terms of tests. As Bob Martin has been saying for a few years “Specification, not Verification”. What Bob means is that verification (aka testing) is all about making sure (i.e. verifying) that your code functions correctly while specification is all about defining what it means (i.e. specifying) for your code to function correctly.”
So based on the above, if you argue that in my behavior specification I would not be sure whether the light is actually turned on right now (verify that my code works correctly), I would say that I just want to now that light has been turned on successfully (not exactly interested if it is on right now.)
Anyway, this debate is going on for the last 10-12 years of OOP, so I think will continue have arguments about that. Still, if we care about the state than what is the difference between OOP and database.
A little correction from above:
All we shouldn’t now is how to ask the light to turn on and whether it went successfully.
should be read:
All we should know is how to ask the light to turn on and whether it went successfully.
Igor it is a great explanation. I think my problem is my state of mind. Even though I am familiar with OOP concepts, I am relaxing those OOP rules to make it testable. This is wrong. I should consciously do behavior driven design.
Instead of State Based Testing, how about Behavior Based Testing?
The point of testing should be to target the areas where there is a high risk of something going wrong. There's always a limited time available, and the goal of testing is to make the software as reliable as possible within that time.
I've always found things often go wrong where one part of a system communicates with another (e.g. business logic connects to a database). The mocked out functions are usually called correctly, but something else is going wrong (e.g. permissions have not been set on the database). Should the time to write tests based on mocks instead be spent on time writing tests that prove the components of a system can talk to each other?
Richard
To test whether components are interacting with each other as expected, Integration tests are more apt than unit tests. Mocks or Stubs are mainly for unit tests.
In case of unit tests, I do not care whether component that I am calling is working correctly, all I am worried about whether my component is calling the other component (Interaction based tests)
Post a Comment