2016-01-17 10 views
1

Допустим, у меня есть следующий тип данных:тестирования пользовательские типы данных, которые не реализуют уравнение

data Animal = Characteristics [Char] (Set.Set [Char]) 

и некоторые функции

checkAnimalType :: [Char] -> Animal -> [Animal] 

теперь я пытаюсь написать hspec тесты для этого как так:

describe "checkAnimalType" $ do 
     it "returns a list of animals" $ do 
     (checkAnimalType ["foo", "coo", "doo", "bar", "moo"](Characteristics "foo" $ Set.fromList(["foo", "coo"]))) $ `shouldBe` [(Characteristics "foo" $ Set.fromList(["cockadoodledoo"]))] 

это терпит неудачу с:

No instance for (Eq Animal) arising from a use of ‘shouldBe’ 

Вопрос в том, можно ли временно, в рамках испытаний, реализовать Eq typeclass на Animal? Или есть лучший способ сделать это?

+0

Вы можете делать 'выведение Eq' при объявлении' Animal' или реализовать свои собственные экземпляры для 'Eq'. – Sibi

+0

, но можно ли вывести тест без получения фактического источника? –

+0

вы можете написать свою собственную декларацию экземпляра; должен использовать внутренний оператор '(==)', который предоставляется 'Eq', поэтому вы не можете его протестировать – epsilonhalbe

ответ

3

Мой вопрос в том, можно ли временно, в рамках испытаний, реализовать класс Eq для Animal?

В рамках модуля, конечно. Но если этот модуль будет импортирован, вы будете утечка экземпляра в другие модули. Вот почему создание экземпляров рекомендуется только в том же модуле, когда был определен тип данных или где был определен класс. В противном случае вы получите orphan instances.

Или есть лучший способ сделать это?

Возможно ли, что пользователь хочет сравнить характеристики? Затем выведите Eq. Это самый чистый путь. Кроме того, вы будете нуждаться в Show экземпляре, так что вы, вероятно, что-то уже вывод:

data Animal = Characteristics [Char] (Set.Set [Char]) deriving (Show, Eq) 

Если вы не можете изменить оригинал вы все еще можете использовать -XStandaloneDeriving для получения экземпляра в другом модуле (см сироту примеры выше, хотя).

Однако, если вы действительно хотите использовать некоторые специальные Eq тест вы можете либо возиться с newtype оберток, или просто написать свой собственный комбинатор:

-- newtype variant 
newtype TAnimal = TAnimal Animal 
instance Eq TAnimal where ... 
instance Show TAnimal where... 

animalShouldBe :: Animal -> Animal -> Expectation 
animalShouldBe = shouldBe `on` TAnimal 

-- custom operator variant 
withShouldBe :: (Show a) => (a -> a -> Bool) -> a -> a -> Expectation 
withShouldBe f a e = unless (f a e) $ expectationFailure msg 
where msg = "expected: " ++ show e ++ ", but got " ++ show a 

animalShouldBe = withShouldBe animalEqualityTest 

-- Fun fact: shouldBe = withShouldBe (==) 
2

Это немного странное требование. Но вы можете просто добавить экземпляр Eq в свой тест. Haskell не имеет ограничений на наличие экземпляра в том же исходном файле или модуле, что и тип данных или тип.

Если вы хотите получить экземпляр, а не писать его самостоятельно, вы можете (в GHC) использовать расширение StandaloneDeriving и написать:

deriving instance Eq Animal 

Edit: Сказав это, я не могу посмотрите, почему вы не просто добавили экземпляр вместе с вашим основным определением Animal. Это не принесет никакого вреда, и довольно стандартно добавлять общие типы дериваций типов вверх, на случай, если они понадобятся вам позже.

2

Вы должны проверить наблюдаемое поведение, а не детали реализации.Если по какой-либо причине, будь то два Животные равны, это не то, что должно быть наблюдаемым (и, следовательно, вы не хотите внедрять Eq в исходный код), тогда не используйте это свойство в своем тесте.

Так что вместо проверки того, что checkAnimalType возвращает определенный список животных, сосредоточьтесь на том, что вы, как предполагается, можете сделать с этим списком и проверить, что эти свойства сохраняются.

E.g. Если вы можете получить имена из них, проверьте их. Если предполагается, что все Животные, имеющие какое-либо отношение к входному животному, возвращаются, убедитесь, что это соотношение выполнено (и, возможно, это не относится к другим животным). Если Animal не является Eq, но вы можете канонически преобразовать его в нечто, что есть (скажем, если Animal содержит некоторый внутренний мусор реализации, который не является дружественным к Eq, но вы можете сериализовать их или иным образом преобразовывать их в более «просто данные»), конвертировать выход checkAnimalType, а затем использовать Eq этого.

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

  • Нет связанных вопросов^_^