平衡设计原则:单元测试

发布于 2024-08-16 06:15:20 字数 301 浏览 4 评论 0原文

我正在编写 Bananagrams 的模拟。目前,我有一个 GameMaster 类来维护公共的棋子集合。 deal(Player) 方法向该玩家发一定数量的棋子。

我想为此编写单元测试。然而,此时我没有吸气剂,因此无法检查对象的状态。

为什么不添加吸气剂?我不想仅将代码添加到公共接口以进行测试。目前,没有其他理由公开这些功能。

我应该在这里做什么?无论如何添加吸气剂,使公共 API 变得混乱(或者希望将来需要它们)?放弃单元测试? (听起来是个坏主意。)

或者,这是否表明我的公共界面有缺陷?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

鲜肉鲜肉永远不皱 2024-08-23 06:15:20

不要依赖于 Player 的具体实例,而是将单元测试编写到接口(或等效项),并依赖模拟或其他交互来验证玩家是否处于正确的状态,而只是验证正确的调用(从 GameMaster 类的角度来看,

如果您无法在不验证 Player 的最终状态的情况下验证 GameMaster 类的正确行为,则表明您的职责不正确。发生了什么,而玩家应该负责采取适当的操作,

这也是一个好处,因为这意味着对 GameMaster 的测试将仅依赖于 GameMaster 类的行为,并且不需要。如果 Player 类更改其行为,

请避免为单元测试添加 getter。当您想添加 getter 时,请考虑使用交互测试(正如我刚才所描述的)而不是基于状态的测试。

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.

烟沫凡尘 2024-08-23 06:15:20

验证一段代码是否有效的方法不止一种。我们大多数人首先想到的是基于状态的测试(即使用 getter 来验证对象的最终状态是否是您认为应该的状态)。但是,验证代码是否有效的另一种方法是使用基于行为或交互的测试。

Martin Fowler 在这里写了一篇关于差异的不错的文章

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

暖树树初阳… 2024-08-23 06:15:20

您应该测试内部使用这些状态保存变量的功能。如果没有公共功能使用它们,那么它们就是死代码。

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.

南风几经秋 2024-08-23 06:15:20

我想说的是,你的课程可能做得太多了。听起来你让它们存储状态并做其他事情。

考虑什么可以让它们更容易测试。如果将状态和逻辑分开,您将如何测试它们? 而不仅仅是调用

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

您可以将 GameState 的纯状态实例传递到 GameMaster 例程的构造函数中,

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

:然后您的单元测试例程可以传入 gameState 和 newScore 所需的任何数据,并在 gameState 返回后检查 gameState 中的结果。依赖注入是你的朋友。

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.

秉烛思 2024-08-23 06:15:20

您可以做的一件事是对主类进行子类化以提供用于测试的吸气剂。如果您有可以使用的接口,这会更容易。

子类化以提供吸气剂是一个半危险的提议。如果您只是为内部属性提供吸气剂,那么损害将是最小的,但您需要小心,您正在测试实际类,而不是您的派生版本。

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.

月隐月明月朦胧 2024-08-23 06:15:20

我认为专门为可测试性添加代码没有什么问题。单元测试对于回归测试来说是无价的。

但是,它不应该影响您的公共 API,这也是正确的。因此,我建议对 getter 进行包保护,并将单元测试放在与 Player 类相同的包中。 (不过,在不同的源文件夹中,这样您就可以将生产代码和测试代码完全分开。)

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.)

倾城花音 2024-08-23 06:15:20

单元测试不会使您的代码变得更好,它们会使您的整个应用程序随着时间的推移通过更改和添加功能变得更加稳定。这就是为什么如果您的 GameMaster 类中没有公共 getter,则无需为单元测试创​​建它们。

没有吸气剂并不意味着你的界面是流畅的,测试驱动开发的概念是编写最少的代码来通过测试(来自需求),如果你不需要它们,你就不会编写它们。在单元测试消失很久之后,打印、跟踪、日志将在这里(好吧,它们也将留在这里,但在许多情况下被过度使用和过度使用)

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 )

闻呓 2024-08-23 06:15:20

我在这里应该做什么?无论如何添加吸气剂,使公共 API 变得混乱(或者希望将来需要它们)?

我没有得到这个回应。 getter 如何变得“混乱”?

您正在进行测试驱动开发。该设计是由测试一切的绝对需要驱动的。

Getter 并不“混乱”。它们是你测试事物的方式。

“放弃单元测试?”

我认为这不是一个好主意。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文