Over a million developers have joined DZone.

Connascence of Value

· DevOps Zone

The DevOps zone is brought to you in partnership with Sonatype Nexus. The Nexus suite helps scale your DevOps delivery with continuous component intelligence integrated into development tools, including Eclipse, IntelliJ, Jenkins, Bamboo, SonarQube and more. Schedule a demo today

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.


The DevOps zone is brought to you in partnership with Sonatype Nexus. Use the Nexus Suite to automate your software supply chain and ensure you're using the highest quality open source components at every step of the development lifecycle. Get Nexus today

Topics:

Published at DZone with permission of Kevin Rutherford, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}