/ TDD

UWP HackerNews: Network Items

I'm back to the UWP app after not too long of a break; and we really just need to get some things happening.

I guess I'm more working to plan out the flow of the project. This is now fairly similar to a project for work I'll be helping lay the foundations of.

I've got the idea behind the core network functionality. Honestly, for the next app; I'm going to be concerned if we use the exact same, or Request object; or develop new - whatever. That it's a clean easy REST service is what matters.

We have that kinda clean rest implementation; with behind the scene black magic. We know what TYPE of objects we need and their structure.

Do we ... build those objects; without clear need? Would the be "real" objects? I'm not going to expose their internals until it's needed? So we build the layer above? And on and on and on... IIRC, I went through a similar phase for Android - I've got the network; I can fake the network responses... I need the functionality driving the code - In this case it's the UI. I need more UI. I don't like UI. ... FINE... I'll create an item.

Except I can't... not entirely. I need to update windows first. At the cheer studio; I refuse. OK; gonna work on the VBM layer (deal with it, I'm still calling it that) and the clean architecture.

I should probably put things in a ... in a list... which is mostly ui... GAH... computers.

time passes
I've worked on the UI a little just to figure out how to to get my Template Control extending the TextBox to display. I finally got it.
I had a co-worker's help with that. He used Blend which I might pull up for more UI things, or maybe just animation stuff; don't know yet. He's more familiar with it, so will see when he suggests it (probably always).

The issue was that I created my Template Control, extended TextBox, and applied my mix-in IText and had this

public sealed class QgTextBox: TextBox, IText
{
    public QgTextBox()
    {
        this.DefaultStyleKey = typeof(QgTextBox);
    }
}

Which wouldn't display. I might have included it in my earlier post, and anyone familiar with UWP/XAML/WHATEVER might be going; Yes, that won't show. OBVIOUSLY...

There is no DefaultStyleKey for QgTextBox
If I change it to

this.DefaultStyleKey = typeof(TextBox);

This shows.

Why would I apply the TextBox style when it inherits from TextBox so it should already HAVE the style applied.
What if I do

public sealed class QgTextBox: TextBox, IText {}

What if it works!

Then I'll be excited!

This ends up doing the same as the Java code; just a wrapper to enable to mix-in. No functionality should be implemented here.

It works. Now I can move onto implementing... the ... UI? for a story.

SHOW A TITLE!

We shall display a title!

LIES!

We shall display a count!
This is the simplest step to get something from the network layer to the UI. We're not going to be writing the high level test and then implementing every step; we're going to TDD this. Probably a lot like was done for Android; but I don't recall exactly how that went down. Also not sure how this will go down.

It's an exciting process. ... and I screw it up. Right off the bat.
I have a fake class


    public class FakeText : IText
    {
        public string Text { get; set; }
    }

    public class FakeMainPage : MainPageBridge.IMainPage
    {
        internal FakeText TxtFakeText = new FakeText();
        public IText Title()
        {
            return TxtFakeText;
        }
    }

that I'm using for my MainPageBridgeTests. What's shown here is a pretty quick thing thrown together to see if IText worked. I'll leave it until it should go away.
I started a test for showing the item count... jumped right into updating FakeMainPage. The test wasn't driving the code. I've been writing this blog and working on pounding TDD into my head for five months at this point... Screw up the process a lot.

At least I'm catching myself sooner. Anyway... back to a TEST...

I've implemented a test (a couple actually, I still do the minimum step when I can) that sets the Count to a value!

A few days ago; Steve and I were doing a kata and I had us back out a step to then do the smallest possible (hard coded value) then do a new test to force the code... then delete the second test.
I do this to myself, Steve - not just to annoy you.

The test I wrote to test displaying a count:

        [TestMethod]
        public void ShouldDisplayCountOfItems()
        {
            FakeMainPage fakeMainPage = new FakeMainPage();

            MainPageBridge mainPageBridge = new MainPageBridge(fakeMainPage);

            mainPageBridge.DisplayItems(new Items(new ItemId[]{null, null, null}));

            fakeMainPage.TxtStoryCount.Text.Should().Be("3");
        }

and the code in the Bridge to implement this

        public void DisplayItems(Items items)
        {
            IText item = _mainPage.Count();
            item.Text = $"{items.Count()}";
        }

It's dead simple. If you've followed the Android code at all; you'll see that this has all come out pretty damn dead simple. I'm hoping to keep that trend for the work UWP app. They tend to have some extra bits that aren't as simple; but TDD will set you free!

