我开始制作一个创建随机新地下城的地下城游戏。
目前我编写了函数createRoom
,它根据参数返回一个新的房间对象。此函数在createDungeon
中调用,但它会在那里接收随机参数。
function createRoom (width = 1, height = 1, topLeftCoordinate = {x: 0, y: 0}) {
return {
width,
height,
topLeftCoordinate
}
}
function createDungeon (numberOfRooms, rooms = []) {
if (numberOfRooms <= 0) {
return rooms
}
const randomWidth = getRandomNumber(5, 10)
const randomHeight = getRandomNumber(5, 10)
const randomTopLeftCoordinate = {x: getRandomNumber(5, 10), y: getRandomNumber(5, 10)}
return createDungeon (
numberOfRooms - 1,
rooms.concat(Room.create(randomWidth, randomHeight, randomTopLeftCoordinate))
)
}
我不知道这是否正确,因为我不知道如何测试createDungeon
。我只能测试这个函数是否返回一个数组和数组的长度。这是否足够或是否存在随机性的设计模式?
答案 0 :(得分:1)
好吧,首先我假设你的getRandomNumber
实际上是一个带有全局种子的伪随机种子生成器。为了使它更符合 true FP的精神,你需要使种子/生成器传递和变异显式,但这不是你绝对必须要做的事情。
现在,答案取决于您要测试的内容。如果你需要确保你的随机生成为给定的种子提供相同的值(这对于你想拥有像Minecraft那样的“世界种子”很重要),那么就足以对种子进行硬编码然后继续进行已知的输出
一个重要的注意事项是,当使用全局随机数生成器时,“抽出”它的每个数字都会影响未来的数字。这意味着如果您稍后更改测试代码以在之前的之前包含一些其他数字,那么您的硬编码值将完全不匹配。您可以通过确保所有独立测试运行以新生成器开始来缓解这种情况。
如果您想以更“合理”的方式测试行为,也就是说,无论函数通常是否正常,您都需要使用更多种子并多次运行。现在种子本身是随机的还是硬编码的并不重要;重要的区别在于您的验证规则现在无法测试特定的值相等,而是需要检查边界或其他一些范围标准。
答案 1 :(得分:0)
一般来说,测试执行应该是确定性的和可重复的,因此应该避免在测试中处理随机性(有一本很棒的书,Lasse Koskela的“有效单元测试”,在那里你可以找到如何处理测试中的随机性,在许多其他问题中)。
这意味着,例如,如果您编写测试以检查override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(true)
navigationController?.setNavigationBarHidden(true, animated: true)
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(true)
navigationController?.setNavigationBarHidden(false, animated: false)
}
的结果是什么,则该调用的结果应始终相同,因此您对该结果的断言始终有效
在你的例子中,我可以建议一个小的重构:随机数生成器应作为参数传递给createDungeon:
createDungeon(3)
在测试中,为function createDungeon (numberOfRooms, randomGenerator, rooms = []) {
...
const randomWidth = randomGenerator.getRandomNumber(5, 10)
...
}
传递测试双精度(模拟对象),之前设置为返回一些已知值。您可以使用像JsMockito这样的模拟框架,然后您可以执行以下操作:
randomGenerator