Calculating mates with Java and TDD

17.10.2014 - Lesezeit ca. 19 Min - 4000 Wörter

Calculator implementation using TDD

In my first genetics article , I introduced you to the basic terminology and brought some examples. Now I would like to start with the implementation. As I already said, I want to get the job in Java. Since I am not familiar with it is a good way to practice a bit. And I’ll also use TDD.

First approach

I write a list of the first test cases, which I want to check. This is not complicated and is obtained from the table of possible combinations with a single locus. I show here the test cases’ table (Red background colour means the test is still incorrect, or not implemented. Green background colour means that the test is correct.):

Test Expected
Both parents are dominant homozygous All hatchlings are dominant homozygous
Both parents are recessive homozygous All hatchlings are recessive homozygous
One parent is dominant homozygous and the other is heterozygous 50% are dominant homozygous, the other 50% are heterozygous.
One parent is recessive homozygous and the other is heterozygous 50% are recessive homozygous, the other 50% heterozygous.
One parent is dominant homozygous and the other is recessive homozygous All hatchlings are heterozygous.
Both parents are heterozygous 25% are dominant homozygous, 50% are heterozygous and 25% are recessive homozygous.

So let’s look at the first test: I need a function to calculate the result of a breeding. In this case, the breeding of two dominant homozygous individuals. As already seen, we need the alleles pairs of both parents in order to perform the calculation. This should be the input data of our function. The expected result is the probability of the possible combinations of these alleles. And that’s what I would like to have as the return value.

The natural way to do this is something like:

 1public class BreedingCalculationTest {
 2
 3    @Test
 4    public void bothParentsDominantHomozygous_Returns100PerCentDominantHomozygous() {
 5        AllelePair parentalAllelePair = createDominantHomozygousMock("parental");
 6        AllelePair maternalAllelePair = createDominantHomozygousMock("maternal");
 7
 8        Map<String, Double> results = Breeding.calculateOutcome(
 9                parentalAllelePair, maternalAllelePair);
10
11        assertEquals(1.0, results.get("AA").doubleValue(), 0.001);
12    }
13        
14    private AllelePair createDominantHomozygousMock(String name) {
15        return CreateAllelePairMock(name, "A", "A");
16    }
17    
18    private AllelePair CreateAllelePairMock(String name, String firstAllele,
19            String secondAllele) {
20        AllelePair allelePair = mock(AllelePair.class, name);
21
22        when(allelePair.getFirstAllele()).thenReturn(firstAllele);
23        when(allelePair.getSecondAllele()).thenReturn(secondAllele);
24        return allelePair;
25    }
26}
As you see I’m using Mockito to mock the interfaces. The test doesn’t compile. I need to define the interface AllelePair and its two functions getFirstAllele() and getSecondAllele(). I also need the class Breeding and a static function calculateOutcome
 1public interface AllelePair {
 2
 3    String getFirstAllele();
 4
 5    String getSecondAllele();
 6
 7}
 8
 9public class Breeding {
10
11    public static Map<String, Double> calculateOutcome(
12            AllelePair parentalAllelePair, AllelePair maternalAllelePair) {
13
14        Map<String, Double> results = new HashMap<String, Double>();
15
16        results.put("AA", 1.0);
17
18        return results;
19    }
20}
As you can see, the test is a bit more complex, but only because of the mocks. Therefore, the implementation has been very easy. The first test is green immediately after the definition of the interface and the calculation function.

Now we take the next test. Both parents are homozygous recessive. The expected result is 100% homozygous recessive.

