Opinion: Anti-patterns with Mock Objects

Discussions

News: Opinion: Anti-patterns with Mock Objects

  1. Opinion: Anti-patterns with Mock Objects (14 messages)

    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)

  2. 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.
  3. jdbc mock antipattern[ Go to top ]

    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.
  4. jdbc mock antipattern[ Go to top ]

    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.
  5. jdbc mock antipattern[ Go to top ]

    <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.
  6. jdbc mock antipattern[ Go to top ]

    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.
  7. jdbc mock antipattern[ Go to top ]

    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).
  8. jdbc mock antipattern[ Go to top ]

    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?
  9. jdbc mock antipattern[ Go to top ]

    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.
  10. Postel's 'Law"[ Go to top ]

    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...
  11. mocking JDBC with jOOQ[ Go to top ]

    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/

  12. is this a valid use?[ Go to top ]

    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.
  13. is this a valid use?[ Go to top ]

    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>
  14. yet another abuse of inheritance[ Go to top ]

    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.
  15. What about the following article ?[ Go to top ]

    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').