UWP HackerNews - Displaying An Item
Since we're now able to TDD up the entire app; showing our wrapped control. Sounds like time to display a story title.
I've re-worked the naming; not behavior; of the VBM pattern. It's now the [Hotel UI Pattern](LINK GOES HERE). Same basic idea; but with a metaphor! Check out the new description of it for a little more detail/reasoning.
A Title
The Items have a title; we're gonna display a title!
After a bit of research and refreshing myself with the code base; HUGELY helped by having tests. I'm looking at the HackerNewsAccessTests
and going to add a new test for pulling an Item
out. To get the title; we need to get the item from the API.
Here is the test I wrote
[TestMethod]
public async Task ShouldReturnSpecifiedItem()
{
// Arrange
FakeResponseHandler fakeResponseHandler = new FakeResponseHandler();
fakeResponseHandler.AddFakeResponse(new Uri($"{HostUrl}/item/123.json"),
new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(@"{""id"":123, ""title"":""My First Title""}")
});
Response<Item> response = await new HackerNewsAccess(fakeResponseHandler).Item();
Item item = response.Body();
FakeText fakeSetText = new FakeText();
// Act
item.Title(fakeSetText);
// Assert
fakeSetText.Text.Should().Be("My First Title");
There's a bit going on here. I expected to have to create a few classes to get this test to pass correctly.
This test resulted in the creation of the Item
class. But I can't TDD the item class via the HackerNewsAccessTests
... I have to do some ItemTests
. Which is exactly what I've done.
While the Title
method already existed before the test class; it was TDD'd. Just from a different location. I think I've gone over this before. It seems pretty common. Find a new class; then get to the new class.
I put in a matching test with the same expectation as above.
[TestMethod]
public void TitleShouldSetConstructorText()
{
// Arrange
Item item = new Item();
FakeText fakeText = new FakeText();
// Act
item.Title(fakeText);
// Assert
fakeText.Text.Should().Be("My First Title");
}
and now I have 2 failing tests! NOES!
... quite a simple fix. Add the value.
public class Item
{
public void Title(ISetText item)
{
item.Text = "My First Title";
}
}
And back to one failing test, in the HackerNewsAccessTests
. That's fine; let's keep going on the Item
class for a few.
Let's make sure safe behavior if null get's passed in
[TestMethod]
public void TitleShouldDoNoethingIfPassedNull()
{
((Action)(() => new Item().Title(null))).ShouldNotThrow();
}
This updates the Title
method to be
public class Item
{
public void Title(ISetText item)
{
if (item == null) { return;}
item.Text = "My First Title";
}
}
Why am I checking for null
? Because I can write a test to check for null?
That's a terrible reason.
There's no reason... (Android may get a refactor around this). SURE - null
can come in via a test. Anywhere else? Maybe? I don't know. I don't see it; so out it goes.
I'm killing the test and the code until I'm shown it's needed.
Only write what you need.
Seriously; that's it. It's REALLY hard to keep that mindset. I struggle with it all the time. I leave these things in the posts because it's important to see that this isn't a perfect journey. I can't just do it. I have to run into a wall; a lot. That's why I do these; I practice. Simplest possible. That's why I hard code in a value. Do I REALLY know how it'll change? I've been wrong.
I'm refactoring Title
into TitleInto
. It's what Android has; and I like it better. It's more informative of what's happening. It's also DOING something, so the method name should be an action.
Running all tests now that I've removed the null check; and still just the one failure. Excellent.
The issue is that... the HackerNewsAccess#Item()
has the following implementation
public Task<Response<Item>> Item()
{
throw new NotImplementedException();
}
We need to get an API implemented for the Item
call.
Uh Oh
As I'm going through preparing to get the item id value out to provide to the network level... I see an issue I didn't refactor.
public class ItemId
{
[JsonProperty("id")]
public long Id;
public ItemId(){}
}
This is a hold over from before I added the ItemsAdapter
. I'll have to get this cleaned up as ... "Ahh, Hellz No" is the only response I have to this.
I'm commenting out the failing test in HackerNewsAccessTests
and encapsulating the Id
in ItemId
. This'll break at least one test (probably many) and give me a VERY specific starting point when I pick up again.
I changed the class to be
public partial class ItemId
{
private long _id;
}
The tests should fail somewhere... and ... no. ... uh oh.
Oh - I had one on ignore. Let's try that. YES!
OK, back to failing!
I had ShouldReturnSpecifiedItem
which is shown above set to ignore. This test fails. It doesn't get the right item because I've broken the network call. Mostly. It's a failing test; we'll get it working next time.
time passes
It's a broken test; excellent - I know exactly where to start...
The body
of our Response
is null. We need to dive in and fix that.
Given that our ItemAdapter
still looks like
internal class ItemAdapter : INetworkAdapter<Item>
{
public Item FromRawContent(string rawContent)
{
throw new System.NotImplementedException();
}
}
Not a wonder it's not working.
But that's not dealing with our ItemId
that we basically tore apart. That's in our ItemsAdapterTests
. We've started putting together some tests
[TestMethod]
public void ShouldCreateItemsCallingToObject()
{
// Arrange
// Act
Items items = new ItemsAdapter().FromRawContent("[]");
// Assert
items.Should().NotBeNull();
}
[TestMethod]
public void ShouldCreateItemsFromData()
{
// Arrange
// Act
Items items = new ItemsAdapter().FromRawContent("");
// Assert
items.Count().Should().Be(0);
}
Which are both passing. We need to get in a failing test. We can't parse the JSON into the ItemId
class anymore. It needs to go into the ItemIdJson
.
I'm going through a bit of a dance with the Adapters. One thing Retrofit does REALLY well is the black magic for json->object conversion. I'm not going black magic; each adapter is gonna be smart about what adapters it needs. shrug Not the best but it's what I need for now.
The ItemsAdapter
uses the ItemIdAdapter
internally. I'm not testing that explicitly because it's implemented in support of another class. Part of a refactor. I also want it as internal. :)
... Terrible reason. PUBLICIFICAITON! ... ... Or... I don't need it. I'll go with not needing it. Also acceptable.
My ideas has 3 classes coming into existence. They didn't. They still might; but not yet. I'm avoiding complication and delaying the pain of changing by holding off on these "probably" required classes.
...
I'm going through and working on getting the HackerNewsAccess#Item
method test working.
It's not a very exciting thing. Nothing big is standing out. I'm getting the supporting classes in place; adding the ItemIdAdapter
and tests... Long; and BORING...
... A few classes and Adapters later; the HackerNewsAccessTests#ShouldReturnSpecifiedItem
is passing!
I added the title for better testing; as well as it being the item I want to be displaying initially.
All the tests pass for getting data from the network layer; so now it's time to get the data to the UI.
UI-ify
I'm starting with the Concierge and loading from the network layer. And cheer is over - Time to head out.
next day
Too bad I didn't have about 20 more minutes. That's all it took me to get the title displaying.
Anyway; we've now got the title going all the way to the UI. Ish. Still only in the testing. But good enough for now!