Ports have Ships - Walkthrough
Steps
- Discuss in pairs how the domain model now looks.
- Create a new test spec for a
Port
addShip
method. - Write the code that makes this test pass.
- Create a new test spec for a
Port
removeShip
method. - Write the code that makes this test pass.
- Create a new test
Ship > gets added to port on instantiation
in theShip
test suite. You'll need to checkship.currentPort.ships
to see if it contains yourShip
instance. - Write the code to make this pass. The
Ship
constructor will need to call the starting port's (this.currentPort
)addShip
method, passing itself in (remember the current instance is referred to withthis
). - Add an extra assertion to the test for
Ship > can dock at a different port
to test that theShip
'scurrentPort
'sships
contains theShip
instance. You'll likely want to assert onship.currentPort.ships
. - Write the code that makes this test pass.
- Modify the test for
Ship > can set sail
to test that theShip
's previouscurrentPort
(you can useindexOf
onship.itinerary
to find this) no longer contains theShip
instance on itsships
property (something likepreviousPort.ships
). - Write the code that makes this test pass.
- Add, commit with a meaningful message, and push to GitHub.
Domain Model
The domain model might now look like this:
Object | Methods | Properties |
---|---|---|
Ship | setSail | currentPort |
dock | ||
Port | addShip | |
removeShip | ||
Itinerary | ports |
Requirements aren't always as specific as we would like. We could have a method on a Port
prototype called trackShips
but when we think about tracking ships in real life, we mean a port operations manager wants to see them come and go. We know from this user story that a port possesses ships. Therefore, we assume that to keep track of ships, we will need to be able to add and remove them from that ships array.
Create a new test spec for a Port
addShip
method
You'll notice here that we've not used the Ship
constructor for creating a Ship
instance, but rather an object literal. The reason is because we've already tested Ship
in the ship test suite. Here we're just checking that a port can store a collection of entities - at the moment we don't even need to be concerned with the interface of the object we add to a port. When we do need to be concerned with the interface then we can use mocks, which will be covered in next week's walkthrough.
Remember also object literals construct new unique objects. If we did this:
Then there are 2 seperate objects stored in memory.
This test should fail with:
Write the code that makes this test pass
:exclamation: You should be able to do this without the walkthrough. Remember, you want to have the ability to store multiple items on a Port
object, and the ability to add to that collection of items. Once you've passed the test, then proceed.
Create a new test spec for a Port
removeShip
method.
It should fail with:
Write the code that makes this test pass
:exclamation: You should be able to do this without the walkthrough (you'll want to look into how to remove items from an array for this one). Once you've passed the test, then proceed.
Create new test Ship > gets added to port on instantiation
in the Ship
test suite
Here we create a Port
, which gets passed to an Itinerary
that gets passed to Ship
. We expect that the Port
instance that eventually ends up being made available to the Ship
after this flow, will have it's addShip
method called, and thus port.ships
will contain our Ship
instance.
Write the code that makes this test pass
We have access to our currentPort
(which we have already pulled out of our itinerary), so we just call addShip
on it and pass in this
(which refers to our current Ship
instance). A constructor just defines what happens when an object is instantiated, so we can perform these operations in constructors in cases like this one.
Modify test for Ship > can dock at a different port
to test that Ship
gets added to the Port
's ships
when it docks
Here, we've just added the line:
Now a port can keep capacity and store ships
, we need to ensure that our Ship.dock
method calls the destination port's addShip
method. Here, the next port on the itinerary is calais
so we expect calais.ships
to include our ship once the dock
method has been called.
We now fail with:
Write the code that makes this test pass
In Ship.js
:
We've just added the line:
Again, we are just calling a method on an object that has been passed into our Ship
instance through dependency inversion.
Modify the test for Ship > can set sail
to test that the Ship
's previous currentPort
no longer contains the Ship
instance on its ships
property
We've added the line:
Here we expect the Ship
's setSail
method to call it's currentPort
's removeShip
method, and we can assert on this by checking dover.ships
no longer has our Ship
inside.
Write the code that makes this test pass.
This one is up to you. Modify the setSail
method in Ship.js
so a Ship
removes itself from a Port
's ships
.