I've got the Bridge in place. Time to bring in a Mediator. I'm not doing this arbitrarily. It's not to use the VBM; but because I have the View. It's only job is to know what control maps to the lower level's interface; MainPageBridge.IMainPageView. That's all it does. The View understands what what wdigets map to what display control.
The Bridge understands what data a display control has. I could argue for having a smaller class for EACH display control -> data mapping. I think that's a little bit of overkill. Though it'd be doing ONE thing and ONE thing well.
I might play with that later just to see what it looks like. It'd be a simple class, so ... maybe? That's what these projects are for. More importantly, I now have a possible refactor for when the class appears to be doing too much.
Anyway; The Bridge is doing one thing. We need the mediator to do another thing.
Remember from reading my write up of VBM; the Mediator is a layer more so than the Bridge or the View. These form a triangle smallest to largest; View->Bridge->Mediator for the number of classes that should be involved. The View should have 1 to N supporting Bridge classes. EACH Bridge should have 1 to M supporting Mediator classes. The Meditator layer itself should have 1 to Z classes handling the various interactions with the rest of the system.

In the end; I need a more complex project; Why Hello Work...; to test this against more robustly. So I shall.

Back to the code; I now need the Mediator to reach out and do things.

Mediator

I have a very basic test/mediator starting

  [TestClass]
    public class MainPageMediatorTests
    {
        [TestMethod]
        public void ShouldGetItems()
        {
            MainPageMediator mainPageMediator = new MainPageMediator();
            mainPageMediator.DisplayItems(new Items(new ItemId[]{ null, null, null, null }));
        }
    }

    public class MainPageMediator
    {
        public void DisplayItems(Items items)
        {
            throw new System.NotImplementedException();
        }
    }

Which doesn't currently assert. I need to pass in the Bridge. I know the double constructor ain't great; but it's what the best solution I have. I am in C#, and I could use Unity (I think). Until it's needed, no thanks.

I'm using a real MainPageBridge with the FakeMainPageView. Use real as much as you can; ensure a much functionality as you can. Mock/Fake what you must.FakeMainPageView

I've got the test in place to feed the Item count into the display text, and my daughters cheer practice is over. So I'll end the work for now. I'm going to keep the post going until I have it operating off of fake network data.

time passes

I'm looking for what I was doing... helps if I have the correct stuff pulled...

Finally writing the test to pull from a faked network test! And it's getting happy path'd!

        [TestMethod]
        public async Task ShouldLoadItemsFromNetwork()
        {
            FakeResponseHandler fakeResponseHandler = new FakeResponseHandler();
            fakeResponseHandler.AddFakeResponse(new Uri($"{HostUrl}/topstories.json"),
                new HttpResponseMessage(HttpStatusCode.OK)
                {
                    Content = new StringContent(@"[{""id"":123},{""id"":1234},{""id"":12345}]")
                });

            HackerNewsAccess hackerNewsAccess = new HackerNewsAccess(fakeResponseHandler);
            FakeMainPageView fakeMainPageView = new FakeMainPageView();
            MainPageBridge mainPageBridge = new MainPageBridge(fakeMainPageView);
            MainPageMediator mainPageMediator = new MainPageMediator(mainPageBridge, hackerNewsAccess);
            await mainPageMediator.LoadItems();
            fakeMainPageView.TxtStoryCount.Text.Should().Be("3");
        }

And it passes! We've TDD'd our way into full stack communication.
The other test in this class can be deleted. It's testing the same thing; with less integration.

        [TestMethod]
        public void ShouldGetItems()
        {
            FakeMainPageView fakeMainPageView = new FakeMainPageView();
            MainPageBridge mainPageBridge = new MainPageBridge(fakeMainPageView);
            MainPageMediator mainPageMediator = new MainPageMediator(mainPageBridge, new HackerNewsAccess());

            mainPageMediator.DisplayItems(new Items(new ItemId[]{ null, null, null, null }));

            fakeMainPageView.TxtStoryCount.Text.Should().Be("4");
        }

This test also exposing the DisplayItems method which doesn't need to be exposed now.
We can delete the test; and refactor the encapsulation to keep it hidden!

Initial MainPageMediator looks like

    public class MainPageMediator
    {
        private readonly HackerNewsAccess _hackerNewsAccess;
        private readonly MainPageBridge _mainPageBridge;
        
        public MainPageMediator(MainPageBridge mainPageBridge, HackerNewsAccess hackerNewsAccess)
        {
            _mainPageBridge = mainPageBridge;
            _hackerNewsAccess = hackerNewsAccess;
        }

        private void DisplayItems(Items items) => _mainPageBridge.DisplayItems(items);
        public async Task LoadItems() => DisplayItems((await _hackerNewsAccess.TopStories()).Body());
    }

I haven't added the empty constructor yet to implement Double Constructor as there is no test driving it. I had ctor typed out to bring it to life via snippet; but then... there's not test. It's not a thing yet. Not making it happen.

I'll be expressionbody-ing the hell outta this project as I touch files. Until then... Done with this post for now!

Code

Quinn Gil

Code Artisan beating the drum of Development Best Practices. Extreme Programming and Object Oriented Design.

Read More