views:

88

answers:

3

I am writing a card game in an attempt to learn Silverlight.

I have domain representation of the game which is exposed to Silverlight as a WCF service and the only public method on my Game object is "PlayCards". Which is a method that looks like this

public void PlayCards(Guid playerId, List<Card> cards)

When this method is invoked, the Game object is updated and transmitted to all the clients using a duplex binding

There are lots of scenarios I need to unit test (doing it via the UI is becoming really tedious, especially as there are lots of scenarios that don't occur naturally that often). However, to do so would mean making various properties public that really don't need to be (except for my testing purposes)

Here's some specifics:

I have a game class that contains a list of players :

public class Game
{       
 public List<Player> Players
 {
    get { return _players; }
 }

 ...

The Player class has a Hand

 public class Player
 {
    public Hand Hand { get; internal set; }

 ...

The Hand class has various parts

public class Hand
    {
        public List<Card> FaceDownCards { get; internal set; }
        public List<Card> FaceUpCards { get; internal set; }
        public List<Card> InHandCards { get; internal set; }

Now, in my unit tests I want to set up various scenarios where one player has certain cards, and the next player has certain cards. Then have the first player play some cards (via the public PlayCards method) and then Assert on the game state

But of course, as Players is a read only property I can't do this. Likewise Hand (on Player) is inaccessible. Likewise the parts of the hand.

Any ideas how I can set this up?

I guess this might mean my design is wrong?

thanks

EDIT:

Here's what some classes look like to give you an idea of some of the properties I want to Assert on after cards have been played

I have changed the private setters to internal for the moment while I experiment with InternalsToVisible, but am interested what the pure TDD way of doing things would be

public class Game
    {
        public List<Player> Players { get; internal set; }
        public Deck Deck { get; internal set; }
        public List<Card> PickUpPack { get; internal set; }
        public CardList ClearedCards { get; internal set; }

        public Player CurrentPlayer
        {
            get
            {
                return Players.SingleOrDefault(o => o.IsThisPlayersTurn);
            }
        }

        internal Direction Direction { get; set; }

..

public class Player
    {
        public Guid Id { get; internal set; }
        public Game CurrentGame { get; internal set; }
        public Hand Hand { get; internal set; }
        public bool IsThisPlayersTurn { get; internal set; }
        public bool IsAbleToPlay { get; internal set; }
        public string Name { get; internal set; }
...

 public class Hand
    {
        public List<Card> FaceDownCards { get; internal set; }
        public List<Card> FaceUpCards { get; internal set; }
        public List<Card> InHandCards { get; internal set; }

...
+1  A: 

I've had the same problem in the past.

Check out the InternalsVisibleToAttribute class.

Here's a tutorial on how to use it.

Also, you can consider changing the methods in your classes from private to protected. Then, you could write a subclass with a public wrapper function for your testing. This way, you get the benefits of encapsulation and the ability to easily test your existing code with minimal changes.

As a rule of thumb, if you really need to test private methods something may need some tweaking in your design. I'd think about separating functionality across different classes to loosen up coupling.

This way, you can have public methods in classes that do specific things and test those individually and how they interact with each other.

This approach might be more overhead, but I think it will help in the long run.

Robert Greiner
yeah, I'm aware of that. Thanks. And it is possibly the way I'll go. But I know it gets a bad press from the purists. And I'd like to hear what they say. Even if it is "you're design is all wrong"
Christo Fur
Also, would this work for properties that don't have Setters? Or would you need to add a private Setter to each one for this to work?
Christo Fur
+1  A: 

I think the crux of the problem is here:

There are lots of scenarios I need to unit test (doing it via the UI is becoming really tedious, especially as there are lots of scenarios that don't occur naturally that often). However, to do so would mean making various properties public that really don't need to be (except for my testing purposes).

I recommend not worrying too much about changing the interface to support testing.

By all means keep set accessors internal.* You can limit the impact on your API, since all you really need are additional public constructors for the objects you need to arrange. For example:

public Player(Guid id, Hand hand, bool isThisPlayersTurn, string name) { ... }

You want to get to the point where you can write simple, clear test cases:

Hand   hand   = new Hand(faceDownCards, faceUpCards, inHandCards);
Player player = new Player(Guid.NewGuid(), hand, true, "Christo");
Game   game   = new Game(player);

I've never regretted doing this, even when it's just for testing purposes. The API is still clear enough that people use the correct constructors: normally, they'd use a default constructor (or no constructor at all - instead they'll call game.AddPlayer("Christo")) and only use the extended constructor for testing or deserialization (when appropriate).

* And consider changing the public interface of members like Game.Players to IEnumerable<Player> to better communicate the read-only semantics.

Jeff Sternal
+2  A: 

It's not your design that's wrong, but your implementation might be. Try this, with the interfaces only having getters on the property declarations:

public class Game : IGame
{       
    public List<IPlayer> Players
    {
        get { return _players; }
    }
}

public class Player : IPlayer
{
    public IHand Hand { get; internal set; }
}

public class Hand : IHand
{
    public List<Card> FaceDownCards { get; internal set; }
    public List<Card> FaceUpCards { get; internal set; }
    public List<Card> InHandCards { get; internal set; }
}

If you build your classes this way, you can mock up the hands you want to test, or build players with those hands programatically, to validate the logic at all the different levels.

EDIT

An example of a Mocked IPlayer (using Moq in this case) to inject a specific (poker) hand -

//Mmmm...Full House
List<Card> inHandCards = new List<Card> { ...Cards...}; //2 Kings?
List<Card> faceDownCards = new List<Card> { ...Cards...}; 
List<Card> faceUpCards = new List<Card> { ...Cards...};  //3 4s?

Mock<IHand> hand = new Mock<IHand>();
hand.SetupGet(h => FaceDownCards).Returns(faceDownCards);
hand.SetupGet(h => FaceUpCards).Returns(faceUpCards);
hand.SetupGet(h => InHandCards).Returns(inHandCards);

Mock<IPlayer> player = new Mock<IPlayer>();
player.SetupGet(p => p.Hand).Returns(hand.Object);

//Use player.Object in Game

EDIT 2

I just realized why Jeff commented that he had misread the question, and realized that I'd only answered part of it. To expand on what you stated in the comments, it sounds like you have something like this:

public class Game
{
    public Player NextPlayer { get; private set; }
    public bool NextPlayerCanPlay { get; private set; }
    ...
}

Which is not a very testable class because your tests can't set anything. The best way I've found to get around this is to build a couple of interfaces IGame and ITestableGame which expose the properties differently:

public interface IGame
{
    IPlayer NextPlayer { get; }
    bool NextPlayerCanPlay { get; }
}

internal interface ITestableGame
{
    IPlayer NextPlayer { get; set; }
    bool NextPlayerCanPlay { get; set; }
}

public class Game : IGame, ITestableGame
{
    public IPlayer NextPlayer { get; set; }
    public bool NextPlayerCanPlay { get; set; }
}

Then, in your service, just send the clients an IGame object, and either make the ITestableGame interface internal and market it as InternalsVisibleTo(TestAssembly), or just make it public and don't use it outside of your testing.

EDIT 3

Based only on what you posted, it sounds like your control flow is set up something like this:

public class Player 
{
    public void Play(List<Card> cardsToPlay) 
    {
        hand.InHandCards.Remove(cardsToPlay);
        currentGame.PickUpPack.Add(cardsToPlay);
        hand.Add(currentGame.Deck.Draw(cardsToPlay.Count));
        currentGame.SelectNextPlayer();
    }
}

Is that fairly accurate?

arootbeer
thanks for taking the time to expand on your answer. There are lots of private properties on the game object, e.g. Direction, Deck, PickUpPack, ClearedCards, ActivePlayer, NextPlayer, CanNextPlayerPlay etc (It's not Poker BTW)...do these all need to go into the Game interface? And they won't be set correctly in the Mock object will they? As the Mock won't contain any of the logic that changes them...And it's these properties that I need to assert on after a player has laid some cards
Christo Fur
I'll back up on the IGame interface - the only reason (related to this question/answer) to build this is if it's going to be injected into a larger testable unit. If you're not going to be giving an IGame to anything to manipulate or query, then IGame is unnecessary. It sounds like that is the case, and my example is intended to illustrate testing the Game class, not something that might take an IGame. If you'd like, I'll update the signature of the Game class to make that more clear.
arootbeer
Cheers - up voted the answer. Will keep it open for now though to see if anyone else has any input -
Christo Fur
Have added some partial class definitions above to give you an idea of what I need to assert on after each time PlayCards is called. Not sure how using a Mock would help as they wouldn't have the logic to change the properties I want to assert on, would they? cheers again
Christo Fur
Can you post some information about what, exactly, you're asserting? It sounds like you might be trying to make assertions e.g. on the players after changing the state of the game, which is getting away from unit testing and into system testing.
arootbeer
Example Scenario. Player 1 plays 2 Fives. Asset that these 2 Fives have been added to the PickUpPack. Asset top 2 cards from the Deck have been added to Player 1's InHandCards. Asset that Player 2 is now Active and Player 1 is Inactive. There could be other assertions - mostly on Cards in a players Hand, cards in the PickUpPack, cards in the ClearedCards, the direction of the game etc
Christo Fur
All of those Assertions can be made with the existing classes you have: they're all public getters. I'll update my answer with some issues I see in your design.
arootbeer
Yes, that's right. The problem was not so much with the Assertions. More injecting the desired Hands onto the Players. Your Edit 3 is close to what I am doing. There are just specific things that specific cards do. e.g. quenn changes direction, Seven means next player plays lower, 10 clears the PickupPack and you get another go. But yes, the idea is simply to inject the desired Hand onto the Players, then assert various properties on Game, Player, Hand, Deck etc
Christo Fur
I thought you're suggestion of using Mocks might not work as I thought the Mock would be a dumb object that wouldn't effect the state of the Game, Player, Hand etc
Christo Fur
And you were correct - the mocks don't have enough capability to correctly affect the state of the components under test. I think I may have to go back on my very first statement, and say that your design is probably incorrect. For example: A `Card` should not directly affect the state of the `Game`, but the `Game` should inspect the state of the `Deck`(s) and determine what its own state should be. A `Player`'s play should not change the state of the `Deck`, but rather the `Game` should ask the `Player` what `Card`(s) he would like to play, and then update the state of the other objects.
arootbeer
This way, the only entity that's responsible for the state of the `Game` is the `Game` itself, which means that you can give the `Game` a starting state based on mocked `Player`s, `Deck`s, etc. and then change some variable and watch how the `Game` changes in response.
arootbeer