Discussion of testing theory and practice, including methodologies (such as TDD, BDD, DDD, Agile, XP) and software - anything to do with testing goes here. (Formerly "The Testing Side of Development")
I really am rusty! Sheesh! I can't think of a good way to test this. Basically I have a deck of cards and I want to make sure that its shuffle() method shuffles the cards. The testing code doesn't have access to the internal array of the class though, so how do I test that the order isn't the same?
$deck = new Cards\Deck;
$deck->shuffle(); // randomizes internal $deck->_cards;
// now how do I know it's shuffled?
I can't just test the top card, because it could, by chance, be the same card. And the class doesn't have any external access to the cards other than deal() which pops off the top card, so how do I test this? Should I add something like Cards\Deck::getCards() that returns the cards array?
namespace Test\Cards;
class DeckTestHelper extends \Cards\Deck {
public function getCards() {
return $this->cards; // make sure $cards is protected
}
}
Or even make the subclass be the test code itself.
My next choice would be to use existing functionality, even if the test code has to work a bit harder. Like if you still need a foreach on the Deck then you can build and compare an array of cards before and after the shuffle. But it's quite possible that you'll decide that foreaching (or otherwise iterating) over the cards in the deck doesn't make sense as an appropriate use case either - it's not like anyone should be able to thumb through the deck looking at what's there.
You could serialize the results to a string and hash that. Shuffle & hash multiple times, then assert you have a minimum number of unique results. Regarding requinix's comment about avoiding a getCards() method on the class, there's nothing wrong with having that method. Why don't you want that method? If there's a temptation to get at some state during testing, there's probably a use to get at that state during production too. The need may not be immediate, but its often inevitable. e.g. maybe you need an audit feature for admins later on. Just like the test is auditing the cards, maybe an admin user needs to audit the cards after a game is played. Having the method doesn't hurt. Security concerns go in the controller, not the model which is the part you're testing.
Private state is a code smell IMO. Encapsulation isn't about disallowing the programmer from inspecting state. Its about allowing him to use the object without having to inspect the state.