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 (==)
deriving Eqwhile declaringAnimalor implement your own instances forEq.(==)operator internally which is provided byEqthus you cannot test without itEqfor your data type in your test module. But I would ask you to re-think your approach. If you feel your testing requiresEqinstances and the code doesn't require it, then there is some problem with the design.