Haskell Pipes и тестирование с HSpec
Я написал программу для проекта, который использует Pipes, которые я люблю! Однако я изо всех сил пытаюсь выполнить модульное тестирование своего кода.
У меня есть ряд функций типа Pipe In Out IO ()
(например), который я хочу протестировать с HSpec. Как я могу пойти по этому поводу?
Например, предположим, у меня есть этот домен:
data Person = Person String Int | Unknown deriving (Show, Eq)
data Classification = Friend | Foe | Undecided deriving Show
и эта труба:
classify :: Pipe Person (Person, Classification) IO ()
classify = do
p@(Person name _) <- await
case name of
"Alex" -> yield (p, Friend)
"Bob" -> yield (p, Foe)
_ -> yield (p, Undecided)
Я хотел бы написать спецификацию:
main = hspec $ do
describe "readFileP" $
it "yields all the lines of a file"
pendingWith "How can I test this Pipe? :("
2 ответа
Хитрость заключается в использовании toListM
из труб ListT
монадный трансформатор.
import Pipes
import qualified Pipes.Prelude as P
import Test.Hspec
data Person = Person String Int | Unknown deriving (Show, Eq)
data Classification = Friend | Foe | Undecided deriving (Show, Eq)
classify :: Pipe Person (Person, Classification) IO ()
classify = do
p@(Person name _) <- await
case name of
"Alex" -> yield (p, Friend)
"Bob" -> yield (p, Foe)
_ -> yield (p, Undecided)
Проверка с использованием преобразователя ListT для преобразования канала в ListT и утверждение с использованием HSpec:
main = hspec $ do
describe "classify" $ do
it "correctly finds friends" $ do
[(p, cl)] <- P.toListM $ each [Person "Alex" 31] >-> classify
p `shouldBe` (Person "Alex" 31)
cl `shouldBe` Friend
Обратите внимание, вам не нужно использовать each
, это может быть простой продюсер с призывом к yield
,
Вы можете использовать функции temporary
пакет, чтобы создать временные файлы с ожидаемыми данными, а затем проверить, что данные правильно прочитаны по каналу.
Кстати, твой Pipe
использует readFile
который выполняет ленивый ввод / вывод. Ленивый ввод / вывод и потоковые библиотеки, такие как каналы, не очень хорошо сочетаются, фактически последние существуют главным образом как альтернатива первым!
Возможно, вам следует вместо этого использовать функции, которые выполняют строгий ввод-вывод, например openFile
а также getLine
,
Одна неприятность со строгим вводом / выводом заключается в том, что это заставляет вас более тщательно рассмотреть распределение ресурсов. Как обеспечить, чтобы каждый дескриптор файла был закрыт в конце или в случае ошибки? Одним из возможных способов достижения этого является работа в ResourceT IO
монада, а не прямо в IO
,