|
|
|
|
|
by mavelikara
2986 days ago
|
|
> Agreed, but we mustn't fall into the fallacy of assuming that means no language can ever be better or worse than another. There are good and bad language design choices. Agreed. > "an implementation of interface foo that returns bar when called with baz" is not some obscure specialized feature It isn't some obscure specialized feature in Java either. Foo foo = new Foo() {
public Bar method(Baz baz) {
return new Bar("bar");
}
}
What mocking frameworks do is to provide a DSL to describe behavior of such implementations, use dynamic bytecode generation (not reflection, BTW) to create implementations of the interfaces dynamically, and bind them to simulate various test conditions. What the makes the language "worse" to require or allow doing this?My Haskell is rusty, but given Haskell psuedocode like: main :: IO ()
main = do
f <- foo
if (f == 1) then
putStrLn "Got 1"
else
putStrLn "Didn't get 1"
foo :: IO Int
-- ...
how would you test that the two branches of main behave appropriately?This is not a snark; I am truly interested to know how Haskell gets rid of the need to bind alternate implementations of an interface for testing purposes. |
|
To the extent that that's true, fine. I'm sure I see a lot of developers writing something along the lines of:
instead of that, not because they need any of the mocking features as such but because it takes up fewer lines on the screen, particularly when there are more methods in the interface. (Partly a cultural problem of having overly large interfaces rather than a language problem per se, perhaps).> use dynamic bytecode generation (not reflection, BTW)
How is it not reflection ("the ability of a computer program to examine, introspect, and modify its own structure and behavior at runtime")?
> What the makes the language "worse" to require or allow doing this?
Reflection or code generation means stepping outside the language and its usual guarantees - any time the programmer is forced to do it it's because the language didn't provide a good way to solve the problem within the language itself. It means you can no longer e.g. extract common expressions, because they don't necessarily mean the same thing; if you have some common mock setup code you can't just blindly automatically extract method, you have to think carefully about when the mocks get instantiated and when the expectations are set.
> how would you test that the two branches of main behave appropriately?
> This is not a snark; I am truly interested to know how Haskell gets rid of the need to bind alternate implementations of an interface for testing purposes.
It doesn't - as I said, you still write test implementations of your interfaces. What it does remove the need for is mocking frameworks, which people use in e.g. Java either because implementing the interface the normal way in the language is more effort (not a problem in Haskell), or because they want to test the specific interactions with the object (e.g. "verify that method foo was called twice") because those methods are used for side effects.
Haskell avoids that one by making it easier to represent actions as values; you can use e.g. a free monad to represent actions that will be performed later, so rather than testing that your complex business logic method called deleteUser(userId) on your mock, you instead test that it returns a DeleteUser(userId) value. To a certain extent you can do this in Java too ("the command pattern"), but without higher-kinded types you can't have a standard implementation of e.g. composed commands or standard methods for working with them, so it gets too cumbersome to really do in practice.
Even in Java you wouldn't want to use mocks for testing methods that operate on simple datatypes: to test e.g. a regex find method, you wouldn't pass in mock strings, you'd just pass in real strings and confirm that the results were true or false as expected. A language like Haskell just expands the space of what you can test in the same these-inputs-these-outputs way, by making it easier to represent more things as values.