2013-12-02 2 views
55

Я новичок в haskell и работаю над модульным тестированием, однако считаю, что экосистема очень запутанна. Я смущен относительно отношений между HTF и HUnit.Испытание блока Haskell

В некоторых примерах я вижу, что вы настраиваете тестовые примеры, экспортируете их в список тестов, а затем запускаете в ghci с runTestsTT (например, this HUnit example).

В других примерах вы создаете тестовый бегун, связанный с файлом cabal, который использует магию препроцессора, чтобы найти ваши тесты, например, в этом git example. Также кажется, что тесты HTF должны иметь префикс test_ или они не запускаются? Мне было трудно найти какую-либо документацию, я просто заметил шаблон, который у всех был.

В любом случае, может кто-то поможет разобраться со мной? Что считается стандартным способом делать вещи в Haskell? Каковы наилучшие методы? Что проще всего настроить и поддерживать?

+0

Вы просматривали библиотеку QuickCheck? Я всегда находил его довольно простым в использовании. – bheklilr

+3

Да, но быстрая проверка - это другой вариант использования, это для тестирования на основе типов, чего я не хочу сейчас.Мне было бы интересно узнать, как интегрировать это, хотя, как только я обертываю голову тем, как htf и hunit относятся к – devshorts

+0

. Https://twitter.com/HaskellTips/status/425793151660331008 говорит, что предпочитает «вкусный» над «тестовой картой» '(HTF?), Но я также вижу, что HTF получил небольшое обновление на прошлой неделе, после нескольких месяцев quiey. – misterbee

ответ

40

Как правило, любой значительный проект Haskell запускается с помощью cabal. Это заботится о строительстве, распространении, документации (с помощью пикши) и тестировании.

Стандартный подход заключается в том, чтобы поместить ваши тесты в каталог test, а затем настроить тестовый пакет в файле .cabal. Это подробно описано в user manual. Вот что тестовый набор для одного из моих проектов выглядит

Test-Suite test-melody 
    type:    exitcode-stdio-1.0 
    main-is:   Main.hs 
    hs-source-dirs:  test 
    build-depends:  base >=4.6 && <4.7, 
         test-framework, 
         test-framework-hunit, 
         HUnit, 
         containers == 0.5.* 

Затем в файле test/Main.hs

import Test.HUnit 
import Test.Framework 
import Test.Framework.Providers.HUnit 
import Data.Monoid 
import Control.Monad 
import Utils 

pushTest :: Assertion 
pushTest = [NumLit 1] ^? push (NumLit 1) 

pushPopTest :: Assertion 
pushPopTest = [] ^? (push (NumLit 0) >> void pop) 

main :: IO() 
main = defaultMainWithOpts 
     [testCase "push" pushTest 
     ,testCase "push-pop" pushPopTest] 
     mempty 

Где Utils определяет некоторые более хорошие интерфейсы над HUnit.

Для более легкого тестирования веса я настоятельно рекомендую использовать QuickCheck. Это позволяет вам писать короткие свойства и тестировать их по серии случайных входов. Например:

-- Tests.hs 
import Test.QuickCheck 

prop_reverseReverse :: [Int] -> Bool 
prop_reverseReverse xs = reverse (reverse xs) == xs 

А потом

$ ghci Tests.hs 
> import Test.QuickCheck 
> quickCheck prop_reverseReverse 
.... Passed Tests (100/100) 
+6

По мере роста проекта вам нужно сохранить экспортированный тестовый список? Кажется, что подвержена ошибкам. Наверное, я все еще смущен тем, как это связано с методом автоматического экспорта препроцессора? Я вижу много примеров тестирования модулей, но все они разные – devshorts

+0

@devshorts Список тестов позволяет вам называть каждый тест индивидуально. Я действительно верю, что есть рамки, которые будут автоматически выполнять ваши тесты, но обычно у меня около 10 тестов на файл, поэтому сохранение таких небольших списков довольно просто. – jozefg

