My solution for UncleBob's Mark IV CoffeeMaker - 4

Back to Part3?
Part 4: Fresh Coffee at the end of this post!
Story#2. The Warmer Plate is turned on only when the pot is present on it.
Going back to my lil seq diagrams, we see that this one is similar to the previous one. We let the WarmerPlateStatus changed event tell us when the pot has been moved off the plate and toggle the heater on/off accordingly.

[Test]
public void Test_WarmerHeaterIsTurnedOff_WhenPotWithCoffeeIsTakenOffThePlate()
{
m_obDevice.WarmerPlateStatus = WarmerPlateStatus.POT_NOT_EMPTY;
Assert.AreEqual(WarmerState.ON, m_obDevice.WarmerPlateState,
"Warmer Plate should be ON if there's a pot with cofee on it");

m_obDevice.WarmerPlateStatus = WarmerPlateStatus.WARMER_EMPTY;
Assert.AreEqual(WarmerState.OFF, m_obDevice.WarmerPlateState,
"Warmer Plate should be turned off");
}

And to make that go Green

void On_WarmerPlateStatusChanged(object sender, EventArgs obEvtArgs)
{
...
if (WarmerPlateStatus.POT_NOT_EMPTY == m_obDevice.GetWarmerPlateStatus())
{
m_obDevice.SetWarmerState(WarmerState.ON);
}
else
{
m_obDevice.SetWarmerState(WarmerState.OFF);
}
}

Easy enough.. complete the rest.

  • Test_WarmerPlateStaysOff_WhenEmptyPotIsReplacedOnPlate
  • Test_WarmerPlateIsTurnedOn_WhenEmptyPotStartsCollectingCoffee
  • Test_WarmerPlateStaysOff_WhenEmptyPotIsTakenFromThePlate
  • Test_WarmerPlateIsTurnedOn_WhenPotWithCoffeeIsReplacedOnPlate
Story#3: When the Brew Button is pressed, the CoffeeMaker makes Coffee.
The description is in the book. The user places the filter with coffee grounds, pours water and presses the BrewButton. The indicator light goes off till Coffee is ready. This just seems like too big a bite..
The CoffeeMaker’s BrewButton is an auto-resetting button. So we add that into FakeCoffeeMaker.

public BrewButtonStatus GetBrewButtonStatus()
{
BrewButtonStatus eBrewButtonStatus = m_eBrewButtonStatus;
m_eBrewButtonStatus = BrewButtonStatus.NOT_PUSHED;
return eBrewButtonStatus;
}

The test for this isn’t flowing to me. I’ll look at the rough seq diagram I drew up.. Ah! the huge seq diagram is scaring me.. I need to break this diagram into multiple tests

  1. Test_BrewButtonPressed
  2. Test_BrewButtonPressed_ButTheUserForgotToPourWater
  3. Test_IndicatorLightIsTurnedON_AsSoonAsCoffeeIsReady
  4. Test_BoilerIsTurnedOFF_WhenWaterIsExhausted

[Test]
public void Test_BrewButtonPressed()
{
Assert.AreEqual(BoilerState.OFF, m_obFakeDevice.BoilerState);
Assert.AreEqual(IndicatorState.ON, m_obFakeDevice.IndicatorState);

m_obFakeDevice.SimulateUser_PlaceFilter_PourWater_And_PressTheBrewButton();

Assert.AreEqual(BoilerState.ON, m_obFakeDevice.BoilerState,
"Boiler must be turned on to heat the water");
Assert.AreEqual(IndicatorState.OFF, m_obFakeDevice.IndicatorState,
"Indicator light must go off to indicate CoffeeMaker is busy");
}

So to make this compile I need to update FakeCoffeeMaker with (I know its an awfully big method name..)

internal void SimulateUser_PlaceFilter_PourWater_And_PressTheBrewButton()
{
m_eBoilerStatus = BoilerStatus.NOT_EMTPY;
PressBrewButton();
}

Alright we have a red to go ahead.. So first the CoffeeMaker has to know when the BrewButton is pressed. The Observer can help us with that.

public void Monitor(CoffeeMakerAPI obDevice)
{
...
m_obObserver.WarmerPlateStatusChanged += new EventHandler(On_WarmerPlateStatusChanged);
m_obObserver.BrewButtonPressed += new EventHandler(On_BrewButtonPressed);

On_WarmerPlateStatusChanged(this, EventArgs.Empty);
}

void On_BrewButtonPressed(object sender, EventArgs e)
{
m_obDevice.SetBoilerState(BoilerState.ON);
m_obDevice.SetIndicatorState(IndicatorState.OFF);
}

Green. Next we handle the error scenarios. The user can forget the filter (and I presume he may get hot water.. not too bad.. I’ll skip that one). But if he forgets to add water, we don’t want our boiler all heated up for nothing. So

