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,

Другие вопросы по тегам