It actually looks very similar to the first test. The only difference is that instead of dominant, recessive is expected. Here’s the test:

 1@Test
 2public void bothParentsRecessiveHomozygous_Returns100PerCentRecessiveHomozygous() {
 3    AllelePair parentalAllelePair = createRecessiveHomozygousMock("parental");
 4    AllelePair maternalAllelePair = createRecessiveHomozygousMock("maternal");
 5
 6    Map<String, Double> results = Breeding.calculateOutcome(
 7            parentalAllelePair, maternalAllelePair);
 8
 9    assertEquals(1.0, results.get("aa").doubleValue(), 0.001);
10}
11
12private AllelePair createRecessiveHomozygousMock(String name) {
13    return CreateAllelePairMock(name, "a", "a");
14}
Logically, it fails. The simplest implementation I can think of is:
 1public static Map<String, Double> calculateOutcome(
 2        AllelePair parentalAllelePair, AllelePair maternalAllelePair) {
 3
 4    Map<String, Double> results = new HashMap<String, Double>();
 5
 6    if (parentalAllelePair.getFirstAllele() =="a") {
 7        results.put("aa", 1.0);
 8    }
 9    else {
10        results.put("AA", 1.0);
11    }
12    return results;
13}
And the second test is green. So we are making progress! I take a short time to think. Can I refactor anything? Well …, I use in the calculation function magic strings and I would like to avoid it.

Instead of aa I use parentalAllelePair.getFirstAllele () + parentalAllelePair.getSecondAllele ().

Interestingly, AA can be replaced by the same expression. Nicely, the refactoring even simplifies the function.

 1public static Map<String, Double> calculateOutcome(
 2        AllelePair parentalAllelePair, AllelePair maternalAllelePair) {
 3
 4    Map<String, Double> results = new HashMap<String, Double>();
 5
 6    results.put(
 7            parentalAllelePair.getFirstAllele()
 8                    + parentalAllelePair.getSecondAllele(), 1.0);
 9
10    return results;
11}
And the tests stay green.

There is a third case in which all hatchlings have the same genotype. It is the case in which one parents are dominant homozygous and the other one recessive homozygous. I write the test.

 1@Test
 2public void dominatHomozygousPlusRecessiveHomozygous_Returns100PerCentHeterozygous() {
 3    AllelePair parentalAllelePair = createDominantHomozygousMock("parental");
 4    AllelePair maternalAllelePair = createRecessiveHomozygousMock("maternal");
 5
 6    Map<String, Double> results = Breeding.calculateOutcome(
 7            parentalAllelePair, maternalAllelePair);
 8
 9    assertEquals(1.0, results.get("Aa").doubleValue(), 0.001);
10}
The test fails. This is a good sign, let me implement the needed functionality. The easiest way to do this is to leave the function as it is unless the parents are dominant homozygous and recessive homozygous. In this case, the function must return heterozygous.
 1public static Map<String, Double> calculateOutcome(
 2        AllelePair parentalAllelePair, AllelePair maternalAllelePair) {
 3
 4    Map<String, Double> results = new HashMap<String, Double>();
 5    if (parentalAllelePair.getFirstAllele() == "A"
 6            && parentalAllelePair.getSecondAllele() == "A"
 7            && maternalAllelePair.getFirstAllele() == "a"
 8            && maternalAllelePair.getSecondAllele() == "a") {
 9        results.put("Aa", 1.0);
10    } else {
11        results.put(parentalAllelePair.getFirstAllele()
12                + parentalAllelePair.getSecondAllele(), 1.0);
13    }
14
15    return results;
16}
The test works now, but I think this is an ugly implementation. Maybe, I should have seen it sooner. I’m interested in whether the alleles are dominant homozygous, recessive homozygous or heterozygous. To check that, it would be easier, if I would have corresponding functions in the interface and don’t have to ask for the individual alleles in the pair. So, I extend it.
 1public interface AllelePair {
 2
 3    String getFirstAllele();
 4
 5    String getSecondAllele();
 6
 7    boolean isDominantHomozygous();
 8
 9    boolean isRecessiveHomozygous();
10
11    boolean isHeterozygous();
12
13}
Obviously all test are still running. Now I make the changes step by step. First of all I change the functions to create the mocks in the test class.
 1private AllelePair createRecessiveHomozygousMock(String name) {
 2    AllelePair allelePair =  CreateAllelePairMock(name, "a", "a");
 3    when(allelePair.isRecessiveHomozygous()).thenReturn(true);
 4    return allelePair;
 5}
 6
 7private AllelePair createDominantHomozygousMock(String name) {
 8    AllelePair allelePair =  CreateAllelePairMock(name, "A", "A");
 9    when(allelePair.isDominantHomozygous()).thenReturn(true);
10    return allelePair;
11}
All tests still running. And I can now ask for it in the calculate function.
 1public static Map<String, Double> calculateOutcome(
 2        AllelePair parentalAllelePair, AllelePair maternalAllelePair) {
 3
 4    Map<String, Double> results = new HashMap<String, Double>();
 5    if (parentalAllelePair.isDominantHomozygous()
 6            && maternalAllelePair.isRecessiveHomozygous()) {
 7        results.put("Aa", 1.0);
 8    } else {
 9        results.put(parentalAllelePair.getFirstAllele()
10                + parentalAllelePair.getSecondAllele(), 1.0);
11    }
12
13    return results;
14}
The function now looks a little better. I replace also the magic string “Aa” with the expression formed with the parental and maternal alleles.
 1    public static Map<String, Double> calculateOutcome(
 2            AllelePair parentalAllelePair, AllelePair maternalAllelePair) {
 3
 4        Map<String, Double> results = new HashMap<String, Double>();
 5        if (parentalAllelePair.isDominantHomozygous()
 6                && maternalAllelePair.isRecessiveHomozygous()) {
 7            results.put(parentalAllelePair.getFirstAllele()
 8                    + maternalAllelePair.getFirstAllele(), 1.0);
 9        } else {
10            results.put(parentalAllelePair.getFirstAllele()
11                    + parentalAllelePair.getSecondAllele(), 1.0);
12        }
13
14        return results;
15    }
The last test examined the offspring of a dominant homozygous father and a recessive homozygous mother. If the father is recessive homozygous and the mother dominant homozygous, the same result should come out, shouldn’t it? I’ll write the test and we’ll see. I will also rename the last test.
 1@Test
 2public void fatherDominantHomozygous_MotherRecessiveHomozygous_ReturnsOnlyHeterozygous() {
 3    AllelePair parentalAllelePair = createDominantHomozygousMock("parental");
 4    AllelePair maternalAllelePair = createRecessiveHomozygousMock("maternal");
 5
 6    Map<String, Double> results = Breeding.calculateOutcome(
 7            parentalAllelePair, maternalAllelePair);
 8
 9    assertEquals(1.0, results.get("Aa").doubleValue(), 0.001);
10}
11
12@Test
13public void motherDominantHomozygous_FatherRecessiveHomozygous_ReturnsOnlyHeterozygous() {
14    AllelePair parentalAllelePair = createRecessiveHomozygousMock("parental");
15    AllelePair maternalAllelePair = createDominantHomozygousMock("maternal");
16
17    Map<String, Double> results = Breeding.calculateOutcome(
18            parentalAllelePair, maternalAllelePair);
19
20    assertEquals(1.0, results.get("Aa").doubleValue(), 0.001);
21}
The old test runs, the new one fails. But why?

