Live data from Hacker News

Testing Without Mocks

jamesshore.com

41–50 of 51 posts

Re: Testing Without Mocks

#41
post #23

Earlier quoted context omitted.

What's your definition of "pure function". Apparently not "pure function" in the sense of "pure functional programming", because if that were so, your claim would just be wrong. Counterexample: "printToConsole: IO[null]": is a pure function but still not easy to test.

The definition of "pure function" I'm familiar with from functional programming has no side effects including I/O. Assuming 'printToConsole' is performing I/O, you're describing a impure function.

Then you have not understand what (pure) functional programming is and how it works.

I recommend you to have a look at Haskell, probably the most famous language for enforcing only pure functions in your program. And I took this example from Haskell.

Here is a good starting point for this concrete example: > https://stackoverflow.com/questions/59035420/understanding-p...

Quote (from the course 'Haskell Fundamentals Part 1' mentioned in the post):

> This example helps illustrate that putStrLn is not a function with side effects.

The answers explain the concept quite well.

Re: Testing Without Mocks

#42
Seems like a lot of work just to avoid mocks. Don’t know if this is language specific but I find it’s really easy to create mocks and/or in-memory substitutions in .NET using something like Moq.

Re: Testing Without Mocks

#43
post #25

Obviously he's keeping the interesting bit about how this works for the actual course, but I already only mock bits right on the edge, e.g. the point where the system would connect to the outside world, and that seems to work. What I'd really like, is some way of acknowledging redundancy - lots of tests ultimately go through the same code underneath, it would be good if that could be tested only once, and then the da…

> Obviously he's keeping the interesting bit about how this works for the actual course

No.

Absolutely everything in the course is in the article. The benefit of the course is that you get to interact with me directly. You can ask for clarification, conduct exercises involving real-world code, and bring your own code for my feedback. It's a guided tour, but you don't need it. Everything is in the article.

Re: Testing Without Mocks

#44
post #4

If you make all your code as pure function, then testing is easy. First thing to do: Stop using class. Ask yourself first: Should i use a class here and there ? Wait, i miss something here: Writing pure functions is hard ?

