如何断言已将适当的值分配给私有字段?
How to assert that a proper value was assigned to a private field?
我有以下 class:
public class Game {
@Getter
private String gameId;
private String player1Id;
private String player2Id;
private String currentPlayer;
private Board board;
public Game() {
board = new Board();
gameId = UUID.randomUUID().toString();
}
public void joinGame(String playerUUID) {
if (player1 == null) {
player1 = playerUUID;
currentPlayer = player1;
} else if (player2 == null) {
player2 = playerUUID;
} else {
throw new IllegalArgumentException("Cannot join");
}
}
.....
}
我想测试 joinGame() 方法中的逻辑:
@Test
void testJoinGame() {
String player1Id = UUID.randomUUID().toString();
String player2Id = UUID.randomUUID().toString();
game.joinGame(player1Id);
game.joinGame(player2Id);
// this won't compile of course as fields are private
assertEquals(player1Id, game.player1Id);
assertEquals(player2Id, game.player2Id);
assertEquals(player1Id, game.currentPlayer);
}
在我的 JUnit 测试中有两件我不应该做的坏事,因为我经常读到:
1. 更改字段的可见性以使测试工作。
2. 使用反射获取私有字段的值。
(我可以看到这两种方法在我从事的项目中被大量使用,但我们假设它们没有被使用)。
此外,我有时读到如果我想测试我的私有字段,则意味着 class 的接口定义错误。
但我要说的是,在这个例子中情况并非如此:两个用户 ID 仅在 class 内部使用,没有必要公开它们。
我仍然想确保顺序是正确的:第一个尝试加入游戏的用户将是 player1(+ he/she 将是当前用户,例如将迈出第一步),第二个 - player2 .
我想问一下测试这种方法的正确解决方案是什么?
有两种明显的方法。
测试实际使用值的位置。如果变量没有在 class 之外使用,为什么它们在那里?在提供的代码中,您只需要测试是否正确地获得了 IllegalArgumentException
。
添加 "get" 方法。
我更喜欢第一个。
我同意 Tom Hawtin 的回答,但我会反过来说:
单元测试通常应避免查看被测 class 的内部实现细节。他们应该测试 class 如何与抽象边界之外的事物交互。
如果您在单元测试中查看 "inside the box",那么如果实现发生变化,测试很可能会不必要地中断。是的,然后您可以修复测试,但是您会遇到非测试代码是否也被破坏的问题。
另一方面,查看 "inside the box" 可能 更容易编写测试用例。如果您决定采用这种方法:
添加您的单元测试用例可以使用的 getter 将允许其他代码依赖于实现细节。这是一个坏主意,即使 getter 是以防止修改的方式实现的。 (您仍然有潜在的不需要的耦合,并且更改您的 classes 实现细节会破坏事物)。
也可以使用反射检查测试用例中的私有字段。这是 丑陋的 ,但可以说比 getter 更好,因为 "normal" 代码不会这样做。实现更改仍然容易破坏单元测试......但仅限于单元测试。
我有以下 class:
public class Game {
@Getter
private String gameId;
private String player1Id;
private String player2Id;
private String currentPlayer;
private Board board;
public Game() {
board = new Board();
gameId = UUID.randomUUID().toString();
}
public void joinGame(String playerUUID) {
if (player1 == null) {
player1 = playerUUID;
currentPlayer = player1;
} else if (player2 == null) {
player2 = playerUUID;
} else {
throw new IllegalArgumentException("Cannot join");
}
}
.....
}
我想测试 joinGame() 方法中的逻辑:
@Test
void testJoinGame() {
String player1Id = UUID.randomUUID().toString();
String player2Id = UUID.randomUUID().toString();
game.joinGame(player1Id);
game.joinGame(player2Id);
// this won't compile of course as fields are private
assertEquals(player1Id, game.player1Id);
assertEquals(player2Id, game.player2Id);
assertEquals(player1Id, game.currentPlayer);
}
在我的 JUnit 测试中有两件我不应该做的坏事,因为我经常读到: 1. 更改字段的可见性以使测试工作。 2. 使用反射获取私有字段的值。 (我可以看到这两种方法在我从事的项目中被大量使用,但我们假设它们没有被使用)。
此外,我有时读到如果我想测试我的私有字段,则意味着 class 的接口定义错误。
但我要说的是,在这个例子中情况并非如此:两个用户 ID 仅在 class 内部使用,没有必要公开它们。 我仍然想确保顺序是正确的:第一个尝试加入游戏的用户将是 player1(+ he/she 将是当前用户,例如将迈出第一步),第二个 - player2 .
我想问一下测试这种方法的正确解决方案是什么?
有两种明显的方法。
测试实际使用值的位置。如果变量没有在 class 之外使用,为什么它们在那里?在提供的代码中,您只需要测试是否正确地获得了
IllegalArgumentException
。添加 "get" 方法。
我更喜欢第一个。
我同意 Tom Hawtin 的回答,但我会反过来说:
单元测试通常应避免查看被测 class 的内部实现细节。他们应该测试 class 如何与抽象边界之外的事物交互。
如果您在单元测试中查看 "inside the box",那么如果实现发生变化,测试很可能会不必要地中断。是的,然后您可以修复测试,但是您会遇到非测试代码是否也被破坏的问题。
另一方面,查看 "inside the box" 可能 更容易编写测试用例。如果您决定采用这种方法:
添加您的单元测试用例可以使用的 getter 将允许其他代码依赖于实现细节。这是一个坏主意,即使 getter 是以防止修改的方式实现的。 (您仍然有潜在的不需要的耦合,并且更改您的 classes 实现细节会破坏事物)。
也可以使用反射检查测试用例中的私有字段。这是 丑陋的 ,但可以说比 getter 更好,因为 "normal" 代码不会这样做。实现更改仍然容易破坏单元测试......但仅限于单元测试。