Well, the check is made in the implementation of the function. Currently the order of the input parameters is important for the calculation. I have to check both combinations in order to make this irrelevant.

 1public static Map<String, Double> calculateOutcome(
 2        AllelePair parentalAllelePair, AllelePair maternalAllelePair) {
 3
 4    Map<String, Double> results = new HashMap<String, Double>();
 5    if (parentalAllelePair.isDominantHomozygous()
 6            && maternalAllelePair.isRecessiveHomozygous()) {
 7        results.put(parentalAllelePair.getFirstAllele()
 8                + maternalAllelePair.getFirstAllele(), 1.0);
 9    } 
10    else  if (maternalAllelePair.isDominantHomozygous()
11            && parentalAllelePair.isRecessiveHomozygous()){
12        results.put(maternalAllelePair.getFirstAllele()
13                + parentalAllelePair.getSecondAllele(), 1.0);
14    }
15    else {
16            results.put(parentalAllelePair.getFirstAllele()
17                    + parentalAllelePair.getSecondAllele(), 1.0);
18        }
19
20    return results;
21}
The tests are now green, but the function looks ugly again. The if queries make the function illegible and I have considered just the half of the cases. If I continue like this, it will look increasingly worse.

What can I do?

In my actual tests I combine two AllelePair items to calculate its outcome. At the moment I use a static calculate function to do this and there are six different combinations of the three possible AllelePair (remember: dominant homozygous, heterozygous and recessive homozygous).

