OK – after the last post of refactoring I now feels great to turn my focus on some new functionality; I want to let the users create new Kanban boards. This involves some new views and controllers stuff, uploading pictures and also changes in the domain model.
I got some valuable feedback from my colleague Joakim Sunden on the real purpose of the Kanban board application / site. I actually never wrote it down. So I’ve updated the first post in the series with the following:
www.kanban-boards.com will be a site where users can upload their Kanban boards for other people to see, vote on and get inspired by. In this way we can share and learn from each other I think and hope.
The creation of this site is documented as my learning process.
On with the show!
New functionality – new scenario
As I always I start off with a new specification and the following scenario:
Looking at that scenario I’ll know I will run into some problems already, but in the spirit of the series (document you shortcomings and failures ) I press on. One step definition at the time.
Without knowing if it’s right – here is usually how I work my way through a scenario like this:
- I usually generate all the step definitions with the Pending state. In this “phase” I checking how the language feels and if I can reuse and steps. So I might update the written specification to get it to work. Also I have to do other minor tweaks to other specs to get more re-usability
- I then run the scenario to check that my steps and step definitions are aligned, i.e. I don’t get any Inconclusive (“No matching step definition found for the step”)
- I then focus on one step at the time working outside in and getting each step to pass before I move on to the next. That focus my efforts and also gives a sense of success or forward motion.
A given background
So I generated the step definitions and they now are in the Pending-state. During this process (see above) I did some minor tweaks to the step definitions and the language, into this:
So now I can focus on the first step in the background; “Given I am logged in on the site”. Obvious I don’t want to perform the actual login now (planning on using Open ID for that later) so I need some way to mock that away.
Also thinking of what “Given I am logged into the site” means I had to stop for a while…This is my reasoning (done in my head, for those who wondering about my mental health):
- So I want to be logged in in order to …
- Be able to be sure to pass any requirements of being authenticated later.
- And what is the simplest possible solution to that right now
- To create a simple interface for authentication checks. I can fill it with a implementation that checks against Open ID later.
- So what are you waiting for?
- You to stop asking stupid questions!
Sorry – got carried away there. But really I just need an interface that my authentication mechanism can implement later. So I think I’ll create an AuthenticationService that I later can implement with the check to the User.Identity.IsAuthenticated property that will be set by Open ID.
Here is my Given step definition:
Obviously I moved the IAuthenticationService to the Web-project, it’s just there now for clarification. But with that simple step I now have a passing step, and can go on to the next:
To the page
Next up is “When I navigate to to the Create page” which feels like something I can reuse from earlier step definition. Well – when examined I cannot. When I write my specifications against the controller “navigating to a page” translates to choosing the right action method to execute and storing that result.
The current implementation can be cleaned up and then maybe reused. Here is what it looks like now:
And I tweaked it to be reusable into this:
… which is a lot more code. It still works (just ran my specs) and it makes my next step very easy. Here it is:
Note that the Create method not is implemented… Yet! But a couple of Resharper-commands later and it’s there – I simply return a view from it.
And with that my the next step is working and I got the Then-step for free since, from the previous scenario:
Enter some information
OK – time to get to the point; the point where some information is about to be entered. I have deliberately used a table format that I know will suit the extension methods of the SpecFlow Table-object good. The one called CreateInstance works on a table with Field – value pairs.
The step definition looks like this:
And this doesn’t do much right now since the CreateKanbanBoardViewModel doesn’t contain any fields. But I get now errors.. Hmm the CreateInstance method must simply try to add and skip if not present. I add the fields to the class and run again. I added a break point just to be sure – and lo and behold here’s my filled in view model:
That part with the BoardImage will need to be reworked but it comes later.
I have now filled out the “form” (quoted since I didn’t actually do that, I just filled out the view model and stored it in scenario context). Lets now submit it and see where it takes us.
Again the method is marked as red since it doesn’t exists. Resharper. Presto – it’s there. Which in turn made me realize that I hadn’t moved the view model class into place. But when all that was done I got the following action method:
Which reminded me on that I better store the Action Result. And that in turn gave me an idea on a improvement. I created a method called LatestActionResult and moved it into the MvcSteps class. Which modified the When-step into this:
And I kind of like that. It fails because my Create action method on the controller simply returns null right now. That makes this step pass so I’m off to the next.
Final steps… Let’s go Then
So now there’s the assertions in/on the Then-steps to take care of. The first (“Then I should be on the MyBoards page”) is quite easy. I simply update the controller method into this:
and it works. But doesn’t sit right. Shouldn’t I be redirected to MyBoards which should pick up my new board from the repository? That feels much more MVC (or PRG). But now it’s late – I’ll do it tomorrow.
I’m back! Yes – I should definitely change that. So I updated the scenario and step definition into this:
And the with the action method updated to this:
my spec now has just the last step failing.
Last step – show my board
So for the last step (“Then I should see KanbanBoard 1 among my boards”) a couple of things need to happen.
- First we need to take into account that there is a redirection going on.
- Then we must handle the call to the Read-service (which needs to be TDD’d out) to get “MyBoards” – which in turn call the Repository’s GetAll()-methods
- Also the Add-method of the repository is called – so we probably should find a good place to set up the expectations for that
- Finally the whole thing should be put together.
Hmmm – this feels a bit to hard. I have probably missed something in the process, but right now I cannot see what.
Detour – my boards page
I’ll start with the new method on the read service. It should return a list of boards for a certain user name. Here’s the test:
And here the implementation:
And actually – with this in place I can easily write a specification for the MyBoards page:
Which in turn helped a lot of other things:
- I updated the Logged-in step to require a name and created an AutenticationServiceSteps class
- I realized that I don’t need to store things that are stateless in the ScenarioContext.Current, for example the KanbanBoardController. It can be created when needed
- I could actually put the services I need in the ScenarioContext.Current when needed (or lazy loading if you like). That shapes up the code a lot, look at the new CreateController-method:
And with all that in place I just needed the following two steps:
Please not the super-slick CompareToSet<T> that really simplifies this code. One cool feature is that it only tests the column you put in, like I used above.
Back to create
And now when I turn back to the Create scenario I see it in another light. I can reuse and shape up the scenario into something a little more concise:
Which in turn means that I can reuse the steps. And now when I run it… it fails because I haven’t implemented the actually add in the Create action method. Just as expected. I can now (is allowed?) to write production code.
A service for commands
The code is going to use a “command service” in my light-weight CQRS implementation. So I create a new class library with the creative name “CommandService” and added a tests for it and then implemented it as follows.
I want that service to have void return types only (as is the common pattern in CQRS) and to receive command classes. But when I created the Command class (AddNewKanbanBoardCommand) I realized that it contains all the values that the CreateKanbanBoardViewModel does. So maybe I should use that instead. I sure would like to receive a filled out command to the Action method.
Yes! I like – lets do it! Eeeeh – well that made me pull things apart a little. My command service will receive commands and (for now) simply just update the (read)model. But to do that I need to separate out the repository since it’s to be referenced from both read and write parts. Also I created a Domain project that contains my Domain model – right now just KanbanBoard (a DTO for now).
Thank God for Resharper! And praise him once again for tests! After that major refactoring I ran my tests again. And they worked. Phew – back on green. Here’s my new solution structure:
Next step was simply to remove the CreateKanbanBoardViewModel and face the facts. Where it blew up I replaced it with the AddKanbanBoardCommand. Pretty simple stuff. Lean on the tools – and scarify a for-loop to the Resharper-makers.
Jupp – still fails because the Create action isn’t implemented yet. Lets do that now. First we implement a test for the add command handler like this:
And here’s the implementation. (Yes I know that this is a bit messy from a CQRS perspective. I should have done the update with an event and then updated the database with a demoralizer, but I couldn’t without bring in a CQRS framework of sort and I think this blog post is long enough ):
And now, once again, I can turn back to the failing specification and wrap it up with this implementation:
But that gave me some pondering to do. Now, in my steps, I need to update the list of Kanban boards that the substituted repository returns. Here is the scenario again:
So when I get to that last step I need to add the new board to the faked return list. And the only place where I have the data to do so is in the When-step (“When I submit a board with the following information”). So I updated the step definition into this:
Which makes use of the AddBoardToReturn-method in the KanbanBoardRepositorySteps-class. Here it is:
Feels like a bit of a cheat but I cannot see another way to do it now.
And just when I “just gonna run the tests and be done for today” my acceptance test blew up. I got the following error message:
Well – at least the message is very informative. My domain has changed – my database hasn’t. In this post by Scott Gu you can read more about it.
I would like to see if the tool can initialize the database with some data for me. And with a quick search on mother of all knowledge I found a post that describe it very quick to me. Here is my implementation of my DatabaseIntializer:
And now I can simply call that initializer in Application_Start like this:
With that my acceptance (and all other) tests run again. And I got a simple migration strategy in-place as well.
Reflection and conclusion
This was a long post but I also added some important functionality. As always I documented all the wrong turns and backtracking I did, hope you bare with me on that. For me that is really where the learning take place. I hope you can follow along and learn something too.
I am thinking of document a little less details and move faster in the next posts. What do you think?
I didn’t get to uploading pictures and I suspect that when I get to that I will change my domain model (yes, yes DTO) a bit. Well that’s later.
Next up is to create an acceptance test that adds a board through the GUI. That in turn will make me implement the Open ID stuff.
The code is here and this was the commit for this post. Love to get some feedback on this post.