如何在 Haskell Tasty 测试中捕获 stdout 并模拟 stdin?

How do I capture stdout and simulate stdin in a Haskell Tasty test?

提问人:Øyvind Roth 提问时间:11/13/2023 最后编辑:Øyvind Roth 更新时间:11/20/2023 访问量:74

问:

我的不纯函数定义如下:

module HangmanImpure (HangmanImpure.getLine, HangmanImpure.putStr, HangmanImpure.putStrLn, HangmanImpure.strlen) where

import qualified System.IO as Sys

getLine :: IO String
getLine = do x <- Sys.getChar
             if x == '\n' then
                 return []
             else
                do xs <- HangmanImpure.getLine
                   return (x:xs)

putStr :: String -> IO ()
putStr [] = return ()
putStr (x:xs) = do Sys.putChar x
                   HangmanImpure.putStr xs

我想使用 Tasty 测试这些功能,例如:

module TestHangmanImpure (scProps) where

import qualified Test.Tasty as T
import qualified Test.Tasty.SmallCheck as SC

import qualified HangmanImpure as HI (getLine, putStr, putStrLn, strlen)

scProps :: T.TestTree
scProps = T.testGroup "checked by SmallCheck"
    [
        SC.testProperty "getLine should return what is written to stdin" $
            "Placeholder to get the test to run" == "Placeholder to get the test to run" -- HI.getLine == IO "Hello" -- PLEASE, HELP ME WITH SOME CONTENT HERE

        , SC.testProperty "putStr should write to stdout what it gets in as an argument" $
            "Placeholder to get the test to run" == "Placeholder to get the test to run" -- HI.putStr "Nice day!" -- PLEASE, HELP ME WITH SOME CONTENT HERE
    ]

如果有人能在我指出的两个简单测试中填写身体,我将不胜感激。

编辑: 我认为[在 haskell 中,我如何与 stdin 的交互?[1] 可以引导我找到解决方案。我可以让该解决方案在我的 Win10 上使用 HSpec。所以这个问题几乎是重复的。但是我无法使用 Tasty.HUnit 让它工作。我的暂定代码运行如下:IO ()

module TestUtilImpure (scProps, qcProps, uTests, properties) where
    
import qualified Test.Tasty as T
import qualified Test.Tasty.HUnit as HU
    
import StdInStdOutUtil as IOU (captureStdout, provideStdin)
import UtilImpure as UI (getLine)
    
properties :: T.TestTree
properties = T.testGroup "TestUtilImpure tests" [
          uTests
        ]
    
uTests :: T.TestTree
uTests = T.testGroup "Checked by HUnit"
        [
            HU.testCase "UI.getLine reading from stdin should return what was provided" $ do
                let line = "Hello, echo!\n"
                let capturedIOString = IOU.captureStdout (provideStdin line echo)
                capturedString <- capturedIOString
                HU.assertBool "The String returned from UI.getLine differs from the one provided for StdIn" $ capturedString == line
        ]

echo :: IO ()
echo = UI.getLine >>= Prelude.putStrLn

(我把 [1] 中的解决方案:在 haskell 中,我如何与“IO()”的 stdin 交互?

 import StdInStdOutUtil as IOU (captureStdout, provideStdin)

我的测试失败:

从 UI.getLine 返回的 String 与为 标准输入

无论我测试 Prelude.getLine 还是我自己的 UI.getLine,我都会收到相同的错误消息。所以我想有一些我不理解的语法使用 Tasty 失败了。

Haskell stdout stdin 美味

评论

0赞 lsmor 11/13/2023
我认为任何属性测试库都是不可能的。请注意,调用方除了其类型签名之外一无所知。特别是,调用方无法知道用途。即使你明确了它(例如:对于某些用户定义的类型),我认为没有任何方法可以测试真实的(交互式shell),你最多只能测试一些纯函数。此外,其文档已过时,您的测试将失败getLinegetLinegetLinestdingetLine :: StdIn -> IO StringStdInstdinString -> StringIOsmallcheck\n
0赞 Øyvind Roth 11/13/2023
我不确定我是否理解你的逻辑。"...getLine 的调用者对 getLine 一无所知,只知道它的类型签名......”。这是正确的,原则上与 Prelude System.IO 的 getLine 变体的签名具有相同的签名相同:getLine :: IO String。但是我,程序员知道它读取 stdin,因为我阅读了它的文档,并且我已经使用和测试了它。同样,我知道 HangmanImpure.getLine 也可以从 stdin 读取,因为我(好吧,Graham Hutton......)已经写了它。对不起,如果误解了你试图向我解释的内容:-)
0赞 Øyvind Roth 11/13/2023
P.S.:我怀疑 hackage.haskell.org/package/tasty-0.11.2.3/docs/ 中的ResourceSource stackoverflow.com/questions/30450329/...可能与解决方案有关。我仍在寻求帮助和/或解释。顺便说一句,我不明白为什么我被否决了,不应该没有资格成为新手吗?我试图在谷歌上找到一个解决方案,但我的大脑以其目前的 Haskell 技能无法将我找到的碎片放在一起。
0赞 lsmor 11/13/2023
我没有投反对票......SO 有时可能有点咄咄逼人。让我来引用“getLine 的调用者对 getLine 一无所知”。一个常见的 poperty 测试是 where is a list 和 .当为 编写属性时,调用者(属性测试库)知道需要一些输入,这是函数的第一个参数,因此它可以随机生成一个并测试给定的属性。在您的情况下没有输入,它是原始的.继续。。reverse (reverse xs) == xsxsreverse :: [a] -> [a]reversereverse[a]reversegetLineIO String
0赞 lsmor 11/13/2023
您知道“输入”是 stdin,但这对程序的其余部分是不透明的,因此无法为其生成随机输入......同样,从类型角度来看,没有输入。我认为是针对其他类型的测试,例如黄金测试等......不适用于基于属性的测试getLinewithResourceSource

答: 暂无答案