Reactive Banana 1.0.0 - MomentIO() Monad 中的单元测试
Reactive Banana 1.0.0 - Unit testing in the MomentIO() Monad
这是我一直用来研究特定单子操作行为的事件网络示例。我想要一种有原则的方法,而不是这种测试我的代码的临时方法。我知道如何测试我的功能,但我正在寻找测试行为和事件的最佳实践,考虑到 reactive-banana 1.0.0
中的新设计选择
我遗漏了很多内容,希望只包含说明我的问题所必需的内容。请让我知道是否有遗漏的内容应该包括在内以使问题更清楚。
makeNetworkDescription :: Parameters -> MomentIO ()
makeNetworkDescription params = mdo
eInput <- fromAddHandler (input params)
eTick <- fromAddHandler (tick params)
let
eValidated :: Event VAC
eValidated = toVAC <$> eInput
eClearBuffer = Clear <$ eBuffer
eBuffer ::Event BufferMap
eBuffer = bBuffer <@ eTick
bBuffer <- accumB (BufferMap (M.empty :: M.Map AID VAC)) $
manageBuffer <$> unionWith (clearBuffer) eValidated eClearBuffer
reactimate $ writeOut_Debug <$> eBuffer
缓冲区应该做的是累积玩家命令(然后在其他地方处理),然后在处理特定批次的玩家命令后清空。在下一次报价时,它会再次发生。
我希望确保缓冲区在应有的时间被清除,并按应有的方式累积命令。现在,代码可以运行,我想编写测试以确保它在我构建这个游戏时继续运行。
我可以在上面的示例中将缓冲区 Behavior
与 Event
网络分开,但是那又如何呢?从测试中获得准确结果的最佳方法是什么?
编辑:更新 - 我相信 this link 会提供足够的提示。我会尝试一下,明天报告更多细节。
编辑:更新 - 我写了一个单元测试。我会上传到github,然后上传到post。上面的link对理清要做什么很有帮助。
编辑:更新 - 事实证明,如果您 运行 堆栈测试存在类型错误,然后您再次 运行 它,您会得到表明您的测试已通过的输出。结果是,我没有比昨天更接近。我有代码和一个更清晰的问题。我可能会为 that.ct
开始一个不同的 post
编辑:更新 - 我有一个以有用的方式中断的测试,但我不知道该怎么做。我已经 post 编辑了整个 project 的上下文。下面我只包括测试代码、错误和一些讨论。
main :: IO ()
main = defaultMain
[ testGroup "EventNetwork Input"
[testBuffer "bBuffer" Populated]
]
testBuffer :: String -> BufferState -> Test
testBuffer name Populated =
testCase name $ assert $ bufferPopulated (UAC (PlayerCommand (Move (ToPlanetName Mongo)) (AID (Data.Text.pack "100"))))
testBuffer name Empty =
testCase name $ assert $ bufferEmptied (UAC (PlayerCommand (Move (ToPlanetName Mongo)) (AID (Data.Text.pack "100"))))
bufferPopulated :: UAC -> MomentIO Bool
bufferPopulated ev = do
let eInput = ev <$ never
eValidated = toVAC <$> eInput
bBufferMap <- (buffer eValidated eClear) :: MomentIO (Behavior BufferMap)
let r2 = [(Just $ BufferMap $ M.insert (AID (Data.Text.pack "100")) (toVAC ev) (M.empty :: M.Map AID VAC))]
r1 <- liftIO $ ((interpret (eBuffer bBufferMap) []) :: IO [Maybe BufferMap])
return $ r1 == r2
bufferEmptied :: UAC -> MomentIO Bool
bufferEmptied ev = undefined
eBuffer :: Behavior BufferMap -> Event a -> Event BufferMap
eBuffer bBufferMap nvr =
bBufferMap <@ (() <$ nvr)
eClear = Clear <$ (() <$ never)
tests/Spec.hs:26:19:
No instance for (Test.HUnit.Base.Assertable (MomentIO Bool))
arising from a use of ‘assert’
In the expression: assert
In the second argument of ‘($)’, namely
‘assert
$ bufferPopulated
(UAC
(PlayerCommand (Move (ToPlanetName Mongo)) (AID (pack "100"))))’
In the expression:
testCase name
$ assert
$ bufferPopulated
(UAC
(PlayerCommand (Move (ToPlanetName Mongo)) (AID (pack "100"))))
问题归结为 accumB
在 MomemtIO
中创建 Behavior
。如果我有 bufferPopulated
return 和 IO Bool
我该如何协调?
编辑:显而易见的事情是编写它想要的实例。我认为这可能是一条红鲱鱼。你觉得怎么样。这只是写 MomentIO Bool
实例那么简单吗?
编辑:更新
我认为我走在正确的轨道上。我已经注释掉所有测试工具代码并更改了 bufferPopulated
的签名
bufferPopulated :: UAC -> IO Bool
bufferPopulated ev = do
let eInput = ev <$ never
eValidated = toVAC <$> eInput
bBufferMap <- liftMoment ((buffer eValidated eClear) :: Moment (Behavior BufferMap))
let r2 = [(Just $ BufferMap $ M.insert (AID (Data.Text.pack "100")) (toVAC ev) (M.empty :: M.Map AID VAC))]
r1 <- (interpret (eBuffer bBufferMap) []) :: IO [Maybe BufferMap])
return $ r1 == r2
我相信这应该可行,但这是错误
tests/Spec.hs:35:17:
No instance for (MonadMoment IO) arising from a use of ‘liftMoment’
In a stmt of a 'do' block:
bBufferMap <- liftMoment
((buffer eValidated eClear) :: Moment (Behavior BufferMap))
让我们看看MonadMoment
来自Reactive.Banana.Combinators
class Monad m => MonadMoment m where
An instance of the MonadMoment class denotes a computation that happens at one particular moment in time.
Unlike the Moment monad, it need not be pure anymore.
Methods
liftMoment :: Moment a -> m a
Instances
MonadMoment MomentIO
MonadMoment Moment
m
可以是任意的Monad
,IO
是一个Monad
。所以 liftMoment
应该将 Moment Behavior (BufferMap)
提升到 IO Behavior (BufferMap)
,为什么不呢。我的推理有什么问题?
答案来源于这个之前的答案。
Testing in reactive-banana
interpretFramwork
需要新签名。
interpretFrameWorks'' :: (Event a -> MomentIO (Behavior b)) -> [a] -> IO (b,[[b]])
interpretFrameWorks'' f xs = do
output <- newIORef []
init <- newIORef undefined
(addHandler, runHandlers) <- newAddHandler
network <- compile $ do
e <- fromAddHandler addHandler
f' <- f e
o <- changes $ f'
i <- valueB $ f'
liftIO $ writeIORef init i
reactimate' $ (fmap . fmap) (\b -> modifyIORef output (++[b])) o
actuate network
bs <- forM xs $ \x -> do
runHandlers x
bs <- readIORef output
writeIORef output []
return bs
i <- readIORef init
return (i, bs)
这是我一直用来研究特定单子操作行为的事件网络示例。我想要一种有原则的方法,而不是这种测试我的代码的临时方法。我知道如何测试我的功能,但我正在寻找测试行为和事件的最佳实践,考虑到 reactive-banana 1.0.0
我遗漏了很多内容,希望只包含说明我的问题所必需的内容。请让我知道是否有遗漏的内容应该包括在内以使问题更清楚。
makeNetworkDescription :: Parameters -> MomentIO ()
makeNetworkDescription params = mdo
eInput <- fromAddHandler (input params)
eTick <- fromAddHandler (tick params)
let
eValidated :: Event VAC
eValidated = toVAC <$> eInput
eClearBuffer = Clear <$ eBuffer
eBuffer ::Event BufferMap
eBuffer = bBuffer <@ eTick
bBuffer <- accumB (BufferMap (M.empty :: M.Map AID VAC)) $
manageBuffer <$> unionWith (clearBuffer) eValidated eClearBuffer
reactimate $ writeOut_Debug <$> eBuffer
缓冲区应该做的是累积玩家命令(然后在其他地方处理),然后在处理特定批次的玩家命令后清空。在下一次报价时,它会再次发生。
我希望确保缓冲区在应有的时间被清除,并按应有的方式累积命令。现在,代码可以运行,我想编写测试以确保它在我构建这个游戏时继续运行。
我可以在上面的示例中将缓冲区 Behavior
与 Event
网络分开,但是那又如何呢?从测试中获得准确结果的最佳方法是什么?
编辑:更新 - 我相信 this link 会提供足够的提示。我会尝试一下,明天报告更多细节。
编辑:更新 - 我写了一个单元测试。我会上传到github,然后上传到post。上面的link对理清要做什么很有帮助。
编辑:更新 - 事实证明,如果您 运行 堆栈测试存在类型错误,然后您再次 运行 它,您会得到表明您的测试已通过的输出。结果是,我没有比昨天更接近。我有代码和一个更清晰的问题。我可能会为 that.ct
开始一个不同的 post编辑:更新 - 我有一个以有用的方式中断的测试,但我不知道该怎么做。我已经 post 编辑了整个 project 的上下文。下面我只包括测试代码、错误和一些讨论。
main :: IO ()
main = defaultMain
[ testGroup "EventNetwork Input"
[testBuffer "bBuffer" Populated]
]
testBuffer :: String -> BufferState -> Test
testBuffer name Populated =
testCase name $ assert $ bufferPopulated (UAC (PlayerCommand (Move (ToPlanetName Mongo)) (AID (Data.Text.pack "100"))))
testBuffer name Empty =
testCase name $ assert $ bufferEmptied (UAC (PlayerCommand (Move (ToPlanetName Mongo)) (AID (Data.Text.pack "100"))))
bufferPopulated :: UAC -> MomentIO Bool
bufferPopulated ev = do
let eInput = ev <$ never
eValidated = toVAC <$> eInput
bBufferMap <- (buffer eValidated eClear) :: MomentIO (Behavior BufferMap)
let r2 = [(Just $ BufferMap $ M.insert (AID (Data.Text.pack "100")) (toVAC ev) (M.empty :: M.Map AID VAC))]
r1 <- liftIO $ ((interpret (eBuffer bBufferMap) []) :: IO [Maybe BufferMap])
return $ r1 == r2
bufferEmptied :: UAC -> MomentIO Bool
bufferEmptied ev = undefined
eBuffer :: Behavior BufferMap -> Event a -> Event BufferMap
eBuffer bBufferMap nvr =
bBufferMap <@ (() <$ nvr)
eClear = Clear <$ (() <$ never)
tests/Spec.hs:26:19:
No instance for (Test.HUnit.Base.Assertable (MomentIO Bool))
arising from a use of ‘assert’
In the expression: assert
In the second argument of ‘($)’, namely
‘assert
$ bufferPopulated
(UAC
(PlayerCommand (Move (ToPlanetName Mongo)) (AID (pack "100"))))’
In the expression:
testCase name
$ assert
$ bufferPopulated
(UAC
(PlayerCommand (Move (ToPlanetName Mongo)) (AID (pack "100"))))
问题归结为 accumB
在 MomemtIO
中创建 Behavior
。如果我有 bufferPopulated
return 和 IO Bool
我该如何协调?
编辑:显而易见的事情是编写它想要的实例。我认为这可能是一条红鲱鱼。你觉得怎么样。这只是写 MomentIO Bool
实例那么简单吗?
编辑:更新
我认为我走在正确的轨道上。我已经注释掉所有测试工具代码并更改了 bufferPopulated
bufferPopulated :: UAC -> IO Bool
bufferPopulated ev = do
let eInput = ev <$ never
eValidated = toVAC <$> eInput
bBufferMap <- liftMoment ((buffer eValidated eClear) :: Moment (Behavior BufferMap))
let r2 = [(Just $ BufferMap $ M.insert (AID (Data.Text.pack "100")) (toVAC ev) (M.empty :: M.Map AID VAC))]
r1 <- (interpret (eBuffer bBufferMap) []) :: IO [Maybe BufferMap])
return $ r1 == r2
我相信这应该可行,但这是错误
tests/Spec.hs:35:17:
No instance for (MonadMoment IO) arising from a use of ‘liftMoment’
In a stmt of a 'do' block:
bBufferMap <- liftMoment
((buffer eValidated eClear) :: Moment (Behavior BufferMap))
让我们看看MonadMoment
来自Reactive.Banana.Combinators
class Monad m => MonadMoment m where
An instance of the MonadMoment class denotes a computation that happens at one particular moment in time.
Unlike the Moment monad, it need not be pure anymore.
Methods
liftMoment :: Moment a -> m a
Instances
MonadMoment MomentIO
MonadMoment Moment
m
可以是任意的Monad
,IO
是一个Monad
。所以 liftMoment
应该将 Moment Behavior (BufferMap)
提升到 IO Behavior (BufferMap)
,为什么不呢。我的推理有什么问题?
答案来源于这个之前的答案。
Testing in reactive-banana
interpretFramwork
需要新签名。
interpretFrameWorks'' :: (Event a -> MomentIO (Behavior b)) -> [a] -> IO (b,[[b]])
interpretFrameWorks'' f xs = do
output <- newIORef []
init <- newIORef undefined
(addHandler, runHandlers) <- newAddHandler
network <- compile $ do
e <- fromAddHandler addHandler
f' <- f e
o <- changes $ f'
i <- valueB $ f'
liftIO $ writeIORef init i
reactimate' $ (fmap . fmap) (\b -> modifyIORef output (++[b])) o
actuate network
bs <- forM xs $ \x -> do
runHandlers x
bs <- readIORef output
writeIORef output []
return bs
i <- readIORef init
return (i, bs)