What if an AllelePair is able to combine itself with another AllelPair? Interesting approach. If I’m able to do something like:

1 Map<String, Double> results = paternalAllelePair.CombineWith(maternalAllelePair);
It may be easier to combine, doesn’t it? Well, not really. The CombineWith function must check the parental allele pair and the maternal one, so it must check the same things as before.

But what if we have three different implementations of AllelePair, one for each of the different allele pair types? Yes it will help, let me refactor.

In order to allow this I need a new function in my interface:

 1public interface AllelePair {
 2
 3    String getFirstAllele();
 4
 5    String getSecondAllele();
 6
 7    boolean isDominantHomozygous();
 8
 9    boolean isRecessiveHomozygous();
10
11    boolean isHeterozygous();
12    
13    Map<String, Double> combineWith(AllelePair otherAllele);
14
15}
The tests are still running.

And now I want to create an abstract class named AllelPairImpl that implements the interface AllelePair.

 1public abstract class AllelePairImpl implements AllelePair {
 2
 3    private String firstAllele;
 4    private String secondAllele;
 5
 6    public AllelePairImpl (String firstAllele, String secondAllele) {
 7        this.firstAllele = firstAllele;
 8        this.secondAllele = secondAllele;
 9    }
10    
11    @Override
12    public String getFirstAllele() {
13        return firstAllele;
14    }
15
16    @Override
17    public String getSecondAllele() {
18        return secondAllele;
19    }
20
21    @Override
22    public boolean isDominantHomozygous() {
23        return false;
24    }
25
26    @Override
27    public boolean isRecessiveHomozygous() {
28        return false;
29    }
30
31    @Override
32    public boolean isHeterozygous() {
33        return false;
34    }
35
36    @Override
37    public abstract Map<String, Double> CombineWith(AllelePair otherAllele);
38
39}
As you see I have declared the combineWith function abstract and all the is… returns false. The combineWith function should be implemented by the inherited classes. It should also override the needed is… function.

The tests still run.

Now I write a new failing test for the dominant homozygous + dominant homozygous case using the combineWith function:

 1@Test
 2public void combineDominantHomozygousWithDominantHomozygousReturnsOnlyDominantHomozygous() {
 3    AllelePair paternalAllelePair = new DominantHomozygous("A");
 4    AllelePair maternalAllelePair = new DominantHomozygous("A");
 5    
 6    Map<String, Double> results = paternalAllelePair.combineWith(maternalAllelePair);
 7    
 8    assertEquals(1.0, results.get("AA").doubleValue(), 0.001);
 9
10}
This test fails, the class DominantHomozygous doesn’t exist. I create it inheriting from AllelePairImpl.
 1public class DominantHomozygous extends AllelePairImpl implements AllelePair {
 2
 3    public DominantHomozygous(String allele) {
 4        super(allele.toUpperCase(), allele.toUpperCase());
 5    }
 6
 7    @Override
 8    public boolean isDominantHomozygous() {
 9        return true;
10    }
11
12    @Override
13    public Map<String, Double> combineWith(AllelePair otherAllele) {
14        Map<String, Double> results = new HashMap<String, Double>();
15        
16        results.put(getFirstAllele()
17                + getSecondAllele(), 1.0);
18
19        
20        return results;
21    }
22}
I override the isDominantHomozygous function to return true. And write a default implementation for the combineWith function.

After that my test is green.

The next step is to write a failing test for recessive homozygous + recessive homozygous:

 1@Test
 2public void combineRecessiveHomozygousWithRecessiveHomozygousReturnsOnlyRecessiveHomozygous() {
 3    AllelePair paternalAllelePair = new RecessiveHomozygous("A");
 4    AllelePair maternalAllelePair = new RecessiveHomozygous("A");
 5    
 6    Map<String, Double> results = paternalAllelePair.combineWith(maternalAllelePair);
 7    
 8    assertEquals(1.0, results.get("aa").doubleValue(), 0.001);
 9
10}
and the needed RecessiveHomozygous class.
 1public class RecessiveHomozygous extends AllelePairImpl implements AllelePair {
 2
 3    public RecessiveHomozygous(String allele) {
 4        super(allele.toLowerCase(), allele.toLowerCase());
 5    }
 6
 7    @Override
 8    public boolean isRecessiveHomozygous() {
 9        return true;
10    }
11
12    @Override
13    public Map<String, Double> combineWith(AllelePair otherAllele) {
14        Map<String, Double> results = new HashMap<String, Double>();
15        
16        results.put(getFirstAllele()
17                + getSecondAllele(), 1.0);
18
19        
20        return results;
21    }
22
23}
Here I override the isRecessiveHomozygous function to return true. The implementation of the combineWith function returns always the same value.

