我正在尝试为我的一些类型创建Arbitrary实例,以便在QuickCheck属性测试中使用。我需要随机生成的UUID,其约束是不允许全零(nil) UUIDs即00000000-0000-0000-0000-000000000000。因此,我设置了以下生成器:
nonzeroIdGen :: Gen UUID.UUID
nonzeroIdGen = arbitrary `suchThat` (not . UUID.null)我在Arbitrary实例中使用它,如下所示:
instance Arbitrary E.EventId where
arbitrary = do
maybeEid <- E.mkEventId <$> nonzeroIdGen
return $ fromJust maybeEid一般来说,这是不安全的代码;但是对于测试来说,假设有非零UUID,我认为fromJust是正常的。
mkEventId被定义为
mkEventId :: UUID.UUID -> Maybe EventId
mkEventId uid = EventId <$> validateId uid使用EventId对UUID.UUID进行了新的类型包装,并且
validateId :: UUID.UUID -> Maybe UUID.UUID
validateId uuid = if UUID.null uuid then Nothing else Just uuid令我惊讶的是,由于上面的代码生成了全零的UUID,我得到了失败的测试。mkEventId中的trace显示以下内容:
00000001-0000-0001-0000-000000000001
Just (EventId {getEventId = 00000001-0000-0001-0000-000000000001})
00000000-0000-0000-0000-000000000000
Nothing
Create valid Events. FAILED [1]第一个生成的ID是正常的,第二个是全零的,尽管我在上面生成了nonzeroIdGen生成器。我遗漏了什么?
发布于 2020-05-12 19:33:47
我通常发现,在这样的情况下,使用newtype定义Arbitrary的实例会更好。下面是我为有效的UUID值创建的一个值:
newtype NonNilUUID = NonNilUUID { getNonNilUUID :: UUID } deriving (Eq, Show)
instance Arbitrary NonNilUUID where
arbitrary = NonNilUUID <$> arbitrary `suchThat` (/= nil)然后,您可以从这个实例组合其他Arbitrary实例,就像我在这里对Reservation数据类型所做的那样:
newtype ValidReservation =
ValidReservation { getValidReservation :: Reservation } deriving (Eq, Show)
instance Arbitrary ValidReservation where
arbitrary = do
(NonNilUUID rid) <- arbitrary
(FutureTime d) <- arbitrary
n <- arbitrary
e <- arbitrary
(QuantityWithinCapacity q) <- arbitrary
return $ ValidReservation $ Reservation rid d n e q请注意,模式匹配(NonNilUUID rid) <- arbitrary将rid解构为UUID值。
您可能注意到,我还为我的Reservation数据类型创建了一个ValidReservation newtype。I依赖(I consistently do this to avoid orphan instances ),并避免使用QuickCheck依赖来污染我的域模型。(我并不反对QuickCheck,但是特定于测试的功能不属于“生产”代码。)
这里显示的所有代码都是available in context on GitHub。
https://stackoverflow.com/questions/61747155
复制相似问题