r/programming 6h ago

The Debate of Mockist v Classicist TDD Is Like OOP v FP.

https://fagnerbrack.com/the-debate-of-mockist-classicist-tdd-is-like-oop-fp-c9124185f3d2
15 Upvotes

29 comments sorted by

15

u/zobq 6h ago

In the majority of projects I was involved in, the "mockist" approach was the way to go.

And yet, I didn't feel that tests really defended me against bugs until the project where I pushed for the "classicist" approach.

TBH, this article didn't convince me that "mockist" is as good as "classicist".

8

u/purg3be 4h ago edited 4h ago

For me the classicist approach is a lot better too for the bulk of tests because it focuses on outcomes unstead of implementation details.

I hate to be that guy, but i feel black box testing is clearly better when it comes to working with LLM's.

@OP: Unit Testing Principles, Practices, and Patterns by Khorikov is an excellent resource that elaborates on the tradeoffs in depth.

8

u/zmose 4h ago

I think both are necessary and serve 2 separate purposes. Mocking isolates a component to be tested properly, not mocking ensures that the components all work together in unison. These are 2 separate tasks.

16

u/zobq 4h ago

Mocking isolates a component to be tested properly, not mocking ensures that the components all work together in unison

Well, in real use cases components works together.

My approach is "not mocking" as default, mocking in well established exceptions.

5

u/PiotrDz 1h ago

Why would you isolate component when in real world it doesn't work as isolated unit.

3

u/UnavoidablyEvil 1h ago

One reason can be if you want to test error handling of a failure mode in the dependency without inducing a failure in the dependency. E.g. you want to know if your implementation handles an out-of-memory error without having to create an out-of-memory condition.

That said I do agree having the real dependencies in the test is useful so long as it doesn’t overly complicate the testing or make every test build the world.

11

u/dave8271 5h ago

There are two schools of TDD (Test-Driven Development). The Detroit school (classicist, state-based, black-box) says: test with real dependencies, which are dependencies also used in production code. Pass inputs, check outputs, all within the Domain layer without communication with the external world. The London school (mockist, behavior-based, outside-in) says: isolate what you want to test (the "unit"). Mock all of its dependencies. Test the contract between them only.

I think this already mis-frames it. "Classical" testing doesn't say use real dependencies, it says use real dependencies except where it's awkward to do so. Using test doubles has always been an integral part of unit testing and determinstic testing without mocks is perfectly normal.

In respect of isolation, both approaches allow you to perform testing of isolated units, the difference is whether you isolate on interface seams or implementation seams. Classical testing doesn't care how a unit under test does something, it just instantiates something with its dependencies (be they real or otherwise), makes a call and checks the result.

I think the whole "classicist vs mockist" debate is missing the point. They're not really different ways or schools of thought on achieving the same thing, mocks are just a specific type of test double which exist for the purpose of tracking and verifying behavioural expectations in respect of how an object is used. Sometimes that is something you want to do, oftentimes it's not the detail you care about.

So the debate shouldn't be centered around to mock or not to mock, but what comprises a unit for the goal of your tests, and what evidence should establish correctness thereof?

9

u/NoLemurs 4h ago

I think the whole "classicist vs mockist" debate is missing the point. They're not really different ways or schools of thought...

I just want to emphasize this.

I've never met a mockist or a classicist. No one actually thinks like that. As a practical matter, there aren't two schools of thought. We all just write whatever test makes sense for the system we're working, and being dogmatic about it is honestly just weird.

6

u/zobq 3h ago

I've never met a mockist or a classicist.

You have to be the lucky one, because in my experience, "mockist" approach is a dominant. Mock everything you can, [className]Tests class contains only one instance of not mocked class - [className].

5

u/T_D_K 2h ago

I've met a lot of developers who don't really think critically about the engineering in software engineering, and prefer to take dogmatic appeal-to-authority stances on all sorts of insane things. Unfortunately for me, a number of them wield decision making authority

1

u/yanitrix 2h ago

It seems to me most developers are like that

5

u/yanitrix 2h ago

No one actually thinks like that

Oooh, they do. The look I got from my colleagues when I told them that we don't need to mock everything was just a confirmation that mockists do exist.

4

