Ports have Ships - Walkthrough
Steps
- Discuss in pairs how the domain model now looks.
- Create a new test spec for a
PortaddShipmethod. - Write the code that makes this test pass.
- Create a new test spec for a
PortremoveShipmethod. - Write the code that makes this test pass.
- Create a new test
Ship > gets added to port on instantiationin theShiptest suite. You'll need to checkship.currentPort.shipsto see if it contains yourShipinstance. - Write the code to make this pass. The
Shipconstructor will need to call the starting port's (this.currentPort)addShipmethod, 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 portto test that theShip'scurrentPort'sshipscontains theShipinstance. You'll likely want to assert onship.currentPort.ships. - Write the code that makes this test pass.
- Modify the test for
Ship > can set sailto test that theShip's previouscurrentPort(you can useindexOfonship.itineraryto find this) no longer contains theShipinstance on itsshipsproperty (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.