[Test]
public void Test_BrewButtonPressed_ButTheUserForgotToPourWater()
{
Assert.AreEqual(BoilerState.OFF, m_obFakeDevice.BoilerState);
Assert.AreEqual(IndicatorState.ON, m_obFakeDevice.IndicatorState);

m_obFakeDevice.SimulateUser_PlaceFilter_And_PressTheBrewButton();

Assert.AreEqual(BoilerState.OFF, m_obFakeDevice.BoilerState,
"Boiler must stays off since there is no water in the boiler");
Assert.AreEqual(IndicatorState.ON, m_obFakeDevice.IndicatorState,
"Indicator light stays as is as CoffeeMaker refuses to work without water");
}

Now I can add another method to this thing, which leaves out the ‘Pour Water’ part. But something doesn’t feel right. I might as well as kill off the Lazy Method & have separate methods for each user action. Like this.

m_obFakeDevice.SimulateUser_PlaceFilterWithCoffeeGrounds();
m_obFakeDevice.SimulateUser_PressTheBrewButton();

I get squiggly lines and I use my newfound IDE ninja knowledge ‘Ctrl + . + Enter’ to generate the stub methods. Then Ctrl+Alt+DownArrow + F(akeCoffeeMaker) + Enter to switch over to the class.

internal void SimulateUser_PlaceFilterWithCoffeeGrounds()
{
return;
}

internal void SimulateUser_PressTheBrewButton()
{
return;
}

Now I see we already have a PressBrewButton… that we can rename. Delete the new stub#2. Stub#1 can stay as is.. just to add to readability of the test. Build and good.. red test.
Simple fix.. add a guard clause and we’re green.

void On_BrewButtonPressed(object sender, EventArgs e)
{
if (m_obDevice.GetBoilerStatus() == BoilerStatus.EMPTY)
{
return;
}
...

We’ll update the previous test to be consistent and readable as follows. So the previous test now reads...

[Test]
public void Test_BrewButtonPressed()
{
...
m_obFakeDevice.SimulateUser_PlaceFilterWithCoffeeGrounds();
m_obFakeDevice.SimulateUser_PoursWater();
m_obFakeDevice.SimulateUser_PressTheBrewButton();
...
}


Next up is

[Test]
public void Test_IndicatorLightIsTurnedON_AsSoonAsCoffeeIsReady()
{
Test_BrewButtonPressed();

m_obFakeDevice.WarmerPlateStatus = WarmerPlateStatus.POT_NOT_EMPTY;

Assert.AreEqual(IndicatorState.ON, m_obFakeDevice.IndicatorState,
"Indicator light comes back on - 'Coffee Ready'");
}

And red straight away.. Make the following changes to Coffee_Maker.Brew

void On_BrewButtonPressed(object sender, EventArgs e)
{
...

m_obDevice.SetBoilerState(BoilerState.ON);
m_obDevice.SetIndicatorState(IndicatorState.OFF);

m_obObserver.WarmerPlateStatusChanged += new EventHandler(On_WarmerPlateStatusChangedAfterBrew);
}

void On_WarmerPlateStatusChangedAfterBrew(object sender, EventArgs e)
{
if (m_obDevice.GetWarmerPlateStatus() != WarmerPlateStatus.POT_NOT_EMPTY)
{ return; }
m_obDevice.SetIndicatorState(IndicatorState.ON);
m_obObserver.WarmerPlateStatusChanged -= new EventHandler(On_WarmerPlateStatusChangedAfterBrew);

}

Neat. Now on to the final one.

[Test]
public void Test_BoilerIsTurnedOFF_WhenWaterIsExhausted()
{
Test_BrewButtonPressed();

m_obFakeDevice.SetBoilerStatus(BoilerStatus.EMPTY);

Assert.AreEqual(BoilerState.OFF, m_obFakeDevice.BoilerState,
"Boiler should have been turned off since there is no more water to heat");
}

Red.. Back to Coffee_Maker

void On_BrewButtonPressed(object sender, EventArgs e)
{
...

m_obDevice.SetBoilerState(BoilerState.ON);
m_obObserver.BoilerEmpty += new EventHandler(On_BoilerEmpty);
m_obDevice.SetIndicatorState(IndicatorState.OFF);
m_obObserver.WarmerPlateStatusChanged += new EventHandler(On_WarmerPlateStatusChangedAfterBrew);
}

void On_BoilerEmpty(object sender, EventArgs e)
{
m_obDevice.SetBoilerState(BoilerState.OFF);
m_obObserver.BoilerEmpty -= new EventHandler(On_BoilerEmpty);
}

All Green. Now I update my visual CoffeeMakerView that I built to strap on a Coffee_Maker to monitor the FakeCoffeeMakerDevice and see the magic happen. I need to make some of the internal test helper FakeCoffeeMaker methods public. Warm Fuzzy Glow! It is alive!!! [CoffeeMakerTestApp\bin\Release\CoffeeMakerTestApp.exe]



Thought of one more test.. Ready Indicator doesn't stays OFF if the pot already had some old coffee in it. Add a new test and make it green by only turning off the light and subscribing for notifications if we have an empty pot on the plate. Done!
Turn the page..

No comments:

Post a Comment