Alright, so you get numbers from a json file in a ftp servers, you use scipy.newton to perform root finding on it, but needs the number of steps to be outputted as well (which newton() doesn't return), and you will save the result in a postgres table. All this should be send to a task queue, triggered by a call to your REST API. Good luck with purity.

1. code that listens to a REST API and puts a request on a task queue

2. code that listens to the task queue for new jobs

3. code that fetches a file from the FTP server

4. code that converts the JSON file to a format appropriate for the root finding

5. code that performs the root finding

6. code that converts the result of (3) to a format appropriate for insertion into a table

7. code that inserts the result into a Postgres table

Steps 4, 5 and 6 seem easy enough to implement in a pure fashion.

Re: Testing Without Mocks

#45
post #23

Earlier quoted context omitted.

The definition of "pure function" I'm familiar with from functional programming has no side effects including I/O. Assuming 'printToConsole' is performing I/O, you're describing a impure function.

Then you have not understand what (pure) functional programming is and how it works. I recommend you to have a look at Haskell, probably the most famous language for enforcing only pure functions in your program. And I took this example from Haskell. Here is a good starting point for this concrete example: > https://stackoverflow.com/questions/59035420/understanding-p... Quote (from the course 'Haskell Fundamentals P…

While there is an important sense in which even IO values in Haskell are still pure values, it is not helpful in the case of testing.

    $ ghci
    GHCi, version 8.8.4: https://www.haskell.org/ghc/  :? for help
    Prelude> putStrLn "hello" == putStrLn "hello"
    
    :1:1: error:
        • No instance for (Eq (IO ())) arising from a use of ‘==’
        • In the expression: putStrLn "hello" == putStrLn "hello"
          In an equation for ‘it’: it = putStrLn "hello" == putStrLn "hello"
If you want to test your IO code, and you should, you still have to test them from the perspective of them actually executing in the real world. There's no way to test them as pure values. The snippet above is only the beginning of your problems if you wanted to do that, but it a plenty sufficient problem on its own. In addition to the obvious problem that it points out that IO values are already not comparable to each other in the naive sense, there's also the more philosophical problem that the IO values are your programs, so even if you could run the test I show above, on more complicated expressions, your tests would be asserting that you wrote the program you thought you wrote, not that the program you wrote does what you think it should do.

Re: Testing Without Mocks

#46
post #45

Earlier quoted context omitted.

Then you have not understand what (pure) functional programming is and how it works. I recommend you to have a look at Haskell, probably the most famous language for enforcing only pure functions in your program. And I took this example from Haskell. Here is a good starting point for this concrete example: > https://stackoverflow.com/questions/59035420/understanding-p... Quote (from the course 'Haskell Fundamentals P…

While there is an important sense in which even IO values in Haskell are still pure values, it is not helpful in the case of testing. $ ghci GHCi, version 8.8.4: https://www.haskell.org/ghc/ :? for help Prelude> putStrLn "hello" == putStrLn "hello" :1:1: error: • No instance for (Eq (IO ())) arising from a use of ‘==’ • In the expression: putStrLn "hello" == putStrLn "hello" In an equation for ‘it’: it = putStrLn "he…

I agree, that was my point.

You probably replied to the wrong person.

Re: Testing Without Mocks

#47
Reposts are fine after a year or so (https://news.ycombinator.com/newsfaq.html) but this had a major thread just a month ago, so it counts as a dupe:

Testing Without Mocks: A Pattern Language - https://news.ycombinator.com/item?id=34293631 - Jan 2023 (76 comments)

Previously:

Testing Without Mocks: A Pattern Language (2018) - https://news.ycombinator.com/item?id=30565918 - March 2022 (28 comments)

Testing Without Mocks: A Pattern Language - https://news.ycombinator.com/item?id=16943876 - April 2018 (12 comments)

Re: Testing Without Mocks

#48

Earlier quoted context omitted.

Alright, so you get numbers from a json file in a ftp servers, you use scipy.newton to perform root finding on it, but needs the number of steps to be outputted as well (which newton() doesn't return), and you will save the result in a postgres table. All this should be send to a task queue, triggered by a call to your REST API. Good luck with purity.

1. code that listens to a REST API and puts a request on a task queue 2. code that listens to the task queue for new jobs 3. code that fetches a file from the FTP server 4. code that converts the JSON file to a format appropriate for the root finding 5. code that performs the root finding 6. code that converts the result of (3) to a format appropriate for insertion into a table 7. code that inserts the result into a…

Yeah, just reading their description I was like "the middle part seems easily done as its own thing".

"Functional Core, Imperative Shell" was just posted too, this is the perfect example of where that structure works: https://news.ycombinator.com/item?id=34860164

Re: Testing Without Mocks

#49
post #31
post #28

Earlier quoted context omitted.

Mocks are for objects, stubs are for responses and ‘fakes’ are basically the same thing as mocks.

Fakes are not mocks. A fake is an actual (simplified) implementation of the dependency. For example, if your dependency is a distributed key-value store, a fake would expose the same API but using an in-memory hashmap under the hood.

Yeah no, that’s a mock. At least, that’s been a mock ever since I started prgramming 15 years ago. Only later did libraries that automatically mock dependencies appear (and I presume people suddenly felt a need for a new name for not-automatically mocked dependencies?

Re: Testing Without Mocks

#50
post #4

If you make all your code as pure function, then testing is easy. First thing to do: Stop using class. Ask yourself first: Should i use a class here and there ? Wait, i miss something here: Writing pure functions is hard ?

Alright, so you get numbers from a json file in a ftp servers, you use scipy.newton to perform root finding on it, but needs the number of steps to be outputted as well (which newton() doesn't return), and you will save the result in a postgres table. All this should be send to a task queue, triggered by a call to your REST API. Good luck with purity.

"Railway Oriented Programming"[1] is a (functional) technique to achieve purity despite steps that could result in error states. The error states are hidden/pushed to the very edges/end of your code. So you can focus on the "happy state" for the main business logic:

> Many examples in functional programming assume that you are always on the “happy path”. But to create a robust real world application you must deal with validation, logging, network and service errors, and other annoyances.

> So, how do you handle all this in a clean functional way?

> This talk will provide a brief introduction to this topic, using a fun and easy-to-understand railway analogy.

Functional programming keywords/shorthands: option/either/monads

[1]: https://fsharpforfunandprofit.com/rop/

Post reply on HN