Martin Fowler has written a piece on the difference between interaction-based testing and state-based testing. Mocks help you accomplish interaction-based, and this is shown by Martin via examples to show us the difference between the approaches.Introduction
The term 'Mock Objects' has become a popular one to describe special case objects that mimic real objects for testing. However the term mock was not originally meant as a more catchy name for stub, but to introduce a different approach to unit testing. In this article I dig into this difference of style to explain the difference between the interaction-based testing style favored by mock object fans, and the more usual state-based testing style.
Read Martin Fowler in Mocks Aren't Stubs
This is a good article (I like reading Dion's articles).
However, here are some problems that I see;
I) With (static) Mock Objects
1) Test info is in many files (Mocks and Junit test code)
2) The code under test is likely to be modifed to make it testable
II)With Dynamic Mock Objects (like EasyMock)
1) The dependencies of code under test needs to be defined as interfaces (though a good practice, may not have been followed)
2) Instances of dynamic mocs which emulates the dependencies have to be passed to code under test. This is not always easy.
3) Due to above #1,2, very likely to require modifing code under test.
There is an alternate.
It is Virtual Mock objects, which uses AOP and doesn't have above problems.
However, I don't know how mature "Virtual Mocks" is and haven't seen a tutorial on it.
There is an alternate.It is Virtual Mock objects, which uses AOP and doesn't have above problems.However, I don't know how mature "Virtual Mocks" is and haven't seen a tutorial on it.
About Virtual Mock Object, maybe this link
will help you a little.
This is what I call black box testing and white box testing.
Black box testing (called state-based testing in the article) is my preferred method because of the decoupling to implementations as Martin points out. White box can be broken too easily as you refactor the code, and it can be too tedious to write. However most of time I find myself writing "gray" box tests.
I like Martin's perspective not on the technologies but on the psychological profile of the technology users. Each choice is a style that may fit with a developers personality style. Mock objects seem best for agile, CRC card weilding types while State style is for data-driven, design-first types.
It's one thing to have a testing dependency on JUnit but to heap Mock and its Reverse Polish Notation style and other devices on top seems a stretch. I like my fixtures light and fluffy. Mocks may lead to more de-coupled and cohesive objects but fixtures just seem to get it done with less overhead.
Today, I taught a session on testability and Spring. I had some of the same ideas. It's all about using the most effective technique to isolate the appropriate block of code. I believe that the most appropriate block of code may change with the strategy of your individual test case. For example, with AOP, your business object is your naked POJO plus the aspects. It's interesting to test both. The Spring inherited context is a great tool for doing this kind of stuff.
Bruce, Interesting what you said about using different techniques for different blocks of code. I tend to use state-based tests for my domain model (business logic) layer and interaction-based tests for my user interface process, application facade and data access layers.
This mix seems to work well and provides me with the style best suited for each layer. My domain model is all about groups of objects and their states, and my other layers are primarily about getting some separation of responsibility into the application. The clean and clear interactions between the layers help to reduce the overall complexity of the application.
I used to use the words isolated and integrated to describe my different testing styles. Now I have another way to describe testing style... excellent.
Now really. Mock objects may be used due to different "testing style" by "Mock Object Fans". Most of the time however, they are used, because there is insufficient infrastructure available to create proper, backend systems or database based tests. For a lot of systems that require integration, there is simply no "testing environment" available, let alone one with predictable results. However there is a grave danger in "interaction based testing". Far too often have I come across tests that test the obvious. It is after all no big surprise that after calling setX(Y) on a Mock Object, getX() returns Y. Where the interaction is more complicated a different problem arises: Who tells you that the mock objects actually behave like the system they are mocking, since the mock objects need changes themselves they would need tests as well, so the question is, if a test tests the actual codebase or if it tests the mock object and how to tell the difference...
Far too often have I come across tests that test the obvious. It is after all no big surprise that after calling setX(Y) on a Mock Object, getX() returns Y.
Aslak Hellesoy had a while ago a good discussion
on this issue:Mocks are substitutes for real classes. It is quite common to misunderstand what classes they are supposed to be substituted with mocks. Mocks are supposed to replace the objects that the class under test depends on. You should never substitute the class you intend to test with a Mock. If you do, you're not testing the real thing.
Where the interaction is more complicated a different problem arises: Who tells you that the mock objects actually behave like the system they are mocking, since the mock objects need changes themselves they would need tests as well, so the question is, if a test tests the actual codebase or if it tests the mock object and how to tell the difference...
Yes, maybe you could have tests for the more complicated mocks to validate that they are behaving as well?
However, I think that for more complicated components, such as databases or network connections, you definitely need a real-like testing environment against which you can do integration testing -- preferably automatically. For the fast-running "programmer tests" needed to do test-driven development, mocks may be good. But it makes sense to have another, slower set of automatic integration tests that take on account networking issues, database datatype conversions and such that would be a pain to mock.
Also application server -dependent functionality can be tested by running the integration tests in the running (development / testing) application server.
I have noted a fixation with testing the compiler rather than the system. Entire development teams are encouraged to write unit tests which totally decouple their functions from the rest of the system.
ie A function of 30 lines, may take 3 params, and have a couple of code branches within it that call out to maybe 4 other objects. The team can focus on driving the code branches, then mocking out the called functions. They will study their code, then run it with junit and clover and applaud the green bar of happiness.
This proves that the high level language does, in fact, work. What a fine compiler we have.
The entire systems interactions may never get tested, or rather, tested very late in the project, with disasterous results. Obviously an extreme example, but I have seen such a thing happen with mocking.