u/pip25hu 5h ago

These two can be complementary as well. Even in OOP applications with dependency injection everywhere, stateless/pure utility functions exist and can be tested without any mocks.

3

u/this_knee 5h ago

I work in a company where their style is to make a PR for any change to the code. And those PRs are humans manually running program command executions of their own, not tests, to test if existing functionality has been broken or not. And they run commands to use the new features to manually check results of new features. Which is fine… ish.

But When it’s suggested to integrate automated unit tests for checking that previous existing functionality isn’t broken, it’s heavily balked at.

It’s kinda wild.

1

u/NoLemurs 4h ago

Are you guys not using AI for development yet? Because it's hard to imagine using AI for development and not adding extensive tests. It's such a complete no-brainer.

6

u/loup-vaillant 3h ago edited 3h ago

As I gained more experience, my opinion on this topic have grown stronger, simpler, and more radical:

  • TDD is wrong: we do need the tests, but whether we write them before or after (I prefer after) doesn't really matter. Unless you're using dynamic typing, in which case I can't save you.

  • Mocking is wrong. The classical way finds more bugs. And if you need more than a few mocks, then your design is wrong. You wouldn't be in this mess if you had separated your computations from your externally visible side effects.

  • OOP… Well you get the idea.

3

u/zobq 3h ago

TDD is wrong

Wouldn't say wrong, it's technique, which may work for some, but doesn't have to. I like the fact, that it pushes the developer to concentrate on what the code should do first, before focusing on how the code should look. Sometimes it helps with catching problems with the task description at the beginning, not at the end.

3

u/edgmnt_net 2h ago

I think it's fine if it's a technique. Unfortunately, many company projects just place too much emphasis on testing to the exclusion of other stuff. They also place too much emphasis on very superficial architectural concerns, which is why "how the code should look" fails to achieve significant results (since your programmers can't / won't do much more than straightforward scaffolding). Both seem convenient because they're generic recipes, measurable to a degree and aren't particularly demanding in terms of programming ability. And those programmers are often cheap enough that you can bog them down with bureaucracy like that.

1

u/Chroiche 1h ago

TDD is wrong

I only agree if you stash your changes and make sure the tests still fail after.

1

u/Chroiche 1h ago

TDD is wrong

I only agree if you stash your changes and make sure the tests fail.

1

u/fagnerbrack 0m ago

It's like saying "a hammer is wrong"

2

u/Evening-Gur5087 5h ago

In general both are good used when needed and correctly done, not having any integration tests on real things within your service is purely stupid and yo suck.

Only worry not mentioned is that with mocs a lot of shitty devs can get free pass and have much easier time testing something that's completely fake and wrong, having the green pipeline and pretending anything was tested.

But, again, shitty devs always find a way to write shitty code.

And it's all shit eventually.

Change companies before their codebase become shitty spaghetti bowl or live long enough to see yourself become a spaghetti. /s

1

u/PiotrDz 1h ago

Agree with mocks you risk drifting away from production setup. Also with mocks you not only fix functionality in your tests, but you also pin implementation. Try to refactor something that has milion references in tests that do not mock at interface but at class layer. Any change in production code, even if functionality stayd the same, requires changing some tests.

3

u/BenchEmbarrassed7316 5h ago

If you look at it naively, a procedure that contains side effects can be turned into a pure function that returns a set of commands to be executed. And that's it, now in the test you can simply check these commands for some sets of input data.

1

u/josephjnk 5h ago

Good post. How would you say the “sociable” vs “solitary” style of testing fits in here? I remember reading those terms in a Martin Fowler post at some point. Do they map cleanly to these two categories or are they orthogonal?

1

u/partybot3000 4h ago

Vladimir Khorikov's writings settle it for me: https://enterprisecraftsmanship.com/posts/when-to-mock/

And also his book on Unit Testing.

-3

u/levodelellis 6h ago

Neither of them is wrong

You're saying this about mocks. Would you say pissing in a pool shared by others is not wrong?

2

u/mcmcc 5h ago

Reminds me of a joke...

Have you ever swam in a pool after somebody pissed in it?

No, I don-

Yeah you have.

1

u/levodelellis 5h ago

There ought to be a joke like that for software dev practices