Connascence of Value

Connascence is a way of describing the coupling between different parts of a codebase. And because it classifies the relative strength of that coupling, connascence can be used as a tool to help prioritise what should be refactored first. This is the first in a short series of posts in which I test-drive a well-known kata, attempting to use only connascence as my guide during refactoring.

Let’s tackle @pragdave‘s  classic Back to the Checkout kata in Java. My first test checks that we can scan a single item and calculate the total correctly:

public class CheckoutTests {

  @Test
  public void basicPrices() {
    Checkout checkout = new Checkout();
    checkout.scan("A");
    assertEquals(50, checkout.currentBalance());
  }
}

Now I make it pass in the simplest way I can think of:

class Checkout {
  public int currentBalance() {
    return 50;
  }

  public Checkout scan(String item) { }
}

Clearly these two classes are now coupled (if they weren’t, the test wouldn’t pass). But is that coupling good or bad?

I can see three four kinds of connascence between the test and the production code:

  1. Connascence of Name, because the test knows the names of the methods to call on the checkout object. This is level 1 (of 9) on the connascence scale — the weakest and least damaging form of coupling.
  2. Connascence of Type, because the test knows which class to instantiate. This is level 2 on the scale, and is thus also relatively benign.
  3. Connascence of Meaning, because both classes know that we are representing monetary values using ints. (I missed this first time around — d’oh!)
  4. Connascence of Value, because both the test and the Checkout know the price of item “A”:

cov1

The Connascence of Value here means that the tests will break if I change the price of item “A”; I definitely wouldn’t want to release this into production.

Connascence of Value is level 8 on the scale of 9 types of connascence. The scale defines seven weaker forms of coupling, and only one more serious kind. I can use that model to help me prioritise the Refactor step in my TDD cycle: Connascence of Value is a serious problem, and should be fixed before I do anything else. The only question is: how?

The first thing I note is that connascence is weaker with proximity, which means that either of the following options would be preferable:

cov2

Thus, if I can move knowledge of the price of “A” so that only one of my classes has it, then the effects of the coupling are greatly diminished.

I can get some help from SOLID here, because the Dependency Inversion Principle also tells me that this code has a problem. The DIP says that we should depend on abstractions, not on details. And yet here I have a test that only works due to its knowledge of one of the details inside the production code.

The DIP (and @jbrains) also tells me what to do next: I should move the detail up towards the tests. That means I need to change the Checkout so that the test injects the value 50 via a parameter. I could pass it in via the scan method:

public class CheckoutTests {

  @Test
  public void basicPrices() {
    Checkout checkout = new Checkout();
    checkout.scan("A", 50);
    assertEquals(50, checkout.currentBalance());
  }
}

Alternatively I could inject it via  the Checkout’s constructor:

public class CheckoutTests {

  @Test
  public void basicPrices() {
    Checkout checkout = new Checkout(50);
    checkout.scan("A");
    assertEquals(50, checkout.currentBalance());
  }
}

Either way, I have now removed the Connascence of Value between the Checkout and the test: I can change the price of item “A” by changing only one method.

The worst of the coupling is now gone, but I can do better. There is still Connascence of Value, albeit very localized, within that test method. Is it worth fixing?

I like my tests to be expressive and easy to read. I wouldn’t want to extract the value 50 to a constant, for example, because I would then have to scan up and down through the test class to discover exactly what the test was doing. But equally, that magic value 50 makes me a little nervous. Does it have business significance? Not in this case, and a new team member might not pick that up.

In cases such as this I like my tests to use random values, to help ensure that the code under test hasn’t made any unfortunate assumptions. So I replace that 50 with a call to a random price generator:

public class CheckoutTests {
  @Test
  public void basicPrices() {
    Checkout checkout = new Checkout();
    checkout.scan("A", randomPrice());
    assertEquals(randomPrice(), checkout.balance());
  }
}

But now the test is broken again, and that Connascence of Value is the guilty party, telling us that the two values need to be the same. I fix it by replacing the Connascence of Value by Connascence of Name:

public class CheckoutTests {
  @Test
  public void basicPrices() {
    Checkout checkout = new Checkout();
    int priceOfA = randomPrice();
    checkout.scan("A", priceOfA);
    assertEquals(priceOfA, checkout.balance());
  }
}

To summarise, I find connascence useful in guiding my refactoring efforts during the TDD cycle. In this case, I weakened the coupling between the code and test by pushing details up the call stack; then I removed the Connascence of Value altogether by replacing it with Connascence of Name.

In the next post I tackle more of the connascence in this code.

Advertisements

3 thoughts on “Connascence of Value

  1. Pingback: Connascence of Value: a different approach | silk and spinach

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s