Lets say I have the following custom data type:
data Animal = Characteristics [Char] (Set.Set [Char])
and some function
checkAnimalType :: [Char] -> Animal -> [Animal]
now I'm trying to write hspec tests for this like so:
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"]))]
this fails with:
No instance for (Eq Animal) arising from a use of ‘shouldBe’
My question is, is it possible to temporarily, within the scope of the tests, implement the Eq typeclass on
Animal? Or is there a better way to do this?
My question is, is it possible to temporarily, within the scope of the tests, implement the Eq typeclass on Animal?
Within the scope of the module, sure. But if that module gets imported, you're going to leak the instance into other modules. That's why creating instances is only recommended in the same module the data type has been defined, or where the class has been defined. Otherwise you end up with orphan instances.
Or is there a better way to do this?
Is it even remotely possible that the user want to compare characteristics? Then derive
Eq. It's the cleanest way. Also, you're going to need a
Show instance, so you're probably deriving something already:
data Animal = Characteristics [Char] (Set.Set [Char]) deriving (Show, Eq)
If you cannot change the original source you can still use
-XStandaloneDeriving to derive the instance in another module (see orphan instances above, though).
However if you actually want to use some special
Eq test you can either fiddle around with
newtype wrappers, or simply write your own combinator:
-- 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 (==)