And my second test is also green.

The third test that I’ve coded in the last session was the dominant homozygous + recessive homozygous one. I write it now again:

 1@Test
 2public void combineDominantHomozygousWithRecessiveHomozygousReturnsOnlyHeterozygous() {
 3    AllelePair paternalAllelePair = new DominantHomozygous("A");
 4    AllelePair maternalAllelePair = new RecessiveHomozygous("A");
 5    
 6    Map<String, Double> results = paternalAllelePair.combineWith(maternalAllelePair);
 7    
 8    assertEquals(1.0, results.get("Aa").doubleValue(), 0.001);
 9
10}
As expected it fails, I need to modify the combineWith function of DominantHomozygous in order to return the right value (100% heterozygous).
 1@Override
 2public Map<String, Double> combineWith(AllelePair otherAllele) {
 3    Map<String, Double> results = new HashMap<String, Double>();
 4    if (otherAllele.isRecessiveHomozygous()){
 5        results.put(getFirstAllele()
 6                + otherAllele.getSecondAllele(), 1.0);
 7    }
 8    else {
 9        results.put(getFirstAllele()
10                + getSecondAllele(), 1.0);
11    }
12    
13    return results;
14}
The easiest way is to check if the other allele is recessive homozygous. if it is, then return 100% recessive homozygous, otherwise return 100% dominant homozygous.

And the test is green.

Obviously I write now the test for the fourth test case of the last article. The recessive homozygous + dominant homozygous. It should also return 100% heterozygous.

 1@Test
 2public void combineRecessiveHomozygousWithDominantHomozygousReturnsOnlyHeterozygous() {
 3    AllelePair paternalAllelePair = new RecessiveHomozygous("A");
 4    AllelePair maternalAllelePair = new DominantHomozygous("A");
 5    
 6    Map<String, Double> results = paternalAllelePair.combineWith(maternalAllelePair);
 7    
 8    assertEquals(1.0, results.get("Aa").doubleValue(), 0.001);
 9
10}
Here I need to change the implementation of the combineWith function, but this time in the RecessiveHomozygous class.
 1@Override
 2public Map<String, Double> combineWith(AllelePair otherAllele) {
 3    Map<String, Double> results = new HashMap<String, Double>();
 4    
 5    if (otherAllele.isDominantHomozygous()){
 6        results.put(otherAllele.getFirstAllele()
 7                + getSecondAllele(), 1.0);
 8    }
 9    else {
10        results.put(getFirstAllele()
11                + getSecondAllele(), 1.0);
12    }
13    
14    return results;
15}
Note that in this implementation I ask if the other allele is dominant homozygous. And I also create the string using another expression to get “Aa” (the expected string for an heterozygous allele)

The test is green.

At this point I have the same results as in my last article, and I don’t need
the old calculate function any more, also the old tests are obsolete and I delete all this stuff.

I have now more classes as I had, but they are now clear. I will think about the names of the classes. Maybe they are now not so good as it could be, but at the moment I don’t have better ones.

Now I have tested three of the cases that I wanted to test. I will show you the list of cases again, so we can see the progress better:

Test Expected
Both parents are dominant homozygous All hatchlings are dominant homozygous
Both parents are recessive homozygous All hatchlings are recessive homozygous
One parent is dominant homozygous and the other is heterozygous 50% are dominant homozygous, the other 50% are heterozygous.
One parent is recessive homozygous and the other is heterozygous 50% are recessive homozygous, the other 50% heterozygous.
One parent is dominant homozygous and the other is recessive homozygous All hatchlings are heterozygous.
Both parents are heterozygous 25% are dominant homozygous, 50% are heterozygous and 25% are recessive homozygous.