+1

Можете ли вы объяснить, как вы используете этот метод с несколькими тестовыми файлами? Является ли главный бегун отдельным или обычно частью испытательного прибора? – devshorts

28

Я тоже новичок haskeller, и я нашел это введение очень полезно: "Getting started with HUnit".Подводя итог, я помещу здесь простой пример тестирования использования HUnit без файла .cabal проекта:

Давайте предположим, что у нас есть модуль SafePrelude.hs:

module SafePrelude where 

safeHead :: [a] -> Maybe a 
safeHead [] = Nothing 
safeHead (x:_) = Just x 

мы можем поставить испытания в TestSafePrelude.hs следующим образом:

module TestSafePrelude where 

import Test.HUnit 
import SafePrelude 

testSafeHeadForEmptyList :: Test 
testSafeHeadForEmptyList = 
    TestCase $ assertEqual "Should return Nothing for empty list" 
          Nothing (safeHead ([]::[Int])) 

testSafeHeadForNonEmptyList :: Test 
testSafeHeadForNonEmptyList = 
    TestCase $ assertEqual "Should return (Just head) for non empty list" (Just 1) 
       (safeHead ([1]::[Int])) 

main :: IO Counts 
main = runTestTT $ TestList [testSafeHeadForEmptyList, testSafeHeadForNonEmptyList] 

Теперь это легко запускать тесты с использованием ghc:

runghc TestSafePrelude.hs 

или hugs - в этом случае TestSafePrelude.hs должен быть переименован в Main.hs (насколько я знаком с объятиями) (не забудьте изменить заголовок модуля тоже):

runhugs Main.hs 

или любой другой haskell компилятор ;-)

Конечно, в HUnit есть более, поэтому я действительно рекомендую прочитать предлагаемый учебник и библиотеку User's Guide.

4

У вас были ответы на большинство ваших вопросов, но вы также спросили о HTF и как это работает.

HTF - это структура, предназначенная для модульного тестирования - она ​​обратно совместима с HUnit (она интегрирует и обертывает ее для предоставления дополнительных функций) - и тестирование на основе свойств - оно интегрируется с quickcheck. Он использует препроцессор для поиска тестов, так что вам не нужно вручную создавать список. Препроцессор добавляется в тестовых исходных файлов с помощью прагму:

{-# OPTIONS_GHC -F -pgmF htfpp #-} 

(в качестве альтернативы, я думаю, вы могли бы добавить те же параметры к ghc-options собственности в файле междусобойчик, но я никогда не пробовал это так что не» t знать, полезно ли это или нет).

Препроцессор сканирует ваш модуль для функций верхнего уровня с именем test_xxxx или prop_xxxx и добавляет их в список тестов для модуля. Вы можете использовать этот список напрямую, поместив в модуль функцию main и запустив их (main = htfMain htf_thisModuleTests) или экспортируйте их из модуля и получите основную тестовую программу для нескольких модулей, которая импортирует модули с помощью тестов и запускает все из них:

import {[email protected] HTF_TESTS @-} ModuleA 
import {[email protected] HTF_TESTS @-} ModuleB 
main :: IO() 
main = htfMain htf_importedTests 

Эта программа может быть интегрирована с интригой, используя методику, описанную @jozefg, или загружены в GHCI и работать в интерактивном режиме (хотя и не на Windows - см https://github.com/skogsbaer/HTF/issues/60 для более подробной информации).

Tasty - еще одна альтернатива, которая обеспечивает способ интеграции различных видов испытаний. Он не имеет препроцессора, такого как HTF, но имеет модуль, который performs similar functions using Template Haskell. Подобно HTF, он также полагается на соглашение об именах для идентификации ваших тестов (в этом случае case_xxxx, а не test_xxxx). Помимо тестов HUnit и QuickCheck, в нем также есть модули для обработки нескольких других типов тестов.

Смежные вопросы