0

我开始制作一个随机创建地下城的地下城游戏。如何在函数式编程中避免或测试随机性

当前我编写函数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

您可以尝试把一些具体的数字,而不是getRandomNumber的的 –

回答

2

那么,首先我假设你的getRandomNumber实际上是一个伪随机种子为基础的发电机与全球种子。为了使它更加符合真正的 FP的精神,你需要使种子/发生器通过和变异明确,但这绝对不是你必须做的事情。

现在,答案取决于你想测试什么。如果你需要确保你的随机世代为一个给定的种子提供了相同的值(当你想要像Minecraft那样的“世界种子”时这很重要),那么就足以对种子进行硬编码,然后继续进行已知输出。

一个重要的注意事项是,当使用全局随机数生成器时,每个“抽出”的数字都会影响未来的数字。这意味着如果您稍后更改测试代码以包含之前的其他一些之前的测试用例,那么您的硬编码值将完全不匹配。您可以通过确保所有独立测试运行以新的生成器开始来减轻这种情况。

如果您想以更“合理”的方式测试行为,也就是说,功能一般是否正常,您需要使用更多的种子并多次运行它。现在种子本身是随机还是硬编码并不重要;最重要的区别是您的验证规则现在无法测试特定的值相等性,而是需要检查边界或其他范围条件。

1

一般来说,测试执行应该是确定性的和可重复的,因此应该避免处理测试中的随机性(有一本很好的书,Lasse Koskela的“有效单元测试”,您可以在这里找到如何处理测试中的随机性,还有许多其他问题)。

这意味着,例如,如果您编写测试以检查createDungeon(3)的结果是什么,那么调用的结果应该始终相同,因此您对该结果的断言总是有效的。

我可以提出一个小重构,在你的榜样:

function createDungeon (numberOfRooms, randomGenerator, rooms = []) { 
    ... 
    const randomWidth = randomGenerator.getRandomNumber(5, 10) 
    ... 
} 

在你的测试,你通过测试双(模仿对象)为:随机数发生器应作为参数传递给createDungeon传递您的randomGenerator,以前设置为返回一些已知的值。您可以像使用JsMockito一些模拟框架,然后你可以这样做:

generator = mock(RandomGenerator); 
when(generator).getRandomNumber(5,10).thenReturn(8); 
// run the test 
generateDungeon(3, generator); 
// verify results 
assert(...) 
相关问题