All tests till now have a single type result. The other tests return multiple results. For example, the mating between dominant homozygous and heterozygous produces an offspring, which may be dominant homozygous or heterozygous. This is the next case that I want to implement. First, I’ll check to see that 50% are dominant homozygous and in a second test that the remaining 50% are heterozygous. The first of these is:

 1@Test
 2public void combineDominantHomozygousWithHeterozygousReturnsFiftyPercentDominantHomozygous() {
 3    AllelePair paternalAllelePair = new DominantHomozygous("A");
 4    AllelePair maternalAllelePair = new Heterozygous("A");
 5    
 6    Map<String, Double> results = paternalAllelePair.combineWith(maternalAllelePair);
 7    
 8    assertEquals(0.5, results.get("AA").doubleValue(), 0.001);
 9
10}
First of all I need a new class named Heterozygous, as I needed DominantHomozygous and RecessiveHomozygous before. I implement it the same way.
 1public class Heterozygous extends AllelePairImpl implements AllelePair {
 2
 3    public Heterozygous(String allele) {
 4        super(allele.toUpperCase(), allele.toLowerCase());
 5    }
 6
 7    @Override
 8    public boolean isHeterozygous() {
 9        return true;
10    }
11
12    @Override
13    public Map<String, Double> combineWith(AllelePair otherAllele) {
14        return null;
15    }
16
17}
At the moment I don’t need the implementation of combineWith so it returns null.

The test still fails. Now I must change the implementation of the combineWith function in DominantHomozygous to pass the test. It means that if the other allele is heterozygous then I add 50% dominant homozygous in my map.

 1@Override
 2public Map<String, Double> combineWith(AllelePair otherAllele) {
 3    Map<String, Double> results = new HashMap<String, Double>();
 4    if (otherAllele.isRecessiveHomozygous()){
 5        results.put(getFirstAllele()
 6                + otherAllele.getSecondAllele(), 1.0);
 7    }
 8    else {
 9        if (otherAllele.isHeterozygous()) {
10            results.put(getFirstAllele()
11                    + getSecondAllele(), 0.5);
12        
13        }
14        else {
15            results.put(getFirstAllele()
16                    + getSecondAllele(), 1.0);
17        }
18   }
19    
20    return results;
21}
and my test passes. Now I need to test the second part. The result should also contain 50% heterozygous. I also write the implementation.
 1@Test
 2public void combineDominantHomozygousWithHeterozygousReturnsFiftyPercentHeterozygous() {
 3    AllelePair paternalAllelePair = new DominantHomozygous("A");
 4    AllelePair maternalAllelePair = new Heterozygous("A");
 5    
 6    Map<String, Double> results = paternalAllelePair.combineWith(maternalAllelePair);
 7    
 8    assertEquals(0.5, results.get("Aa").doubleValue(), 0.001);
 9
10}
11
12@Override
13public Map<String, Double> combineWith(AllelePair otherAllele) {
14    Map<String, Double> results = new HashMap<String, Double>();
15    if (otherAllele.isRecessiveHomozygous()){
16        results.put(getFirstAllele()
17                + otherAllele.getSecondAllele(), 1.0);
18    }
19    else {
20        if (otherAllele.isHeterozygous()) {
21            results.put(getFirstAllele()
22                    + getSecondAllele(), 0.5);
23            results.put(getFirstAllele()
24                    + otherAllele.getSecondAllele(), 0.5);
25        
26        }
27        else {
28            results.put(getFirstAllele()
29                    + getSecondAllele(), 1.0);
30        }
31   }
32    
33    return results;
34}
As you see I only added a new item to the result map with the expected 50% heterozygous and all the tests are green again.

Now I want to test the same case but with interchanged parental an maternal alleles. It means I implement the same two test, but now I combine heterozygous + dominant homozygous. The results should be the same.

