First you have to set up your unit test class. Create the GameListViewModelTest class and initialize a Community, a model, and a view model.
[TestClass]
public class GameListViewModelTest
{
    private ICommunity _community;
    private GameListViewModel _viewModel;

    [TestInitialize]
    public void Initialize()
    {
        _community = new Community(new MemoryStorageStrategy())
            .Register<Model.CorrespondenceModel>();
        User user = _community.AddFact(new User("alan1"));
        _viewModel = new GameListViewModel(user);
    }
}
The GameListViewModel takes a User and exposes three properties.
public class GameListViewModel
{
    private User _user;

    public GameListViewModel(User user)
    {
        _user = user;
    }

    public string Opponent
    {
        get
        {
            throw new NotImplementedException();
        }
        set
        {
            throw new NotImplementedException();
        }
    }

    public ICommand Challenge
    {
        get
        {
            throw new NotImplementedException();
        }
    }

    public IEnumerable<GameSummaryViewModel> ActiveGames
    {
        get
        {
            throw new NotImplementedException();
        }
    }
}
Let's start with the ActiveGames property, since that is the first unit test. The active games are based on the players associated with the user. Execute that query and pass each Player to a new GameSummaryViewModel.
public IEnumerable<GameSummaryViewModel> ActiveGames
{
    get
    {
        return _user.Players
            .Select(p => new GameSummaryViewModel(p));
    }
}
Add the GameSummaryViewModel constructor and store the Player in a field. Now the first unit test should pass.

For the second one, we need to edit an opponent name. This is not persistent data. It is just there for the user to make a selection. So it belongs in a navigation model. Create a GameListNavigationModel to store the opponent name. Remember that navigation fields need to be Independent.
public class GameListNavigationModel
{
    private Independent<string> _opponentName = new Independent<string>();

    public string OpponentName
    {
        get { return _opponentName; }
        set { _opponentName.Value = value; }
    }
}
Add the navigation model to the constructor of the view model and expose the property.
public class GameListViewModel
{
    private User _user;
    private GameListNavigationModel _navigation;

    public GameListViewModel(User user, GameListNavigationModel navigation)
    {
        _user = user;
        _navigation = navigation;
    }

    public string Opponent
    {
        get { return _navigation.OpponentName; }
        set { _navigation.OpponentName = value; }
    }
}
Now make a command that issues a challenge. It needs to create a game between this user and the specified opponent. We can put this business logic in a User partial class.
public partial class User
{
    public void Challenge(string opponentName)
    {
        Game game = Community.AddFact(new Game());
        User opponent = Community.AddFact(new User(opponentName));
        Community.AddFact(new Player(this, game));
        Community.AddFact(new Player(opponent, game));
    }
}
Call this method from the command.
public ICommand Challenge
{
    get
    {
        return MakeCommand
            .Do(() => _user.Challenge(_navigation.OpponentName));
    }
}
Run the unit test again and see where it fails. It's telling us that we need to get the name of the opponent. For that, we need to get all of the players associated with the game. Go back to the Factual model and add that query to the Game.
fact Game {
key:
    unique;

query:
    Player* players {
        Player p : p.game = this
    }
}

Hit the "Transform All Templates" button, then we can use that query in the code. The opponent is the player who is not this one. Put that logic in the OpponentName property.
public string OpponentName
{
    get
    {
        Player opponent = _player.Game.Players
            .FirstOrDefault(p => p != _player);
        return opponent == null
            ? null
            : opponent.User.UserName;
    }
}
Now the unit test passes. Let's move on to the next one.

The Challenge button should be enabled only when an opponent name has been entered. Add a .When() clause to the command.
public ICommand Challenge
{
    get
    {
        return MakeCommand
            .When(() => !string.IsNullOrEmpty(_navigation.OpponentName))
            .Do(() => _user.Challenge(_navigation.OpponentName));
    }
}
That takes care of the next two. Finally, let's clear the opponent name when the user presses the Challenge button. Expand the .Do() clause from a lambda expression to a statement block. Then add the extra step to clear the opponent name.
public ICommand Challenge
{
    get
    {
        return MakeCommand
            .When(() => !string.IsNullOrEmpty(_navigation.OpponentName))
            .Do(() =>
            {
                _user.Challenge(_navigation.OpponentName);
                _navigation.OpponentName = null;
            });
    }
}
And with that, all of the tests pass. Just create a view for the view model and add it to the main window.

Next: Practice 2: Data bind thew new view model

Last edited May 15, 2011 at 5:00 AM by MichaelLPerry1971, version 5

Comments

No comments yet.