为什么你的测试应该只验证可观察的行为,而不是实现细节
文章插图
在本文中,我们将考虑我们的测试到底应该(不)验证什么以防止误报,以及为什么有时越少越好 。为了更好地理解这个主题,我们将仔细研究脆性测试和可观察行为的定义,以便我们能够检测设计不良的测试并使其抵抗重构 。
让我们开始吧!
当您的测试想知道太多时
回到过去,在我深入研究自动化测试这个主题之前,它已经发生在我身上很多次了 。究竟是什么?好吧,以防万一,我想确保我的测试验证了比必要的更多的东西 。我曾经相信我的测试包含的断言和类似的陈述越多,它们带来的价值就越大 。
虽然上述方法看起来很合理,但从长远来看,选择它会让开发人员的生活变得困难 。当我自己的测试让我不得不比我预期的更频繁地回到他们身边时,我很难发现这一点 。一个理由?事实证明,这些测试与实现细节有关,而不是可观察到的行为,因此,在重构时,即使功能仍然可以正常工作,它们也会失败 。
脆弱的测试? 可观察的行为? 实施细则?
在我们进一步讨论之前,让我们先定义一下这些神秘短语背后的含义,因为它们对于理解如何编写为我们的项目增加真正价值而不是不必要的包袱的良好测试至关重要 。
- 它们无法承受重构,无论底层功能是否损坏,它们都会变红
- 要使一段代码成为系统可观察行为的一部分,它必须执行以下操作之一:
- 公开帮助客户实现其目标之一的操作 。操作是一种执行计算或产生副作用或两者兼而有之的方法 。
- 暴露一种可以帮助客户实现其目标之一的状态 。状态是系统的当前状态 。
- 任何不做这两件事的代码都是实现细节 。
这种方法应该让您在可观察的行为和实现细节之间有一个更清晰的区别 。
案例研究:排行榜
让我们仔细看看下面用 JAVA 编写的示例:
- 我们正在为一款游戏开发排行榜,我们称这款游戏为“Chase and Race”
- 我们希望我们的排行榜根据得分返回最佳玩家
Leaderboard 类允许我们通过 Leaderboard#addPlayer 函数将玩家添加到排行榜的列表中,并通过 Leaderboard#getBestPlayer 检索游戏中最好的玩家 。
在 LeaderboardTesttest 类中,我们正在检查 Leaderboard#getBestPlayer 方法是否能够返回得分最高的玩家:
package chaseandrace.player;import java.util.ArrayList;import java.util.Comparator;import java.util.List;public class Leaderboard {List<Player> players;public Leaderboard() {players = new ArrayList<>();}public void addPlayer(Player player) {this.players.add(player);}public Player getBestPlayer() {return players.stream().max(Comparator.comparing(Player::getScore)).orElse(null);}}
package chaseandrace.player;import static org.junit.jupiter.api.Assertions.assertEquals;import org.junit.jupiter.api.Test;public class LeaderboardTest {@Testvoid getPlayerWithHighestScore() {var playerOne = new Player("I don't know what I'm doing here");var playerTwo = new Player("Chase me");var playerThree = new Player("Okie Dokie");playerOne.updateScore(50);playerTwo.updateScore(90);playerThree.updateScore(85);var board = new Leaderboard();board.addPlayer(playerOne);board.addPlayer(playerTwo);board.addPlayer(playerThree);var bestPlayer = board.getBestPlayer();assertEquals(playerTwo, bestPlayer);assertEquals(bestPlayer, board.players.get(1));}}
package chaseandrace.player;public class Player {private String name;private int score;public Player(String name) {this.name = name;}public int getScore() {return score;}public void updateScore(int points) {score += points;}public String getName() {return name;}public void setName(String name) {this.name = name;}}
推荐阅读
- go-micro集成链路跟踪的方法和中间件原理
- 如何恢复网站浏览记录?
- 计量检测中心校准证书上的CMA、CNAS、CAL有什么意义,区别在哪?
- 英式红茶泡几次,英式罐装红茶的泡法
- 明星|王牌:化妆师什么审美?给明星化妆,像是在脸上“刮大白”
- 饵料|《师傅请赐教》中年男人解压方式竟是夜钓?背后原因让人深思
- 中国最美乡村全集
- 中国最美丽的村庄是什么
- 冲红茶泡中间,润元昌红茶系列
- 骁龙8|中兴Axon 40 Pro官方渲染图首度公布:“双圆环”后摄模组抢眼