1. Cassandra
  2. CASSANDRA-741

Refactor for testability: Make class DatabaseDescriptor a real class with member methods, and non-static methods


    • Type: Improvement Improvement
    • Status: Resolved
    • Priority: Minor Minor
    • Resolution: Won't Fix
    • Fix Version/s: None
    • Component/s: Tools
    • Labels:


      I've stumbled across this issue when working on CASSANDRA-740 (Create InProcessCassandraServer for unit tests).

      I think the current way of how DatabaseDescriptor is implemented and used isn't great for testability perspective, readability and extensibility, here's why -

      The class has a giant static{} block that initializes all it's static data members. This static block is hard wired to read a file named storage-config.xml form a system defined property path.

      System.getProperty("storage-config") + File.separator + "storage-conf.xml";

      This isn't great for testing. What this means is that tests need to create the folder and put the file there (see scratch code inline the issue CASSANDRA-740).
      Tests would rather be able to configure the class from an InputStream or something like that. However, the fact that the class initializes itself autonomously isn't helping - a driver of the class cannot tell it to initialize itself from a different input stream. dependency injection is a typical solution to these cases.
      If the class had a constructor that accepts an InputStream (and an empty one that acts at the default, reading the storage-config.xml from the file system) that would be nicer for tests.
      It would actually be nicer not only for tests, but also for readability and extensibility purposes - it would make all dependencies explicit (see more on this below) and it would be possible to extend the class by inheriting and overriding some of its methods (when everything is static it's impossible.)

      Here's a mockup of my suggestion:

      public class DatabaseDescriptor
      public DatabaseDescriptor()

      { this(new FileInputStream(System.getProperty("storage-config") + File.separator + "storage-conf.xml"); }

      public DatabaseDescriptor(InputStream input)

      { // read xml from input }


      However, implementing what's suggested above, although the change is rather trivial, means changing all references to DatabaseDescriptor application wide... and there are more than 200 of those...
      It usually looks like this:

      public class RingCache
      final private int port_=DatabaseDescriptor.getThriftPort();
      final private static IPartitioner partitioner_ = DatabaseDescriptor.getPartitioner();

      The above code would have to be changed to:

      public class RingCache
      final private int port_;
      final private static IPartitioner partitioner_;

      public RingCache(DatabaseDescriptor descriptor) {
      port_ = descriptor.getThriftPort();
      partitioner_ = descriptor.getPartitioner();

      This means making the RingCache depend explicitly on DatabaseDescriptor since it requires it in its constructor (and not implicitly, via a static method) so this is good for being able to test the RingCache in a unit test and is also great for readability - no hidden dependency loops.

      Actually, since the RingCache depends only on the thrift port and the partitioner, but not an the entire database descriptor, it'll make even more sense to pass only the two in the constructor. This would again make unit tests easier and code easier to understand (you don't have to wonder "what is it in the database descriptor that I need to have in order to make the RingCache work?")

      public class RingCache
      final private int port_;
      final private static IPartitioner partitioner_;

      public RingCache(int thriftPort, IPartitioner partitioner) {
      port_ = thriftPort;
      partitioner_ = partitioner;

      The suggested change is a code quality improvement and testability improvement and should not affect the logic of the app.


        Ran Tavory created issue -
        Jonathan Ellis made changes -
        Field Original Value New Value
        Priority Major [ 3 ] Minor [ 4 ]
        Fix Version/s 0.6 [ 12314361 ]
        Component/s Tools [ 12312979 ]
        Component/s Core [ 12312978 ]
        Jonathan Ellis made changes -
        Status Open [ 1 ] Resolved [ 5 ]
        Resolution Won't Fix [ 2 ]
        Gavin made changes -
        Workflow no-reopen-closed, patch-avail [ 12489347 ] patch-available, re-open possible [ 12752065 ]
        Gavin made changes -
        Workflow patch-available, re-open possible [ 12752065 ] reopen-resolved, no closed status, patch-avail, testing [ 12755161 ]


          • Assignee:
            Ran Tavory
          • Votes:
            1 Vote for this issue
            2 Start watching this issue


            • Created: