Bogdan Bujdea

Windows Platform Developer

Windows Phone TDD Part 4: The proper way to do MVVM

A few days ago a friend asked me if you should put the business logic in the Models and the ViewModels from MVVM. Basically, my answer was like this:

I will talk about just two of the reasons why I think this.

The first one is that you will have to rewrite your business logic for every app you will use it. Let’s say that you have a client that want’s an app for handling his account, so you make him a desktop app with WPF. You use MVVM, and you have your logic there, but what do you do when the client wants a Windows Phone app? The logic is in the ViewModels from the WPF app, so now you have to rewrite it again for the Windows Phone app. Wouldn’t it be better if the business logic was in some Portable Class Library that you could reuse for every platform instead of binding it with the ViewModels? I thought so.

The second reason is that I think the ViewModels should handle some of the UI logic as well. If you’re doing TDD, then you will want to test as much of your code as possible. If the ViewModels don’t contain the business logic, what’s stopping you from using them to control your UI? Imagine the following scenario, you have an app that shows a popup with a login form. When the user clicks on the login button, and he is logged in, you want the popup to dissappear. The problem is that you don’t know if the login was successful or not, so you can’t just close the popup when the button was pressed. So how do you make the View wait for an answer from the ViewModel? Or better, how do you write the code for this task so you could test it?

If you’re using Caliburn.Micro, you could bind the Visibility property of the popup to a boolean property in the ViewModel. The code for the login function could be like this:

<span style="color: #569cd6;">public</span> <span style="color: #569cd6;">async</span> <span style="color: #4ec9b0;">Task</span> Login(<span style="color: #569cd6;">string</span> username, <span style="color: #569cd6;">string</span> password)
{
    <span style="color: #569cd6;">var</span> loginResult <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">await</span> LoginUser(username, password);
    <span style="color: #569cd6;">if</span> (loginResult)
        LoginPopupIsOpened <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">false</span>;
    <span style="color: #569cd6;">else</span> <span style="color: #4ec9b0;">MessageBox</span><span style="color: #b4b4b4;">.</span>Show(<span style="color: #d69d85;">"An error occurred"</span>);
}

Because the popup visibility is binded to our property, then when I will set it to false, the popup visibility will be set to Collapsed. The upside here is that I can write a test for this very easy.

[<span style="color: #ff3333;">Test</span>]
<span style="color: #569cd6;">public</span> <span style="color: #569cd6;">void</span> successful_login_should_close_popup()
{
    <span style="color: #569cd6;">var</span> viewModel <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">new</span> <span style="color: #ff3333;">LoginViewModel</span>();
    viewModel<span style="color: #b4b4b4;">.</span><span style="color: #ff3333;">LoginPopupIsOpened</span> <span style="color: #b4b4b4;">=</span> <span style="color: #569cd6;">true</span>;
    <span style="color: #ff3333;">BindAuthenticationServiceToSuccess</span>(); <span style="color: #57a64a;">//we bind the login function to return true</span>
    await viewModel<span style="color: #b4b4b4;">.</span><span style="color: #ff3333;">Login</span>();<span style="color: #57a64a;">//ignore the red texts, it's because the classes don't exist, think of it as pseudocode</span>
    viewModel<span style="color: #b4b4b4;">.</span><span style="color: #ff3333;">LoginPopupIsOpened</span><span style="color: #b4b4b4;">.</span>Should()<span style="color: #b4b4b4;">.</span>BeFalse();
}

If you want a high code coverage, try to make your ViewModel to handle the UI, but don’t do it directly, so don’t use objects that are related to the UI(Button,Grid,TextBox, etc… or properties like Visibility, Text, etc). You can see that I haven’t used the Visibility enum above, that’s because I want to keep the UI related stuff out of my ViewModel. So I’m binding Visibility to boolean properties, Image source Uri to string properties, events to functions, and so on. The rule is to keep all your functionality in the ViewModel, and the strictly UI stuff in the View. So far this is working great for me, especially because I’m using Caliburn which makes the separation between the View and the ViewModel very easy.

, , ,