-
Opinion: Anti-patterns with Mock Objects (14 messages)
- Posted by: Dion Almaer
- Posted on: January 12 2004 14:57 EST
Aslak has written a nice little piece about an experience that he has had in the trenches. Test Driven Development is new (in the mainstream), and a lot of people are still getting to grips with it. As always, often people think they have "got it", when there has actually been a misunderstanding. Aslak discusses an anti-pattern with Mocking.
"...the problem with the codebase developed by this team I'm referring to is that many of the "mocks" replace the wrong classes. The result of this is overly complicated tests, in addition to a false perception of what is being tested. Many of the "mocks" in this codebase is not really proper mocks, apart from having the word "Mock" in their name.
So where and how are these misunderstood "mocks" used? They are being used as substitutes for the classes that are supposed to be tested(!) In short, the unit tests are testing the "mocks".
The system consists of WebWork Actions that talk to Hibernate via DAOs (Data Access Objects). -A fairly common combination of technologies and patterns."
Read Aslak Hellesoy in Oh no, we're testing the Mock!Threaded Messages (14)
- Another Anti-Pattern: "Assert-Free Testing" by Paul Strack on January 12 2004 16:55 EST
- jdbc mock antipattern by Larry Sherrill on January 12 2004 18:03 EST
- jdbc mock antipattern by Michael Mahemoff on January 12 2004 19:25 EST
-
jdbc mock antipattern by Ara Abrahamian on January 13 2004 05:40 EST
-
jdbc mock antipattern by Praveen Peddi on January 13 2004 12:21 EST
-
jdbc mock antipattern by Aslak Hellesøy on January 13 2004 06:38 EST
- jdbc mock antipattern by Michael Mahemoff on January 13 2004 08:27 EST
-
jdbc mock antipattern by Aslak Hellesøy on January 13 2004 06:38 EST
-
jdbc mock antipattern by Praveen Peddi on January 13 2004 12:21 EST
-
jdbc mock antipattern by Shorn Tolley on January 13 2004 11:21 EST
- Postel's 'Law" by Robert Stine on January 15 2004 08:34 EST
-
jdbc mock antipattern by Ara Abrahamian on January 13 2004 05:40 EST
- mocking JDBC with jOOQ by Lukas Eder on August 01 2013 17:16 EDT
- jdbc mock antipattern by Michael Mahemoff on January 12 2004 19:25 EST
- is this a valid use? by nathan weisz on January 14 2004 10:41 EST
- is this a valid use? by Aslak Hellesøy on January 14 2004 15:42 EST
- yet another abuse of inheritance by Paul Campbell on January 16 2004 04:50 EST
- What about the following article ? by A A on January 28 2004 08:11 EST
-
Another Anti-Pattern: "Assert-Free Testing"[ Go to top ]
- Posted by: Paul Strack
- Posted on: January 12 2004 16:55 EST
- in response to Dion Almaer
Nice article. Another testing anti-pattern I have seen is "tests" that only check that code executes without generating any exceptions. I have seen a number of tests that simply call the tested method, then assume that if no exceptions were generated, the component must be behaving correctly: "If it didn't crash, it must be working".
A good symptom of this "Testing Exceptions Only" anti-pattern is tests that have no assert statements. I have also seen tests that dump method results into a log file, so that someone has to manually check the log file to ensure the methods are generating the expected results. That pretty much eliminates the advantage of automated testing. -
jdbc mock antipattern[ Go to top ]
- Posted by: Larry Sherrill
- Posted on: January 12 2004 18:03 EST
- in response to Dion Almaer
Another good example of a mock antipattern is to mock jdbc stuff. Using mock Connections, PreparedStatements, and ResultSets result in very brittle, white-box tests that only test if the PreparedStatement setters were called in the same way in which the mock object expectations were initialized in the first place. The circular validation logic goes like this: if A == A, then the system must be valid. Wrong. -
jdbc mock antipattern[ Go to top ]
- Posted by: Michael Mahemoff
- Posted on: January 12 2004 19:25 EST
- in response to Larry Sherrill
Another good example of a mock antipattern is to mock jdbc stuff.
Yep - most of the time this happens when people don't have a decent database abstraction, e.g. Hybernate or a custom DAO class. So they're doing JDBC directly from a Struts Action or session bean or whatever and the SQL that these kinds of objects spit out, which must be verified by the Mock, will indeed be brittle.
You could use a Mock to test the boundary itself, e.g. test a DAO with Mock JDBC classes, but this seems less useful - you still need to test the DAO against the real database.
A couple more mock antipatterns I've come across:
- Assuming a Mock is just a conventional "shunt" or "stub", i.e. an object that provides fake data. The focus of mocks seems to be what goes into them rather than what comes out. (Maybe this misconception is testimony to the term "mock" being much more evocative and meaningful than the plurality of older, vaguer, terms).
- Verifying only at the end of the test. I did this while learning the mock pattern with custom-built Mocks - I was assuming you tell it what to expect, run with the mock configured, and then ask the mock to verify the end-state. What's supposed to happen is the mock throws an exception as soon as it notices an expectation fails, so you get a stack trace directly to the point of failure, and don't waste any further time proceeding with the test run. Works like a charm. -
jdbc mock antipattern[ Go to top ]
- Posted by: Ara Abrahamian
- Posted on: January 13 2004 05:40 EST
- in response to Michael Mahemoff
<Michael Mahemoff>
you still need to test the DAO against the real database.
</Michael Mahemoff>
Yup. If you don't test the real DAO component you're not testing it at all.
So:
- always write a real test with a real db for all your DAOs.
- use mock DAOs anywhere else. You're safe because the DAO is already tested itself. Use mocks to easily setup tests and easily TDD the code.
Ara. -
jdbc mock antipattern[ Go to top ]
- Posted by: Praveen Peddi
- Posted on: January 13 2004 12:21 EST
- in response to Ara Abrahamian
I agree with Michael. I think mocking DB connections, prepared statements and testing with the dummy data is not very useful and it does not really test the method. A method dealing with the real data in the database should be tested with the real data.
> <Michael Mahemoff>
> you still need to test the DAO against the real database.
> </Michael Mahemoff>
>
> Yup. If you don't test the real DAO component you're not testing it at all.
>
> So:
> - always write a real test with a real db for all your DAOs.
> - use mock DAOs anywhere else. You're safe because the DAO is already tested itself. Use mocks to easily setup tests and easily TDD the code.
>
> Ara. -
jdbc mock antipattern[ Go to top ]
- Posted by: Aslak Hellesøy
- Posted on: January 13 2004 18:38 EST
- in response to Praveen Peddi
I agree with this with some reservations.
Let's assume you have a java method that deals with real data in an RDBMS (accessing it either via raw JDBC or via Hibernate or some other O/R mapping framework).
If you start testing that method against real data in the database, you're doing one of the following:
a) You're testing the JDBC driver or the O/R mapping framework.
b) You're testing that the data (or rather the structure) in the database is compatible with your Java datamodel.
Doing a) makes little sense. When you choose a JDBC driver or O/R framework, you are not testing your own code. You should simply assume that your the JDBC driver or O/R framework works. They should have their own tests.
Doing b) makes more sense, but not from a unit testing perspective. This is integration testing. When you do this, you are actually testing that your Java code is in sync with your database structure. I recommend doing this in a different test suite (an integration test suite). -
jdbc mock antipattern[ Go to top ]
- Posted by: Michael Mahemoff
- Posted on: January 13 2004 20:27 EST
- in response to Aslak Hellesøy
Doing a) makes little sense. When you choose a JDBC driver or O/R framework, you are not testing your own code. You should simply assume that your the JDBC driver or O/R framework works. They should have their own tests.
>
> Doing b) makes more sense, but not from a unit testing perspective. This is integration testing. When you do this, you are actually testing that your Java code is in sync with your database structure. I recommend doing this in a different test suite (an integration test suite).
Fair enough - running against a database is integration testing. You definitely need to do that. But given that you're doing that, I'm not sure if there is value in testing a DAO other than against a live database. You could have a mock object that verifies the SQL created by the DAO (which I think was the example in the original mock object paper). This would allow a completely isolated unit test. But doing this exhaustively this would slow down any changes to domain objects, due to overhead of maintaining the unit tests as well as the integration tests. Also, many different SQL queries are possible, so you can only know the correct one by peeking at the code - the test is somewhat circular.
Maybe there's another strategy for unit testing DAOs that I'm missing here? -
jdbc mock antipattern[ Go to top ]
- Posted by: Shorn Tolley
- Posted on: January 13 2004 23:21 EST
- in response to Michael Mahemoff
The focus of mocks seems to be what goes into them rather than what comes out.
I think that not only is that true, but that it is a *good* thing.
I remember reading a quote a loooong time ago, from (I think) one of the C-language guys (Kernighan & Ritchie) that went something like this:
"you're components should be pedantic about what they send, and liberal about what they accept".
Personally, I think it's mostly good advice and a large part of my personal unit testing is devoted to testing that this statement is true of components that I write.
Does anyone know the quote I am talking about? It was a long time ago and I tried googling for it but couldn't find anything.
Cheers,
Shorn. -
Postel's 'Law"[ Go to top ]
- Posted by: Robert Stine
- Posted on: January 15 2004 08:34 EST
- in response to Shorn Tolley
you're components should be pedantic about what they send, and liberal about what they accept"
That's a paraphrase of an old rule for data communications protocols. I remember it as "be liberal in what you accept but conservative in what you send."
I recently heard it referred to as "Postel's Law", a reference to the late Jon Postel, who was the author of many of the fundamental internet protocols and the keeper of the "assigned network numbers".
Postel said it, but the expression seems to me to be more of a directive or admonition than a law... -
mocking JDBC with jOOQ[ Go to top ]
- Posted by: Lukas Eder
- Posted on: August 01 2013 17:16 EDT
- in response to Larry Sherrill
You don't necessarily have to mock the JDBC API. You can use a more sophisticated API that allows to use a single callback method for all JDBC interactions. Check out how to mock JDBC with jOOQ:
http://blog.jooq.org/2013/02/20/easy-mocking-of-your-database/
-
is this a valid use?[ Go to top ]
- Posted by: nathan weisz
- Posted on: January 14 2004 10:41 EST
- in response to Dion Almaer
Forgive me for only glancing at the article, which probably has the answer to this, but is it valid to use mock objects to replace container-provided objects when testing outside of a container? Example, passing mock http request to servlet. -
is this a valid use?[ Go to top ]
- Posted by: Aslak Hellesøy
- Posted on: January 14 2004 15:42 EST
- in response to nathan weisz
Forgive me for only glancing at the article, which probably has the answer to this, but is it valid to use mock objects to replace container-provided objects when testing outside of a container? Example, passing mock http request to servlet.
That is indeed valid usage. I strongly recommend MockObjects or the newer but not quite as stable JMock libraries' support for dynamic mocks to do that. Example:
<pre>
// Create mock
Mock reqMock = new Mock(ServletRequest.class);
ServletRequest req = (ServletRequest) reqMock.proxy();
Mock resMock = new Mock(ServletResponse.class);
ServletResponse res = (ServletResponse) reqMock.proxy();
// Set up expectations.
// Expect that the class under test calls req.getAttribute("foo") and return "bar"
reqMock.expectAndReturn("getAttribute", "foo", "bar");
...
// Instantiate the class you want to test
Servlet s = new SomeServlet();
s.doPost(req, res);
// Verify that all expected methods were called.
reqMock.verify();
reqMock.verify();
Bear in mind that setting up all the expectations for requests and responses quickly becomes cumbersome. If you're using a web framework with better decoupling (like WebWork), you won't have to deal with all this servlet API goo. TDD/Dynamic Mocking with plain old Servlets and Struts is a pain.
</pre> -
yet another abuse of inheritance[ Go to top ]
- Posted by: Paul Campbell
- Posted on: January 16 2004 04:50 EST
- in response to Dion Almaer
TBH doing something this hideous wouldnt have occured to me in the first place. However as somone who didnt start out as an OO developer I tend not to use inheritance as a blunt-edged weapon for every problem. -
What about the following article ?[ Go to top ]
- Posted by: A A
- Posted on: January 28 2004 08:11 EST
- in response to Dion Almaer
http://www-106.ibm.com/developerworks/library/j-mocktest.html
Sounds exactly the case that matches this antipattern.
Am I right?
Also, is it THIS bad? Sometimes we want strong coupling between classes.
(Yes, I'm a newbie to TDD. Yes, I agree 90% with the TSS article. But ..
maybe we should not be fixed with a single perception of 'things must be like this, in any situation you will ever face').