4

I'm totally new to TDD and am using NUnit to write tests for a Card Game application in writing in C#.

Mostly its proving really useful, but there are some things I'd like to test, but don't wish to expose any public properties from which to do so.

For example, I'd like to be able to test that a new card deck consists of 4 suits, each with 13 cards. it might look something like:

[Test()]
public void ADeckOfCardsHas4Suits()
{
   Game game = new Game();
   Assert.AreEqual(4, game.Deck.SuitCount);
   Assert.AreEqual(13, game.Deck.GetCards(Suit.Spades).Count)
}

With SuitCount returning something like:

return this.cards.Select(c => c.Suit).Distinct().Count();

And GetCards(Suit suit):

return this.cards.where(c => c.Suit == suit);

However I don't want to expose these properties or methods in the API to the UI (I may not even wish to expose Deck), but am not sure how I can test against them without making them public.

Are there any generally accepted ways of doing this?

Cheers

Stewart

Péter Török
  • 114,404
  • 31
  • 268
  • 329
Stewart Alan
  • 1,521
  • 5
  • 23
  • 45
  • possible duplicate of [How do you test private methods with NUnit?](http://stackoverflow.com/questions/249847/how-do-you-test-private-methods-with-nunit) – Steve Townsend May 07 '12 at 15:33

5 Answers5

1

In this scenario we often use (abuse?) friend assemblies to allow the test project to see the internal members of the class under test.

Edit:

In your main project add a FriendAssemblies.cs file:

using System.Runtime.CompilerServices;

// making internals available to the testing project
[assembly: InternalsVisibleTo( "MyProject.IntegrationTests" )]
[assembly: InternalsVisibleTo( "MyProject.UnitTests" )]
Mike Parkhill
  • 5,511
  • 1
  • 28
  • 38
  • I wouldn't call it abusing, though. I think that it is a legitimate call to expose a product assembly's internals to its test assembly. – Assaf Stone May 08 '12 at 05:48
0

Certainly clients of your card deck should be able to access and iterate through the cards in a given desk, shouldn't they? I.e. the deck should publish something like an IEnumerable<Card>.

If so, your unit tests can grab this and use it to count the number of distinct suits. The methods SuitCount and GetCardsOfSuit should thus be implemented as helpers in your unit test code.

Péter Török
  • 114,404
  • 31
  • 268
  • 329
  • Why should they? What if he's writing a multi-client server and each client should only know about its own cards? – Matt Mills May 07 '12 at 15:45
  • I'm just trying to keep the API as simple as possible. There's no reason that the client needs to know how many suits are in the deck (that should be assumed), but I still need my tests to be able to confirm it. – Stewart Alan May 07 '12 at 15:48
  • Also, I'm undecided as to whether to give the client access to the cards collection at all. I'm thinking instead of just having methods such as DealNextCardFromDeck() so as not to actually need to expose the inner List at all. Bottom line is, whenever a situation occurs that you want/need to test something that cannot be exposed to client, what's the best approach? – Stewart Alan May 07 '12 at 15:54
  • I mean, I know I could just call the DealNextCardFromDeck() for every card to create a List on the client and then calculate the SuitCount from that, but it seems like a lot of legwork for the test to go through. – Stewart Alan May 07 '12 at 15:58
  • @StewartAlan, well, if you make your API cumbersome to use, it will be cumbersome to test it too. Writing unit tests in TDD fashion is in fact an excellent litmus test to detect API design problems (which IMHO is the root cause here). – Péter Török May 07 '12 at 16:22
  • @arootbeer, should that be the concern of the deck? I doubt it. IMHO the single responsibility of the deck should be to serve a full sequence of cards to its client(s) in random order, without repeats etc. Making sure that each player can only get a single card at a time, in the right order, are different responsibilities. If you put all of these in the Deck, you will get a class which is hard to (re)use and maintain. If you adhere to [SRP](http://en.wikipedia.org/wiki/Single_responsibility_principle), you get several small classes which are easier to test, maintain and (re)use. – Péter Török May 07 '12 at 16:28
  • @PéterTörök I agree, but my point remains that having a simple public means of returning the number of suits in a deck is totally unnecessary for the API, its only required by the tests so that I can be sure that internally the deck is built correctly. I am beginning to understand though that this isn't possible and that I probably need to just write a method in the test layer to get at the required properties (i.e suitcount) in as long-winded a way as necessary (i.e deal all cards from deck into a client list and test against that) – Stewart Alan May 07 '12 at 16:37
  • @PéterTörök - Apologies - I misunderstood your answer. The question was about the game itself, but you're specifically talking about the deck. With that said, I still do not think that the deck should allow *all* clients to inspect it. This sounds more like an interface segregation problem. – Matt Mills May 07 '12 at 20:27
0

You should test the public behaviour of your class. If the UI talks to a Game and needs to know how many suits there are then rather than game.Deck.SuitCount test game.GetDeckSuitCount() if you have more than 1 Deck overload the method to specify which Deck via a parameter game.GetDeckSuitCount(2). This way Deck is hidden from Game's clients and is encapsulated.

Also consider that you might need to test Deck and the services it provides to Game independently, in which case it would be preferable to supply it as a dependency to Game explicitly via Game's constructor.

This approach also allows you to depend on an interface rather than an implementation which facilitates supplying a different implementation of Deck to Game either for mocking or to allow you the flexibility to supply an alternative implementation of Deck to Game in future.

e.g.

public interface IDeck{
    // provide the methods and properties that Game requires from Deck
}

public class PokerDeck:IDeck{
    // ... etc
}

public class TravelCardsDeck:IDeck{
    // ... etc
}

// [Test]
IDeck deck = new TravelCardsDeck();
var game = new Game(deck);
Assert.That(game.GetDeckSuitCount(),Is.EqualTo(4));
Grokodile
  • 3,881
  • 5
  • 33
  • 59
  • 1
    Yes, but that still requires that I make a method GetDeckSuitCount() available to the client that isn't necessary. It's only necessary that I can test it, but exposing it to the client burdens the API with methods that exist purely for testing purposes, which doesn't seem good. – Stewart Alan May 07 '12 at 16:09
  • Why do you need to test it if no client's require it? Assuming that the UI is the sole client of Game. If you are doing TDD you only implement what's required by your tests and you only test the behaviour clients require. – Grokodile May 07 '12 at 16:16
  • Well I need to be able to test that the deck has been created correctly (i.e. consisting of 4 suits etc..), but I want the UI to assume that its dealing with a correctly formed deck and so not give it methods by which to test this. This seems like it would be a reasonably common requirement. – Stewart Alan May 07 '12 at 16:23
  • Yes, but from a TDD point of view you should be writing tests to drive the behaviour that clients require rather than tests which demonstrate that you have correctly implemented what you know to be a valid deck of cards. The correctness of your implementation should be as a result of writing the correct set of tests. You seem to be writing tests to verify that you have the implementation you want, which is backwards from a TDD perspective. – Grokodile May 07 '12 at 16:34
  • OK, well that makes sense, but this surely still leaves potentially large holes in the tests. Without this test I have no way of being sure that my deck is as the client expects/requires it. Would you say then that, as a rule, if its needs to be tested, it needs to be available to client? – Stewart Alan May 07 '12 at 16:41
  • 1
    No I'd say that as a rule if it doesn't need to be available to the client then you shouldn't write a test for it (YAGNI - you ain't gonna need it) and if you don't write a test for it then you shouldn't implement it. It's a hard fight to adjust to a pure TDD mindset, reading about [BDD](http://en.wikipedia.org/wiki/Behavior_Driven_Development) might help. Of course what you might really want is just the benefits of a regression test suite to cover yourself as you design the way you are most comfortable with at the moment which is fine, but it's not TDD per se. – Grokodile May 07 '12 at 16:48
0

In this case, I wouldn't immediately care about the content. If you model the act, you are given a set of cards that become a deck (below, a Deck takes an IEnumerable<Card>). Then you would shuffle them and deal them out.

public class Deck
{
    ...
    public Deck(IEnumerable<Card> cards) { ... Shuffle() ... }        
    public void DealTo(IDealable dealable, int numberOfCards) {...}
}

Here you create a simple test case that deals out one card:

var deck = new Deck(new Card[] { <somecard> });
var hand = new Hand(); 

deck.DealTo(hand, 1);

Assert.AreEqual(<somecard>, hand[0]);

... plus whatever tests you can think up.

Then you create a poker deck:

public class PokerDeck : Deck
{
    public PokerDeck() : base(<poker cards>)
}

// Pinochle, Uno, whatever 

So to test the PokerDeck, you would probably start with something like this:

var deck = new PokerDeck();
IDealable testHand = new TestDealable();
deck.DealTo(testHand, 52);

//assert all are distinct
//assert all make up expected deck
//assert is shuffled

// test with multiple players / "dealables"

Simulating Texas Hold'em would be something like this:

// first card
foreach(var player in players)
   deck.DealTo(player, 1);

// second
foreach(var player in players)
   deck.DealTo(player, 1);

// wait for action

// flop
deck.DealTo(burnPile, 1);
deck.DealTo(board, 3);

// wait for action

// turn
deck.DealTo(burnPile, 1);
deck.DealTo(board, 1);

// wait for action

// river
deck.DealTo(burnPile, 1);
deck.DealTo(board, 1);
Austin Salonen
  • 49,173
  • 15
  • 109
  • 139
0

As suggested, I'd use the InternalsVisibleTo attribut to allow a test assembly to exercise the production assembly's code.

I would, however, extract the internal behaviors and implementations into separate, internal classes.

This way, I can test the public API classes, without relying on any internal behavior, and if the internal behaviors change, only the behaviors' tests will be affected; if you don't do that, you will find yourself with brittle tests, and public API tests that may break due to internal implementation changes.

Assaf Stone
  • 6,309
  • 1
  • 34
  • 43