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

  • #16
    Originally posted by Arno Werr View Post
    AOP, mixings, introspection, reflection just for simple sanity check. It's all hot and interesting but definitely is not my interpretation of kiss
    I would agree, couldn't we go for a simplified version. This usually works for me.

    #1. Want to create objects with messed up states. If collaborating objects actually change the state, ensure that these objects are injected. This means you can inject a test version in that will mess with the states for you.

    #2. Encapsulation you say, are you programming to interfaces? On your business object make all the getters default scope, atleast that way in your unit tests you can access them to make assertions. Ensure all of the code knows nothing about the actual domain object and only ever has references to the interface. If you ensure that the static factory methods return the interface as well, there should be very few direct calls to the REAL domain object.

    Comment


    • #17
      Originally posted by Arno Werr View Post
      AOP, mixings, introspection, reflection just for simple sanity check.
      It's all hot and interesting but definitely is not my interpretation of kiss
      True, its a bit too much for testing code. In fact all that AOP, mixins etc are not required unless you want these test interfaces. Assuming the domain entity has a business interface and has all setters and getters in only the implementation class, then the test code can simply cast the domain object to its implementation and directly call the accessors.
      Last edited by sabarish; Dec 15th, 2006, 05:57 PM.

      Comment


      • #18
        Originally posted by nlif View Post
        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.
        Sorry, but I'm not sure I understand this.
        If you have some private members that are always correctly initialized by the static factory and can't be changed because you don't provide setters for them, why would you want to set them to illegal values in your tests?
        If no code is able to set your properties to the illegal values why would you write tests for illegal values?

        Originally posted by nlif View Post
        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.
        In a similar way, if you don't have getters for some members why do you care if they are populated by Hibernate? Who will use those private members if they are not accessible? Do you have to populate them at all?

        Did I misunderstood something?


        Tamas

        Comment


        • #19
          Originally posted by szabtam View Post
          Sorry, but I'm not sure I understand this.
          If you have some private members that are always correctly initialized by the static factory and can't be changed because you don't provide setters for them, why would you want to set them to illegal values in your tests?
          If no code is able to set your properties to the illegal values why would you write tests for illegal values?

          In a similar way, if you don't have getters for some members why do you care if they are populated by Hibernate? Who will use those private members if they are not accessible? Do you have to populate them at all?

          Did I misunderstood something?
          I would agree with the first point. If there is no possible way of illegal data getting in there, what are you actually trying to test for?

          I think the second point is, although getters aren't supplied you still need to ensure you the correct internal state. Collaborators work with this data and the object uses it internally, you need to ensure its correct.

          Comment


          • #20
            Replies

            Originally posted by Arno Werr View Post
            AOP, mixings, introspection, reflection just for simple sanity check.
            It's all hot and interesting but definitely is not my interpretation of kiss
            I agree. That's why we decided to post a question in the forum. Whenever my solution starts to get complicated, I always suspect I am looking at things in the wrong way.

            Originally posted by szabtam View Post
            Sorry, but I'm not sure I understand this.
            If you have some private members that are always correctly initialized by the static factory and can't be changed because you don't provide setters for them, why would you want to set them to illegal values in your tests?
            If no code is able to set your properties to the illegal values why would you write tests for illegal values?
            Good point. I was waiting for someone to bring it up This argument is a good one, and it came up in our internal discussions as well. The counter-argument was, that I may want to test other components, that use my domain object as input, and verify that they can handle incorrect data. But I am not sure that the counter-argument is valid: after all, if there is absolutely no way to construct an object in a certain way, then nobody will ever get it as input...

            Originally posted by szabtam View Post
            In a similar way, if you don't have getters for some members why do you care if they are populated by Hibernate? Who will use those private members if they are not accessible? Do you have to populate them at all?
            I will explain: let's say that a domain object has a state property. This is saved in the database, and needs to be populated when the object is loaded by Hibernate. However the state is only used by the object itself, so there is no setter and no getter. The object transitions across its state-space in response to events, but no-one can change the state from outside. No one can query on the state , either. Or, at least, not using a getter (i.e. I can ask isFinished(), and get true or false, but not getState(). Therefore, there are no setter or getter, but there is a need to test that the Hibernate mapping is correct. I hope this clarifies it for you.

            Comment


            • #21
              Originally posted by nlif View Post
              Good point. I was waiting for someone to bring it up This argument is a good one, and it came up in our internal discussions as well. The counter-argument was, that I may want to test other components, that use my domain object as input, and verify that they can handle incorrect data. But I am not sure that the counter-argument is valid: after all, if there is absolutely no way to construct an object in a certain way, then nobody will ever get it as input...
              I don't think the counter-argument is valid.
              If you can't construct an object in a certain way then you can't, you don't need those tests ...

              Originally posted by nlif View Post
              I will explain: let's say that a domain object has a state property. This is saved in the database, and needs to be populated when the object is loaded by Hibernate. However the state is only used by the object itself, so there is no setter and no getter. The object transitions across its state-space in response to events, but no-one can change the state from outside. No one can query on the state , either. Or, at least, not using a getter (i.e. I can ask isFinished(), and get true or false, but not getState(). Therefore, there are no setter or getter, but there is a need to test that the Hibernate mapping is correct. I hope this clarifies it for you.
              Yes, this one is trickier.
              Do you have an isFinished()-like method for every state?
              If you do, you could use those methods to test the population.
              My point is that you should probably try to test through the object's public interface. Every test that knows about the implementation details of your class will break if you change the implementation.

              You might use the state just internally in the object, but your object probably acts in a different way in different states. There must be some noticable difference for the outside world, otherwise you wouldn't need the internal state. So could you test those differences visible for the outside world?


              Tamas

              Comment


              • #22
                Test behavior, not methods

                Tamas,

                Here's an interesting quote I found in JUnit Recipes/J.B.Rainsberger, which I think says the same, but in a more generalized form. Although we're getting just a tad bit philosophical here, I think this is an important principle. What's left, I guess, is just see how to apply it to concrete cases...

                1.3.2 Test behavior, not methods
                This brings us to another recommendation when writing tests: your tests should
                focus on the behavior they are testing without worrying about which class is under
                test. This is why our test names tend to be verbs rather than nouns: we test behavior
                (verbs) and not classes (nouns). Still, the difference between behavior and methods
                might not be clear: we implement behavior as methods, so testing behavior
                must be about test methods. But that’s not entirely true. We do implement behavior
                as methods, but the way we choose to implement a certain behavior depends on a
                variety of factors, some of which boil down to personal preference. We make a
                number of decisions when implementing behavior as methods: their names, their
                parameter lists, which methods are public and which are private, the classes on
                which we place the methods, and so on—these are some of the ways in which methods
                might differ, even though the underlying behavior might be the same. The
                implementation can vary in ways that the tests do not need to determine.
                Sometimes a single method implements all the required behavior, and in that
                case, testing that method directly is all you need. More complex behaviors require
                the collaboration of several methods or even objects to implement. If you let your
                tests depend too much on the particulars of the implementation, then you create
                work for yourself as you try to refactor (improve the design). Furthermore, some
                methods merely participate in a particular feature, rather than implement it. Testing
                those methods in isolation might be more trouble than it is worth. Doing so
                drives up the complexity of your test suite (by using more tests) and makes refactoring
                more difficult—and all for perhaps not much gain over testing the behavior
                at a slightly higher level. By focusing on testing behavior rather than each
                individual method, you can better strike a balance between test coverage and the
                freedom you need to support refactoring.

                Comment


                • #23
                  Philosophical Unit testing, sounds like a good book, I'd buy it .

                  Comment


                  • #24
                    Yes, the quote says the same thing and it is much more detailed...after all it is a book not just a post on a forum :-)

                    The idea isn't something new, it has been formulated by most of the guys involved in Test Driven Development.

                    Hopefully, you can apply the idea to your scenario...

                    Take care,

                    Tamas

                    Comment

                    Working...
                    X