Here are the two test:

 1@Test
 2public void combineHeterozygousWithDominantHomozygousReturnsFiftyPercentDominantHomozygous() {
 3    AllelePair paternalAllelePair = new Heterozygous("A");
 4    AllelePair maternalAllelePair = new DominantHomozygous("A");
 5    
 6    Map<String, Double> results = paternalAllelePair.combineWith(maternalAllelePair);
 7    
 8    assertEquals(0.5, results.get("AA").doubleValue(), 0.001);
 9
10}
11
12@Test
13public void combineHeterozygousWithDominantHomozygousReturnsFiftyPercentHeterozygous() {
14    AllelePair paternalAllelePair = new Heterozygous("A");
15    AllelePair maternalAllelePair = new DominantHomozygous("A");
16    
17    Map<String, Double> results = paternalAllelePair.combineWith(maternalAllelePair);
18    
19    assertEquals(0.5, results.get("Aa").doubleValue(), 0.001);
20
21}
Both fail and this time I need to implement the combineWith function in the Heterozygous class. As usually I take the easiest implementation I can.
1@Override
2public Map<String, Double> combineWith(AllelePair otherAllele) {
3    Map<String, Double> results = new HashMap<String, Double>();
4    results.put(getFirstAllele() + getSecondAllele(), 0.5);
5    results.put(getFirstAllele() + otherAllele.getSecondAllele(), 0.5);
6    return results;
7
8}
Next I write the recessive homozygous + heterozygous case. I am now writing this test and their implementation quickly because it actually have the same schema as the last test / implementation.
 1@Test
 2public void combineRecessiveHomozygousWithHeterozygousReturnsFiftyPercentRecessiveHomozygous() {
 3    AllelePair paternalAllelePair = new RecessiveHomozygous("A");
 4    AllelePair maternalAllelePair = new Heterozygous("A");
 5
 6    Map<String, Double> results = paternalAllelePair
 7            .combineWith(maternalAllelePair);
 8
 9    assertEquals(0.5, results.get("aa").doubleValue(), 0.001);
10
11}
12
13@Test
14public void combineRecessiveHomozygousWithHeterozygousReturnsFiftyPercentHeterozygous() {
15    AllelePair paternalAllelePair = new RecessiveHomozygous("A");
16    AllelePair maternalAllelePair = new Heterozygous("A");
17
18    Map<String, Double> results = paternalAllelePair
19            .combineWith(maternalAllelePair);
20
21    assertEquals(0.5, results.get("Aa").doubleValue(), 0.001);
22
23}
24
25@Test
26public void combineHeterozygousWithRecessiveHomozygousReturnsFiftyPercentRecessiveHomozygous() {
27    AllelePair paternalAllelePair = new Heterozygous("A");
28    AllelePair maternalAllelePair = new RecessiveHomozygous("A");
29
30    Map<String, Double> results = paternalAllelePair
31            .combineWith(maternalAllelePair);
32
33    assertEquals(0.5, results.get("aa").doubleValue(), 0.001);
34
35}
36
37@Test
38public void combineHeterozygousWithRecessiveHomozygousReturnsFiftyPercentHeterozygous() {
39    AllelePair paternalAllelePair = new Heterozygous("A");
40    AllelePair maternalAllelePair = new RecessiveHomozygous("A");
41
42    Map<String, Double> results = paternalAllelePair
43            .combineWith(maternalAllelePair);
44
45    assertEquals(0.5, results.get("Aa").doubleValue(), 0.001);
46
47}
The combineWith function from DominantHomozigous doesn’t change.

