Announcement Announcement Module
Collapse
No announcement yet.
Unit-Testing of Domain Objects and Encapsulation Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Unit-Testing of Domain Objects and Encapsulation

    Hello all,

    I would like to hear some opinions on an issue, on which we are somewhat undecided. While this is not a "spring issue" strictly speaking, it is a question that, in my opinion, should be interesting to anyone that tries to follow good OOD principles. I hope it's ok to bring it up in this forum.

    We try to design our domain model with proper encapsulation: make all members private, and provide getters and setters if, and only if, it is really required. For example, if the ID should be created upon instantiation, and should never be modified - then it is generated in the constructor, and there is no setter; If certain members should never be exposed, then there are no getters for them at all, so that only the class itself can modify them (thus ensuring it cannot be made to break its invariants).

    Somewhat ironically, we find that our own tests require us to break encapsulation. For example, in order to test our equals() method, we want to be able to mess with the ID; in order to verify an object was loaded from the database with all its fields, we need getters for all members. What are we to do? should we add to the domain-objects API, solely for testing purposes? Surely that couldn't be right! Give up on testing certain things? We don't want to do that either.

    I am certain others face similar issues, and I'd really be interested in hearing how you address them in your projects.

    Naaman

  • #2
    Have you tried these two books (my favorites actually)
    Code:
    JUnit Recipes
    Practical Methods for Programmer Testing
    J.B. RAINSBERGER
    with contributions by SCOTT STIRLING
    В2005 by Manning Publications
    
    JUnit in Action
    VINCENT MASSOL
    with TED HUSTED
    В2004 by Manning Publications
    Most probably you will find there answers to your troubles I did.

    For domain objects I am using mostly integration tests against lightweight and fully functional rdbms like PostgreSQL. Run the object
    in a test environment and then check whether db state changed according to my object specifications.

    For example, in order to test our equals() method, we want to be able to mess with the ID
    I am a big fan of Hibernate and JPA by extention. In Hibernate it is 'NO-NO!!!' to use ID in equals(). I am using fields which constitute business primary key of a record (not actual surrogate primary key which is object's id). These so called candidate primary key fields have both gets and sets. So, there's no problem for JUnit. In my view this is a sound strategy not only for Hibernate but across the board.
    Last edited by Arno Werr; Dec 14th, 2006, 09:53 AM.

    Comment


    • #3
      I am a big fan of Hibernate and JPA by extention. In Hibernate it is 'NO-NO!!!' to use ID in equals(). I am using fields which constitute business primary key of a record (not actual surrogate primary key which is object's id). These so called candidate primary key fields have both gets and sets. So, there's no problem for JUnit. In my view this is a sound strategy not only for Hibernate but across the board.
      There are lots of pages dedicated to this one, its a common discussion/argument .
      http://peter.jteam.nl/?p=10
      http://www.hibernate.org/109.html

      As for the rest, it is a difficult one. You want to hide things but at the same time you want them to be visible for tests. I don't see how you can really overcome the mismatch in any revolutionary way.

      If you want to make classes testable, you have to be able to get at the things you want to test. You just have to design for testing, at the same time you might have to compromise on something to allow it to be tested.

      I haven't read the books talked about but they have been recommended before, I'll have to check them out. Out of interest is there a clear winner between the two, or is it worth reading both of them? Running out of shelf space is all .
      Last edited by karldmoore; Dec 14th, 2006, 10:25 AM. Reason: added book preference question

      Comment


      • #4
        There are lots of pages dedicated to this one, its a common discussion/arguement
        Yep, it's common. And yet, as you see, some folks are trying to stick actual db-generated ids into their equals(). Not good...

        JUnit in Action is for rookies.
        JUnit Recipes (ISBN 1932394230) is a hard-core stuff. I feel it for you.
        Book shelf space. How about HD? It's Manning Publications. They have pdf versions. Pdfs do not collect dust
        Last edited by Arno Werr; Dec 14th, 2006, 10:31 AM.

        Comment


        • #5
          Hard-core testing, sounds like my kind of err..... fun . Pdf version doesn't give me the same sense of satisfaction when I put it on the book shelf. See all the books I've read and think "man if only some of this information would stay in my head".

          Comment


          • #6
            Originally posted by karldmoore View Post
            See all the books I've read and think "man if only some of this information would stay in my head".
            Same here, same here...

            Comment


            • #7
              Originally posted by Arno Werr View Post
              Same here, same here...
              Atleast I'm not the only one, I thought at 25 it might be my age, already on the way down . Thanks for the recommendation, I'll check it out.

              Comment


              • #8
                Thanks for the input

                Thanks for the input, folks.

                As for the books that were mentioned - I have read them both. They're both good books, and I agree that "Recipes" is the more advanced of the two.

                As for the need to compromise... well, I consider myself a fairly pragmatic guy, but this feels like selling-out on one of the tenets of OOD. I guess I'll do it if I have to, but I won't go down without a fight

                As for the id issue: I only gave the id issue as an example, and it is not the real topic here, as far as I am concerned. However, since it seems to have attracted some attention, I just want to clarify: I am very familiar with Hibernate's formal standing with regards to database ids and business ids. The id I was referring to *is* the business id, and, as such, it is assigned upon instantiation, and should not be changed later.

                Naaman
                Last edited by nlif; Dec 14th, 2006, 02:53 PM.

                Comment


                • #9
                  As for the need to compromise... well, I consider myself a fairly pragmatic guy, but this feels like selling-out on one of the tenets of OOD. I guess I'll do it if I have to, but I won't go down without a fight.
                  Object-oriented zealot are we? Hm... Same here, same here...
                  Like Papa Rod says:
                  OO design is more important than specific technologies, such as J2EE. We should try to avoid letting our technology choices, such as J2EE, constrain our ability to use true OO design.
                  Have you tried
                  Code:
                  Expert Spring MVC and Web Flow
                  Copyright  Apress 2006 by Seth Ladd, Darren Davison, Steven Devijver, and Colin Yates
                  ISBN-13 (pbk): 978-1-59059-584-8
                  ISBN-10 (pbk): 1-59059-584-X
                  There's Ch. 10 'Testing Spring MVC Applications' where guys are talking about testing domain objects in JUnit environment using jMock http://www.jmock.org
                  Having said that I still prefer integration testing for domain objects. Mine are usually buckets. Their business responsibilities are limited to domain validation only. These validation rules are captured by Hibernate validator annotations and therefore integration testing looks to me as the only viable way doing things.

                  Comment


                  • #10


                    Originally posted by Arno Werr View Post
                    Object-oriented zealot are we? Hm... Same here, same here...
                    Zealot? Me?

                    I would like to thing that I can be reasoned with, in the name of practicality. At the end of the day, we have an application to roll out, and it can't be "good design" if it just doesn't do what you want it to do, does it?

                    I've read Expert Spring MVC and Web Flow, and it's an excellent book. However, my problem really has nothing to do with Spring MVC, or Hibernate, or any framework for that matter. After all, isn't the whole philosophy behind POJOs and IoC that the code can exist with or without any technology/framework/container?

                    Originally posted by Arno Werr View Post
                    Having said that I still prefer integration testing for domain objects. Mine are usually buckets. Their business responsibilities are limited to domain validation only. These validation rules are captured by Hibernate validator annotations and therefore integration testing looks to me as the only viable way doing things.
                    We actually strive for non-anemic domain objects. That is - they also have behavior and business rules, so there is stuff to test.

                    Let me try to restate my question, more generally: how can we write tests that make assertions regarding an object's state, without compromising encapsulation? One idea we came up with, but haven't tried yet, is to use reflection in our test-code, to "open-up" the object for inspection. Has anyone tried this approach?

                    Naaman

                    Comment


                    • #11
                      Its funny you should talk about reflection, I was having the same discussion with a colleague about this thread. You could use it to do you testing, but it just feels so heavy handed.

                      The nice thing is if you are using injection you can plug-in test versions of the objects. It might help to have a concrete example to work with here and we could come up with a way of testing it.

                      Comment


                      • #12
                        One problem with reflection (apart from being heavy) is that you can't rely on the compiler to let you know when method signatures change.

                        And because its test code one would always want to work with the real object without adding proxies and stuff just for testing the object.

                        If you are ok with proxies, you could have additional test interfaces for these domain objects which are mixed in using AOP. And the objects themselves would have to implement the getters and setters, though their business interfaces need not necessarily expose them.

                        Comment


                        • #13
                          Examples

                          Originally posted by karldmoore View Post
                          It might help to have a concrete example to work with here and we could come up with a way of testing it.
                          Agreed. Let me try to describe some of the problems we are facing.

                          Example 1:

                          We decided not to provide public constructors, but instead, provide each class with a public static factory method as the only means of getting a new instance. This ensures that all objects are always constructed properly (e.g. we provide initial values for fields that must not be null etc.). Furthermore, we don't provide setters for the private members, because most of them should not be changed directly by other objects. However, in our tests we sometime want to create instances with messed-up state. We do this because we want to test our code against illegal, as well as legal, input.

                          Example 2:

                          As I may have mentioned, we use Hibernate. When we test our repositories (i.e. DAOs), we want to make sure all mapped properties were populated. However, we do not want to provide getters for all private members of the class, since this compromises encapsulation completely.

                          Originally posted by sabarish View Post
                          If you are ok with proxies, you could have additional test interfaces for these domain objects which are mixed in using AOP. And the objects themselves would have to implement the getters and setters, though their business interfaces need not necessarily expose them.
                          So we can provide getters, setters and constructors for the classes, but have everybody (except the tests) use them through interfaces, which are narrower. Interesting...

                          I'm not sure I understand why I would need AOP for this, though. Could you elaborate on that please?

                          Comment


                          • #14
                            Originally posted by nlif View Post
                            So we can provide getters, setters and constructors for the classes, but have everybody (except the tests) use them through interfaces, which are narrower. Interesting...
                            Exactly. So, though we are compromising on encapsulation, we can use the right layer specific abstractions (kind of different views of the same object) to keep things cleaner.

                            We need AOP or something similar because our domain objects are not going to implement these 'test interfaces'.

                            This is similar to the discussion that happened earlier on allowing presentation layer to access domain object getters and setters. So the ways to do it would be similar. Sergio's XT modeling framework, part of Spring Modules, may make it easier in case you are using JDK 5.0.

                            Comment


                            • #15
                              AOP, mixings, introspection, reflection just for simple sanity check.
                              It's all hot and interesting but definitely is not my interpretation of kiss

                              Comment

                              Working...
                              X