Balancing Design Principles: Unit Testing
Asked Answered
S

8

2

I am writing a simulation of Bananagrams. Currently, I have a GameMaster class that maintains the common collection of pieces. The deal(Player) method deals a certain number of pieces to that player.

I want to write unit tests for this. However, at this point, I have no getters, and thus no way to check the status of the objects.

Why not add getters? I don't want to add code to the public interface only for testing. Right now, there is no other reason to expose those functions.

What am I supposed to do here? Add the getters anyway, cluttering the public API (or hoping that they will be needed in the future)? Forgo unit testing? (Sounds like a bad idea.)

Or, does this indicate that my public interface is flawed?

Subversive answered 26/12, 2009 at 6:23 Comment(2)
Duplicate: #35071Amathiste
Not really, that one was more about testing private methods, while this question is really more about the difference between state-based and interaction testing (even if the asker didn't realize it).Fireplug
F
3

Instead of relying upon a concrete instance of a Player, write your unit tests to an interface (or the equivalent), and rely upon mocks or other interaction to validate not that the player is in the correct state, but simply that the correct calls (from the view of the GameMaster class.

If you cannot validate correct behavior of the GameMaster class without relying upon validating the end state of the Player, that's a sign that your responsibilities are misplaced. The GameMaster should be responsible for telling the Player what happened, while the Player should be responsible for taking the appropriate action.

This is also a benefit, as it means that the tests for the GameMaster will be dependent only on the behavior of the GameMaster class, and will not need to be touched if the Player class changes its behavior.

Avoid adding getters for unit tests. When you're tempted to add a getter, look instead at using interaction testing (as I just described) instead of state-based testing.

Fireplug answered 26/12, 2009 at 6:46 Comment(2)
ok, so how exactly do I test the interaction happens correctly without looking at the state?Subversive
If you can, just use an existing mocking framework. Otherwise make your own fake implementation that will just verify what calls were made, and with what parameters.Fireplug
A
2

There are more than one means of verifying that a piece of code works. The first one most of us think of is state based testing (ie, using getters to verify that your object's final state is what you think it should be). However, another way of verifying that your code works is to use behavior or interaction based testing.

Martin fowler has a decent article about the difference here

Angel answered 27/12, 2009 at 21:54 Comment(0)
P
0

You should test the functionality that is internally using these state holding variables. If no public functionality is using them, then they are dead code.

Penner answered 26/12, 2009 at 6:49 Comment(1)
Tests for GameMaster should not depend upon the behavior of the Player class. Testing the internal variables of a Player couples the GameMaster tests to the Player implementation - which should be avoided.Fireplug
B
0

I'd say that your classes may be doing too much. It sounds like you have them storing state AND doing other stuff.

Consider what would make them easier to test. How you would test them if you split the state and logic apart? Instead of just calling

GameMaster gameMaster = new GameMaster;
playerOne.Score = gameMaster.ComputePlayerScore(newScore);

you'd pass the state-only instance of GameState into the constructor of the GameMaster routine:

GameState gameState = new GameState;    
GameMaster gameMaster = new GameMaster(gameState);
playerOne.Score = gameMaster.ComputePlayerScore(newScore);

Then your unit test routines can pass in whatever data they need for gameState and newScore, and check the results in gameState after it returns. Dependency Injection is your friend.

Bilberry answered 26/12, 2009 at 6:55 Comment(0)
C
0

One thing you could do is subclass your main class to provide the getters for testing. This would be easier if you had interfaces to work with.

Subclassing to provide getters is a semi-dangerous proposition. If you are simply providing getters for internal properties the damage will be minimal, but you need to be careful that you are testing the actual class and not your derived version.

Catchup answered 26/12, 2009 at 6:55 Comment(0)
A
0

I see nothing wrong with adding code specifically for testability. Unit tests are invaluable for regression testing.

However, it's also correct that it shouldn't affect your public API. Therefore, I suggest making the getters package-protected, and placing your unit tests in the same package as the Player class. (In a different source folder though, so that you have clean separation between production code and test code.)

Ashe answered 26/12, 2009 at 10:37 Comment(0)
C
0

unit tests don't make your code better , they make your whole application more stable over time with changes and added features. this is why if you do not have public getters in your GameMaster class you do not need to create them for unit tests.

no getters does not mean your interface is flowed ,test driven development concept is write minimum code to pass a test ( that comes from a requirement ) , if you don't needem you don't writem . print, trace, log will be here long after unit tests vanish ( ok they are also here to stay , but in many cases overated and overused )

Calcine answered 27/12, 2009 at 21:34 Comment(0)
M
-6

What am I supposed to do here? Add the getters anyway, cluttering the public API (or hoping that they will be needed in the future)?

I don't get this response. How is a getter "clutter"?

You're doing Test Driven Development. The design is driven by the absolute need to test everything.

Getters aren't "clutter". They're how you test things.

"Forgo unit testing?"

I don't think this is a good idea.

Mattah answered 26/12, 2009 at 6:39 Comment(6)
-1 for unnecessary jerkitude toward a very reasonable question.Radiotelegraph
The getter is clutter because it is something in the public API that exists only for testing.Subversive
@Rosarch: While I agree with you in this case, in some cases, testing will end up changing APIs in various ways. That's okay, so long as it improves design - which you seem to be asserting is not the case for this instance (and which I agree with - state-based testing is probably not the way I'd go)Fireplug
@Phoebus: Are you bothered by my "Brilliant" but not bothered by the question's "forgo unit testing?" I'm not sure how "forgo unit testing" isn't worthy of downvotes and "bad question" comments. Indeed, the "forgo X" gambit is often a way of saying "see, I told that technique X was useless." Perhaps I'm overly sensitive to the question, but, as a consultant, I see companies avoiding best practices because one noisy person has identified something like this that they have portrayed as a "contradiction" subverting efforts at change and improvement.Mattah
some people feel a getter is clutter if it is only added to facilitate testing. especially people who are aware of interaction or behavior based test verification, like using mock objects. The "i told you XXX was useless" straw man type arguments are quite annoying but you probably shouldn't dump all the frustration of a bad day at work onto some poor SO poster :)Angel
OP didn't actually say TDD was the model. In fact, there is already code and OP wants to add unit tests. This implies that TDD is not the lifecycle of choice. Seems to me they are just trying to learn more about unit testing in generalEphemera

© 2022 - 2024 — McMap. All rights reserved.