The combineWith function from RecessiveHomozigous is:

 1@Override
 2public Map<String, Double> combineWith(AllelePair otherAllele) {
 3    Map<String, Double> results = new HashMap<String, Double>();
 4
 5    if (otherAllele.isDominantHomozygous()) {
 6        results.put(otherAllele.getFirstAllele() + getSecondAllele(), 1.0);
 7    } else {
 8        if (otherAllele.isHeterozygous()) {
 9            results.put(getFirstAllele() + getSecondAllele(), 0.5);
10            results.put(otherAllele.getFirstAllele() + getSecondAllele(),
11                    0.5);
12        } else {
13            results.put(getFirstAllele() + getSecondAllele(), 1.0);
14        }
15    }
16
17    return results;
18}
And the combineWith function from Heterozygous is:
 1@Override
 2public Map<String, Double> combineWith(AllelePair otherAllele) {
 3    Map<String, Double> results = new HashMap<String, Double>();
 4    if (otherAllele.isDominantHomozygous()) {
 5        results.put(getFirstAllele() + getSecondAllele(), 0.5);
 6        results.put(getFirstAllele() + otherAllele.getSecondAllele(), 0.5);
 7    } else {
 8        if (otherAllele.isRecessiveHomozygous()) {
 9            results.put(getFirstAllele() + getSecondAllele(), 0.5);
10            results.put(
11                    otherAllele.getFirstAllele()
12                            + otherAllele.getSecondAllele(), 0.5);
13        }
14    }
15    return results;
16
17}
After this changes all test are running and only one case must be implemented. The heterozygous + heterozygous case.

This case return three different possible combinations: 50% heterozygous, 25% dominant homozygous and 25% recessive homozygous. So I write the three tests and modify the combineWith function in the Heterozygous class.

 1@Test
 2public void combineHeterozygousWithHeterozygousReturnsFiftyPercentHeterozygous() {
 3    AllelePair paternalAllelePair = new Heterozygous("A");
 4    AllelePair maternalAllelePair = new Heterozygous("A");
 5
 6    Map<String, Double> results = paternalAllelePair
 7            .combineWith(maternalAllelePair);
 8
 9    assertEquals(0.5, results.get("Aa").doubleValue(), 0.001);
10
11}
12
13@Test
14public void combineHeterozygousWithHeterozygousReturnsTwentyFivePercentDominantHomozygous() {
15    AllelePair paternalAllelePair = new Heterozygous("A");
16    AllelePair maternalAllelePair = new Heterozygous("A");
17
18    Map<String, Double> results = paternalAllelePair
19            .combineWith(maternalAllelePair);
20
21    assertEquals(0.25, results.get("AA").doubleValue(), 0.001);
22
23}
24
25@Test
26public void combineHeterozygousWithHeterozygousReturnsTwentyFivePercentRecessiveHomozygous() {
27    AllelePair paternalAllelePair = new Heterozygous("A");
28    AllelePair maternalAllelePair = new Heterozygous("A");
29
30    Map<String, Double> results = paternalAllelePair
31            .combineWith(maternalAllelePair);
32
33    assertEquals(0.25, results.get("aa").doubleValue(), 0.001);
34
35}
36
37@Override
38public Map<String, Double> combineWith(AllelePair otherAllele) {
39    Map<String, Double> results = new HashMap<String, Double>();
40    if (otherAllele.isDominantHomozygous()) {
41        results.put(getFirstAllele() + getSecondAllele(), 0.5);
42        results.put(getFirstAllele() + otherAllele.getSecondAllele(), 0.5);
43    } else {
44        if (otherAllele.isRecessiveHomozygous()) {
45            results.put(getFirstAllele() + getSecondAllele(), 0.5);
46            results.put(
47                    otherAllele.getFirstAllele()
48                            + otherAllele.getSecondAllele(), 0.5);
49        } else {
50            results.put(getFirstAllele() + getSecondAllele(), 0.5);
51            results.put(getFirstAllele() + getSecondAllele().toUpperCase(),
52                    0.25);
53            results.put(getFirstAllele().toLowerCase() + getSecondAllele(),
54                    0.25);
55        }
56    }
57    return results;
58
59}
At this point all the test that I wanted to proof are written and run to green.

Test Expected
Both parents are dominant homozygous All hatchlings are dominant homozygous
Both parents are recessive homozygous All hatchlings are recessive homozygous
One parent is dominant homozygous and the other is heterozygous 50% are dominant homozygous, the other 50% are heterozygous.
One parent is recessive homozygous and the other is heterozygous 50% are recessive homozygous, the other 50% heterozygous.
One parent is dominant homozygous and the other is recessive homozygous All hatchlings are heterozygous.
Both parents are heterozygous 25% are dominant homozygous, 50% are heterozygous and 25% are recessive homozygous.

Is this the end? No, it isn’t. I still have some points which need to be addressed:


If you enjoyed reading the article, I would be glad if you share it. :-)