Skip to content
Open
47 changes: 33 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,48 @@

This project is used as the basis of a number of tutorials and exercises, as part of the *Professional Java Development and Test Automation Skills* program (see http://johnfergusonsmart.com/products). Each tutorial explores a different technique or practice essential to modern Java developers or Engineers in Test.

These tutorials are designed to be used as the basis of small coding exercises (similar to very short coding katas) that you can learn and practice until you are familiar with a particular technique. The approach is outlined here:
## Vet Clinic Tutorial 3 - Polymorphism

![Learning from the tutorials](src/documentation/images/tutorial-process.png)
In this exercise we will learn about using polymorphism in Java.

1. Clone this repository and check out the starting point for the tutorial you want to do.
2. Watch the tutorial and follow along on your own machine.
3. Redo the tutorial following the step-by-step instructions given in the tutorial's README file.
4. Redo the exercise without the instructions.
### Step 1 - Add a new test class
Create a new test class called WhenCollectingAnimals and add a test called `a_list_of_animals_can_contain_cats_and_dogs`. This class should illustrate how you can add a cat and a dog to a list of animals, of type `List<Animal>`. Create an empty Animal class, and make sure the test fails.

## The problem domain
### Step 2 - Cats and Dogs are Animals

The domain is a simple one. We are writing an application for a Vet clinic, where you can take your pets to be treated when they are sick. At the vet clinic, we need to be able to register new animals when they arrive for treatment.
Make the Cat and Dog class extend the Animal class. This should make your test pass.

## Starting a tutorial
### Step 3 - Adding a polymorphic method

Each tutorial has two main branches, one for the starting point for the tutorial, and one for a sample solution. The format for the branch names uses a simple naming convention to identify the starting point and the sample solutions for each tutorial. For example, to start tutorial 1, check out the `start` branch like this:
Create a new test called `a_dog_complains_by_growling` and make it pass. The assertion should look like this:
```
$ git checkout tutorial-1/start
assertThat(fido.complaint(), is(equalTo("Grrrr")));
```

And to see the solution for tutorial 1, use the solution branch:
### Step 4 - Cats meow

Create a new test called `a_cat_complains_by_meowing` and make it pass. The assertion should look like this:
```
assertThat(felix.complaint(), is(equalTo("Meow")));
```

### Step 5 - Animals complain

Add an abstract `complain()` method to the Animal class.

Demonstrate that this works in a test called `cats_and_dogs_complain_to_the_assistant`. Create a `ComplaintRegister` class and a `VetAssistant` class, and illustrate in your test that dogs and cats complain differently. For example:

```
$ git checkout tutorial-1/solution
...
ComplaintRegister complaintRegister = new ComplaintRegister();
VetAssistant assistant = new VetAssistant(complaintRegister);

assistant.recordComplainFrom(fido)
assistant.recordComplainFrom(felix)
assertThat(complaintRegister.getComplains(), hasItems("Grrrr", Meow");

```

Go to the tutorial branch to see the step-by-step instructions for that tutorial.
# Step 6 Vaccinations

Write a new test called `the_vet_should_know_when_a_pet_vaccination_is_due`. Assuming that dogs need vaccinations every 6 months and cats need vaccintions every 12 months, do this with polymorphism using interfaces, and by creating a `NeedsVaccination` interface.
10 changes: 10 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@
<version>3.1.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-all</artifactId>
<version>1.3</version>
</dependency>
</dependencies>
<build>
<plugins>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package serenitylabs.tutorials.vetclinic.domain;

public abstract class Animal {
public abstract String complaint();
}
71 changes: 71 additions & 0 deletions src/main/java/serenitylabs/tutorials/vetclinic/domain/Cat.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package serenitylabs.tutorials.vetclinic.domain;

import com.google.common.collect.ImmutableList;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

public class Cat extends Animal implements NeedsVaccinations{
private final String name;
private final String breed;
private final List<String> colour;
private LocalDateTime lastVaccinationDate;

public Cat(String name, String breed, List<String> colour) {

this.name = name;
this.breed = breed;
this.colour = colour;
}

@Override
public String toString() {
return name + " the " + (colour + " " + breed).toLowerCase();
}

public String getName() {
return name;
}

public String getBreed() {
return breed;
}

public List<String> getColour() {
return new ArrayList(colour);
}

public static CatBuilder called(String name) {
return new CatBuilder(name);
}

@Override
public String complaint() {
return "Meow";
}

public void wasVaccinated(LocalDateTime vaccinationDate) {
this.lastVaccinationDate = vaccinationDate;
}
public LocalDateTime nextVaccinationDue() {
return lastVaccinationDate.plusYears(1);
}
public static class CatBuilder {
private final String name;
private String breed;

public CatBuilder(String name) {
this.name = name;
}

public CatBuilder ofBreed(String breed) {
this.breed = breed;
return this;
}

public Cat andOfColour(String... colour) {
return new Cat(name, breed, ImmutableList.copyOf(colour));
}
}
}
74 changes: 74 additions & 0 deletions src/main/java/serenitylabs/tutorials/vetclinic/domain/Dog.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package serenitylabs.tutorials.vetclinic.domain;

import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

public class Dog extends Animal implements NeedsVaccinations {
private final String name;
private final String breed;
private final List<String> colour;
private LocalDateTime vaccinationDate;

public Dog(String name, String breed, List<String> colour) {

this.name = name;
this.breed = breed;
this.colour = colour;
}

@Override
public String toString() {
return name + " the " + (Joiner.on(" and").join(colour) + " " + breed).toLowerCase();
}

public String getName() {
return name;
}

public String getBreed() {
return breed;
}

public List<String> getColour() {
return new ArrayList(colour);
}

public static DogBuilder called(String name) {
return new DogBuilder(name);
}

@Override
public String complaint() {
return "Grrrr";
}
@Override
public void wasVaccinated(LocalDateTime vaccinationDate) {
this.vaccinationDate = vaccinationDate;
}
@Override
public LocalDateTime nextVaccinationDue() {
return vaccinationDate.plusMonths(6);
}

public static class DogBuilder {
private final String name;
private String breed;

public DogBuilder(String name) {
this.name = name;
}

public DogBuilder ofBreed(String breed) {
this.breed = breed;
return this;
}

public Dog andOfColour(String... colour) {
return new Dog(name, breed, ImmutableList.copyOf(colour));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package serenitylabs.tutorials.vetclinic.domain;

import java.time.LocalDateTime;

public interface NeedsVaccinations {
void wasVaccinated(LocalDateTime vaccinationDate);
LocalDateTime nextVaccinationDue();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package serenitylabs.tutorials.vetclinic.domain;

public class TotalConsultationPrice {
public static TotalConsultationPrice includingTax() {
return new TotalConsultationPrice();
}

public int forANetPriceOf(double netPrice) {
return (int) (netPrice * 1.20);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package serenitylabs.tutorials.vetclinic;

import com.google.common.base.Splitter;
import org.junit.Test;

import java.util.List;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.hasItems;
import static org.junit.Assert.assertThat;

public class BuilderExamples {
@Test
public void a_string_builder_concatenates_strings() {
// Given
StringBuilder stringBuilder = new StringBuilder();

// When
String deepThought = stringBuilder.append("The answer")
.append(" is ")
.append(42)
.toString();

// Then
assertThat(deepThought, equalTo("The answer is 42"));
}

@Test
public void a_guava_splitter() {
// When
List<String> names =
Splitter.on(",")
.omitEmptyStrings()
.trimResults()
.splitToList("Alfred, Billie,,Charlie ");

// Then
assertThat(names, hasItems("Alfred", "Billie", "Charlie"));
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package serenitylabs.tutorials.vetclinic.domain;

import java.util.ArrayList;
import java.util.List;

public class ComplaintRegister {
private final List<String> complaints = new ArrayList<>();
public List<String> getComplaints() {
return new ArrayList(complaints);
}
public void recordComplaint(String complain) {
complaints.add(complain);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package serenitylabs.tutorials.vetclinic.domain;

import java.util.ArrayList;

public class VetAssistant {
private final ComplaintRegister complaintRegister;
public VetAssistant(ComplaintRegister complaintRegister) {
this.complaintRegister = complaintRegister;
}
public void recordComplainFrom(Animal someAnimal) {
complaintRegister.recordComplaint(someAnimal.complaint());

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package serenitylabs.tutorials.vetclinic.domain;

import org.junit.Test;

import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertThat;

public class WhenCalculatingTotalPrices {

@Test
public void total_consultation_price_should_include_20_percent_tax() throws Exception {
// GIVEN
int netPrice = 100;

// WHEN
int totalPrice = TotalConsultationPrice.includingTax().forANetPriceOf(netPrice);

// THEN
assertThat(totalPrice, greaterThan(100));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package serenitylabs.tutorials.vetclinic.domain;

import org.assertj.core.api.Assertions;
import org.junit.Test;

import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasItems;
import static org.hamcrest.Matchers.is;

public class WhenCollectingAnimals {

@Test
public void aListOfAnimalsShouldContainCatsAndDogs(){
Dog fido = Dog.called("Fido").ofBreed("Poodle").andOfColour("White");
Cat tom = Cat.called("Tom").ofBreed("Siamese").andOfColour("Gray");

List<Animal> animalList = new ArrayList<>();
animalList.add(fido);
animalList.add(tom);

assertThat(animalList, hasItems(fido, tom));
}

@Test
public void aDogComplaintsByGrowling() {
Dog fido = Dog.called("Fido").ofBreed("Poodle").andOfColour("White");

assertThat(fido.complaint(), is("Grrrr"));
}

@Test
public void aCatComplaintsByMeowing(){
Cat tom = Cat.called("Tom").ofBreed("Siamese").andOfColour("Gray");

assertThat(tom.complaint(), is("Meow"));


}
}
Loading