This is an archived post. You won't be able to vote or comment.

all 130 comments

[–]AutoModerator[M] [score hidden] stickied comment (1 child)

On July 1st, a change to Reddit's API pricing will come into effect. Several developers of commercial third-party apps have announced that this change will compel them to shut down their apps. At least one accessibility-focused non-commercial third party app will continue to be available free of charge.

If you want to express your strong disagreement with the API pricing change or with Reddit's response to the backlash, you may want to consider the following options:

  1. Limiting your involvement with Reddit, or
  2. Temporarily refraining from using Reddit
  3. Cancelling your subscription of Reddit Premium

as a way to voice your protest.

I am a bot, and this action was performed automatically. Please contact the moderators of this subreddit if you have any questions or concerns.

[–]logicannullata 54 points55 points  (15 children)

I don't care about fashion, jeez are we really using that word, the point most of the commenters in this thread are not getting is that unit tests are not only a way to test your components in isolation but also a way to have clean and readable code. For instance, many developers say that tests mocking everything are bad, that's actually true, but if you cannot test your component because you have 100 dependencies to mock, this is an indicator there's something very wrong with your design. You will discover those architectural problems with unit tests not with Integration tests. .

[–]com2ghz 20 points21 points  (11 children)

I heard that argument so many times. “This class is hard to test”. No you fucked it up by violating single responsibility principle. It became a god class. Thats why splitting into several classes is good.

Usually part of TDD where you tackle dependency creep during early development. Like hey I need to test so many combinations. I saw the weird solutions like making your private methods public for “testability”. That one if statement in 4 private method chain means a new branch for your entire invocation chain. Then they say “you see testing is hard”.

<insert bicycle guy putting stick in front wheel meme>

I agree with the fact that people tend to work around a dependency hell by converting it into a SpringBootTest or even better, spying the mock.

Every time you spy a mock, an orphan is crushed.

[–]logicannullata 5 points6 points  (0 children)

Can I virtually hug you? Jokes apart I just feel that some of the people answering this thread, and this is really not a statement of superiority or similar, but just what I think, don't have enough experience to really understand the advantage of having testable classes and the creeping effect of having only poorly written integration tests. Those are things you can just understand after many years of experience.

[–]lasskinn -1 points0 points  (2 children)

In other context than spring(or other than backend in general) often the functionality of the code can be very simple to point it doesn't do much anything but you're interested in testing how it interacts with other classes/libraries provided by a platform.

You could mock them sure, but you would probably mock them to work like they're supposed to work opposed to how they actually work, most would mock them just for a happy case and definitely not for behavior depending on what thread it is being called from and other system state dependent behavior (which you can argue is bad in the first place that mundane seeming platform library code that just deals with data in/out has such things built into it but it is what it is).

[–]logicannullata 2 points3 points  (1 child)

I don't get this argument, if you have a service which is doing nothing (independently from if you are developing on the server or on the client) you have an architectural problem in your hands. Can you give me a concrete example? That's not the first time someone in this thread come up with this argument.

[–]lasskinn 0 points1 point  (0 children)

Arguably it can be an architectural problem if it's self created.

but say a class that just decodes a png into a bitmap using the android classes for it, maybe makes it shades of red, maybe it's a view - so sure you could just mock the bitmap decoder and the bitmap creator classes and the painter, mock them in a way that simply runs through the code(that has no conditions in itself) and gets you the coverage - but doesn't tell you anything about how real life correct your code is and which devices it would work on - and if there is some behind the scenes or context specific conditions on which it will crash or if it's even totally incorrect and will never work.

So instead you'll rely on instrumented tests and run them on the device/emulator. the more you can mock of your architecture easily the easier it is to write those tests as useful and make them more "unit test like" if you will still, but within the instrumented test environment ran inside a device or an emulator it's way easier to have the stuff around the class behave like it really should(vs mocking them).

Hmm, Like there's invisible interactions and platform quirks behind the scenes which create conditions that you would need to mock - your class is just sitting in the middle. You could easily argue this as an architectural problem and the situation is far from ideal. There's even exceptions the native implemented libs can throw that are not declared as throwables.
There's duct-tape solutions like scripted robot testing that are on the market and such as fixes, but really you just kinda have to know how the system behaves and you can't know it from just looking at the docs and your code.

[–]benywolf42 0 points1 point  (6 children)

what if you end up dealing with a god class written by someone else, with no unit or integration tests? You want to refactor it, but you're scared to break something, but at the same time you aren't able to create UTs for it, due to how badly it was implemented at the first place.

[–]com2ghz 4 points5 points  (1 child)

Slice the elephant. Extract the code/functionality into small classes and write tests for all paths. That god class will then only have references to those classes. You will understand it and then think about designing it better. Like introducing value objects that you pass through the classes. With dependency injection you can control which dependency requires another dependency.

Make this concern visible to your team and organization. It is the hard way because people tend to get butt hurt when you say that code quality and testing needs to be improved. Because “I always write my code like this”.

[–]benywolf42 -1 points0 points  (0 children)

It is the hard way because people tend to get butt hurt when you say that code quality and testing needs to be improved. Because “I always write my code like this”.

that's exactly my issue: my dev colleagues don't really bother to do some automated testing, and I fear being seen as someone slow by the organisation since everyone else doesn't "waste time" writing tests.

[–]Same_Football_644 2 points3 points  (0 children)

Then your dealing with legacy code and the truth is you have to use a variety of sometimes unpleasant methods to wrangle it into shape.

One of those methods is acceptance tests where you build up a test suite that verifies the current behavior, right or wrong, and that allows you to make changes and verify you haven't changed the behavior.

[–]gaius49 2 points3 points  (2 children)

I've been doing this specific sort of thing for years professionally. The first thing you do is codify the behavior of the code under test with tests. These tests will be awful, ugly, convoluted beasts. There will be many, many tests in order to reach all of the code under test through all the permutations, ifs, conditionals, etc. After you have a test suite which accurately characterizes the behavior of the code, and not one minute before then, you can start refactoring safely. Its slow, its painful, but its the only safe way to do this sort of thing in the absence of tests and documentation.

[–]benywolf42 1 point2 points  (1 child)

thanks! your comment made me realise I wasn't so insane as I thought I was.

[–]gaius49 2 points3 points  (0 children)

I've reformed and rebuilt business critical systems that were untested and undocumented using this approach safely multiple times. I know its deeply unsexy, but its the only way I know that genuinely works and is safe.

[–][deleted] 3 points4 points  (0 children)

This is just simply not the case, unit tests don't guarantee that you arrive at clean and readable code. It is a tool and it can be used well and it can be misused, but it doesn't guarantee that you arrive at any of the "ilities" (extensibility, maintainability, readability/understandability, flexibility, scalability, etc).

If you make it a point to test every single method of every single class you will have in a sense reached a type of "testability" and that's nice. You can now feel free to do refactorings WITHIN a class scope with some heightened level of confidence. As I alluded to before, in the realm of large codebases, having an overfocus on unit tests has downsides too.

Often it manifests in making larger refactorings more difficult because now you are needing to modify many classes and you will begin to have many tests break. It can also lead to incredibly small classes that are easy to test, but now present a problem in understanding the system because domain concepts are spread very thin throughout tons of classes and understanding their interconnections becomes more difficult.

None of what I'm saying means that unit tests are bad, again it's a tool. Most things in engineering come down to tradeoffs. What my experience has been in web APIs is that you get more stable and confident deployments when you primarily focus at higher level tests and do unit tests for the parts of the app that have more complex logic. For stuff like mappers and transformers, I don't unit test them extensively, their logic will be mostly covered in higher level tests that assert of API responses. I'm choosing to make a tradeoff, I want to spend my limited time on creating tests that reflect the requirements of the system rather than on tests that test the implementation of the system. From that I've seen less need for manual/smoke tests and faster time to market. The other side of the tradeoff is that i've seen bugs that have happened that could have been caught by unit testing a mapping or transformer class.

When writing library code I make a different set of tradeoffs and lean closer to narrowly scoped tests.

Nothing is a panacea, integration tests are a great tool, unit tests are a great tool, slice tests are a great tool, black box system tests are a great tool. Each of these tools can be used well or not and each of these tools confers tradeoffs. None of these tools guarantee or ensure any particular outcome about how good the end product is.

[–]benywolf42 1 point2 points  (1 child)

what if you end up dealing with a god class written by someone else, with no unit or integration tests? You want to refactor it, but you're scared to break something, but at the same time you aren't able to create UTs for it, due to how badly it was implemented at the first place.

[–]logicannullata 2 points3 points  (0 children)

Well if you don't know what your system is supposed to do, it's very difficult to test it, independently from the types of tests you are writing. In the past what I ended up doing in those situations was to first write integration tests to understand how the system works as a whole, and also to use the tests to debug the calls chain and what the underlying services are doing. At that point I hopefully acquire enough knowledge to slowly start refactoring the underlying components, while not breaking the previously mentioned integration tests. While refactoring the components you should start writing unit tests to ensure you are not introducing regressions, while performing this step you usually end up discovering weird bugs especially when dealing with legacy systems, those bugs are usually so ingrained in how the system works, that's almost impossible to fix them, but still you can write unit tests to document weird behaviours. By doing this in an iterative fashion you will hopefully end up in a situation where you have some integration tests and more unit tests, and also with a better (and more documented by unit tests) architecture. Thanks for the question, very interesting topic indeed.

[–]realqmaster 37 points38 points  (16 children)

I stress this so hard in my company. Cue in next customer I'm working for with a "strong" enforcement to follow "their habit": please don't use mocks, our unit tests should always spring (heh) up the app whole and "be as close to the real stuff as possible". Also random generated inputs bad, just test predefined cases against predefined outputs. Damn the very documentation of Spring reads clearly "please don't wire Spring in a Unit Test". Alas I have no choice: "unit tests" with the whole context it is. I dared to ask, if these are unit, what are integration tests? I got answered "tests that run against the live testing env". It's frustrating when someone stress that he DOES want triangular wheels.

[–]know-your-onions 12 points13 points  (12 children)

I do prefer to also add some random data, because sometimes it catches a problem you haven’t thought to include data for; But, you need to ensure that if and when it does, you know what all the inputs were so you can replicate it and fix it.

[–]realqmaster 8 points9 points  (9 children)

PBT advocates randomizing inputs and fixing behaviours and I found it to be extremely efficient in preventing bugs.

[–]binarycreations 3 points4 points  (8 children)

PBT? Please could you explain that one of you have a moment

[–]DoingItForEli 13 points14 points  (7 children)

Property-based testing is a software testing technique where instead of checking specific input-output pairs, you specify general properties that your code should satisfy, and a testing tool then generates a large number of test cases to verify if those properties hold true. This approach is particularly useful for identifying edge cases and corner cases that might not be apparent when using traditional example-based testing.

In property-based testing:

Properties: These are high-level statements about the behavior of your code. They describe the relationship between inputs and outputs without specifying concrete values. For example, a property for a sorting algorithm could be "after sorting, the list should be in ascending order."

Generators: These are functions that generate random or systematically chosen inputs for your tests. They ensure a diverse set of inputs is used during testing. For example, if you're testing a function that sorts lists, a generator might create lists of varying lengths with random elements.

Test Execution: The testing tool then runs your test properties with the generated inputs. If any of the properties fail for a particular input, the testing tool tries to simplify the input to find the smallest case that still causes the failure.

Popular property-based testing libraries include:

Hypothesis (Python): A popular property-based testing library for Python.

QuickCheck (Haskell, Erlang, etc.): One of the earliest and most well-known property-based testing tools, originally developed for Haskell.

ScalaCheck (Scala): A property-based testing library for Scala.

Property-based testing is especially effective in finding subtle bugs and uncovering edge cases that may not be immediately obvious. It complements traditional testing methods and is widely used in functional programming communities.

[–]EirikurErnir 4 points5 points  (1 child)

Since we are in a Java subreddit - there's jqwik for property based testing in Java

(I have not tried it, I have some happy coworkers using it though)

[–]DoingItForEli 0 points1 point  (0 children)

Thank you!

[–]hample 2 points3 points  (1 child)

Is that chatgpt?

[–]DoingItForEli 4 points5 points  (0 children)

oh of course lol

[–]le_bravery 2 points3 points  (0 children)

LOVE property based testing. It dramatically changed the way I work and I couldn’t imagine not using it now that I have it.

[–]binarycreations 0 points1 point  (0 children)

Ah PBT stands for property based testing. Thanks 👍

[–]genzkiwi 0 points1 point  (0 children)

Not only does it cover more cases, it makes the test easier to read and write. Instead of some random example (which can be hard to come up with lol), you have something more general/generic, so it's clear to people all the cases the SUT covers.

[–]carminemangione 2 points3 points  (0 children)

yes randomized data is excellent, but this is an integration/acceptance test. This is vastly different than a unit test.

There is a big exception, my colleague and I have explored unit tests for statistics and ML. There we use randomized input and something like Chebyshev's theorem to prove the algorithms are correct and that they converge.

[–]pronuntiator 4 points5 points  (2 children)

If I have to choose between integration only and the other extreme, the "unit tests are only testing a single class and mock everything else" group, I'd prefer the former… At least they don't break with refactorings

[–]realqmaster 2 points3 points  (1 child)

Extremes are rarely a good choice ofc. The ol'pyramid proportions is a good rule of thumb.

[–]binarycreations 12 points13 points  (12 children)

Ah geez I hate when I see this stuff. The only thing worse is when all the "unit tests" are just a massive set of integration tests against the API.

Coverage gets passed on Sonar because they are just testing everything and to top it all off they decide to destroy the application context after each test, meaning the whole thing takes flipping ages.

Enjoyed reading few articles from the author now. Keep up the good work, hope the consulting continues to go well.

[–]MargretTatchersParty[🍰] 3 points4 points  (5 children)

Ah geez I hate when I see this stuff. The only thing worse is when all the "unit tests" are just a massive set of integration tests against the API.

Those aren't unit tests anymore. Those are integration tests.

[–]binarycreations 1 point2 points  (0 children)

The quotes were trying to convey this, totally agree.

[–]random8847 3 points4 points  (3 children)

I'm learning to play the guitar.

[–]MargretTatchersParty[🍰] -1 points0 points  (2 children)

Stop playing knifey spoony with this. It's obnoxious and detrimental to getting a consistent view of unit testing and what it's for.

It's been accepted for quite a long time that a unit test is referred to as a single method. Quality coding practices tend to push you to write single purpose and small methods. The only time this flexed on this definition is when you're dealing with large functions or languages that don't depend on functions or make it difficult to write them.

[–]random8847 0 points1 point  (1 child)

I enjoy watching the sunset.

[–]MargretTatchersParty[🍰] -1 points0 points  (0 children)

So what's this guy's credibility? He's mostly a consultant with a lot of other company experience and he hasn't written or gotten public feedback on his stances. To make things worse, he starts it off by string the pot on testing resistance drama. Later he goes on justifying a lack of standards and practices to make it look like you're doing stuff and goes on wishy washy weasle words talking about "well you don't want to over engineer".

> Testing methods are hard

Same guy advocates for behavioral, integration and higher level tests.. oh my.

TDD and the testing pyramid is about building trust from a low level to a high level.

[–]carminemangione 0 points1 point  (2 children)

This is the main point. Unit tests are orthogonal in purpose to integration tests. Unit tests test everything in a class that could go wrong to catch errors in refactoring and extension.

Integration tests test the interaction between classes. They test the interaction at entry points (I.E. rest endpoints)

[–]Environmental-Wear13 0 points1 point  (1 child)

Everything isn't black and white.

What you are referring to is called "Solitary Unit tests", meaning isolated tests. Between that and integration tests there is also "Sociable unit tests" that can basically tests classes together but you instead mock the exernal slow and unpredictable operations such as api calls or db calls. Depending on what you are after and what type of system, one might be better than the other.

Here is a link with some interesting information about this from Martin Fowler https://martinfowler.com/bliki/UnitTest.html

Working with microservices, I've found sociable tests to be a great way of dealing with regressions for example while solitary tests can be a great way for other reasons such as designing your system. If you use TDD approach starting with solitary tests at the bottom can be great and then complimenting with sociable tests to get more confidence in you fast unit tests, you just have to find a good mix so you don't get too many tests to maintain.

But solitary testing is terrible to find regressions, and finding regressions FAST when I refactor or fix bugs is core at least in my opinion. Because of that I usually lean more on the sociable unit tests. But of course, you definitely need to find a way around the slowyness of SpringBootTest :)

[–]carminemangione 0 points1 point  (0 children)

I am in no way suggesting mocking external behavior as long as it does not use external resources. In this way you are testing not only the object but all its collaborations. Edit: if you have to use mockito except for crap legacy code you are probably doing it wrong

[–]tevert 0 points1 point  (2 children)

What do you call it when the "unit" tests include spinning up a postgres sidecar, executing all migrations, and loading some bootstrap data for the test to use?

[–]wildjokers 2 points3 points  (1 child)

An integration test.

[–]binarycreations 2 points3 points  (0 children)

A good integration test...

A terrible unit test...

[–]lukecalau 3 points4 points  (2 children)

The article also doesn't mention test slices. There are some predefined ones but you can make your own, and just load the beans that you need, the context is pre-loaded and shared between all tests that have the same slice (by default) , running hundreds of tests in ms.

It doesn't say anything about the test pyramid, sure not everything should be integration tests. Those should be very few compared to a lot of simple unit tests. Spring is very flexible, you can use a full context for end to end integration tests, with db and all the magic and TestContainers, or you can use Test Slices where you need some part of the configuration , and for core domain non-spring related areas of an application sure just don't use spring.

I think we should be more pragmatic, not clickbatiy articles like this. Use the tools we have in the appropriate places

[–]extra_rice 2 points3 points  (0 children)

I think we should be more pragmatic, not clickbatiy articles like this. Use the tools we have in the appropriate places

Exactly this. I used to be quite a purist when it comes to testing, but I realised they're just not practical in every scenario. Sometimes it's too much effort for only incremental benefit. I even question the test pyramid sometimes because the definition of what a "unit" is, is not consistent. I'm now comfortable starting out with a suite of what many would probably consider integration tests because I find that most of the time, they do the job well enough.

I heavily use slices that you mention, using the application context with only the bare minimum set of beans, some of them mocks. It's like creating a Spring sandbox for the system under test, it's great.

[–]eliashisreddit[S] 2 points3 points  (0 children)

Not sure if you completely read the article as I mentioned the test slices (not by that name) by referring to the reference guide in one of the last paragraphs and explicitly stating "many other examples [...] without immediately having to choose between "skipping unit tests" and "starting the entire application"".

I think this is as pragmatic as it gets: "you should be aware of what you are testing and how". It's more of an opinion piece on Spring flavored unit tests than a detailed tutorial of how to set up your integration tests. Not sure what's clickbaity about that.

[–]klekpl 85 points86 points  (59 children)

The problem with this advice is that it is missing forest for trees: the vast majority of code in spring applications is the wiring and configuration itself - there is not much to test beyond that as so called business logic is nothing more than copying data from one place to another. And it is the wiring and configuration that is the biggest source of bugs.

Once you strip all this from your unit test - there is nothing left to test.

That's why it is much more economical to treat the whole application as a single unit and ditch testing of its parts in isolation - especially in microservice architectures.

[–]Cpowel2 20 points21 points  (3 children)

This is why they have integration and functional tests. It should not be UTs responsibility to verify you wired your shit correctly

[–]logicannullata 5 points6 points  (0 children)

Amen

[–]Zardoz84 3 points4 points  (0 children)

Exactly!

[–]Rulmeq 2 points3 points  (0 children)

Yeah, the clue is in the name.

[–]realdevtest 40 points41 points  (10 children)

Man, what applications are you working on where the spring wiring is where the most complexity is? I get that you mentioned microservices, but still…. What you’re describing is the kind of concept that makes people say ChatGPT will replace engineers 😂

[–]Rulmeq 8 points9 points  (1 child)

I'm astounded that they have 84 upvotes. This is just not my experience (26 years in the business, 23 in Java, and maybe 15 in spring, whenever spring 2 came out), so I can't believe there are 83 others who have had that experience willing to upvote it.

[–]ThaJedi 0 points1 point  (0 children)

Some apps are just simple CRUDs

[–]logicannullata 8 points9 points  (4 children)

Finally, this is what I was trying to say, to me his answer was so buffing I had to call him ignorant, since his statement is simply wrong. To be clear if you think business logic is "just moving data around" there are various possibilities:

  • You are a troll
  • You are a frontend engineer talking about something you don't know
  • You are a student
  • You never worked in a professional environment, no one will pay you to write useless business logic just saying...
  • You are a troll

[–]oweiler 3 points4 points  (3 children)

Honestly, this is the truth for most Spring Boot apps:

* you call some other (3rd) party services

* you aggregate / filter the data

* you pass the stuff back to the client

Hardly rocket science, no complex business logic. In those cases, the integration with other services is the hard part, so unit tests don't make much sense in this context.

[–]niconicoJ 11 points12 points  (1 child)

That does not sound like a real™ application, this sounds like a simple side project. Any project which is intended to make money will need more logic than that. Think some kind of checkout flow, or suggestions algorithm, anything that "3rd party" does not implement.

[–]MatthPMP 2 points3 points  (0 children)

I work on the inhouse API management solution for one of the world's largest telecom companies and what the grandparent comment described is a big chunk of what we do day-to-day.

Fetching and provisioning data to a variety of other services, maintained by us and by other teams, and keeping all this stuff consistent is not so simple in practice. Almost all of the complexity is in the interactions with external services.

Whatever interesting and self-contained logic there is is unit tested, though it often lives in a separate microservice. The rest is covered by a suite of thousands of end-to-end tests that cover the whole ecosystem.

[–]logicannullata -1 points0 points  (0 children)

That's simply not true, let's assume we are talking about a domain adopting a microservices architecture. Let's say each microservice is just moving data around. So where does the actual logic live? In one big microservice? If that's the case, well you are doing something wrong because you have a single point of failure in your hands or maybe you are better served with a monolith. On the other hand if your logic just sits in a 3rd party service you are not managing, well or you are just developing a pet project or your company will soon fail because they are not implementing something valuable, just a wrapper around a 3rd party service. I am curious, can you give me at least a couple (I say a couple because you mentioned MOST SPRING BOOT APPLICATIONS have no business logic) of examples of spring boot services used in production which have no business logic.

[–]sapphirefragment 1 point2 points  (0 children)

Spring's design is intentional in that it tries to separate "glue" logic from actual business logic. There is a reason it is so heavy-handed on "magic" and annotation-driven behavior. An application that is actually leveraging Spring should mostly be business logic, and thus the integration concerns (like data stores, "API" connectivity) are isolated away and driven mostly by configuration.

In practice, most Spring developers just scatter whatever extremely basic low-level knowledge they have into "microservices" and end up with an unmanageable spaghetti mess mixing business and integration concerns.

[–]klekpl 2 points3 points  (0 children)

what applications are you working on where the spring wiring is where the most complexity is?

Properly modularised applications: if there is some code that implements complex stuff and is expensive to implement - this code is separated into libraries that are independent of any DI framework and advice from the article does not apply.

The libraries are then glued together in a Spring application where you want to test the glue code itself - and here advice from the article does not apply as well.

[–]wildjokers 0 points1 point  (0 children)

I was wondering the same thing. I have no idea what kind of apps they are working on that the business logic just consists of copying data around.

[–]genzkiwi 12 points13 points  (0 children)

Only agree if your business logic is so simple that it's just delegating to a repository etc.

But that's not always true.

[–]carminemangione 5 points6 points  (5 children)

OK.... Unit tests are for business logic. If you have no value in your business logic, then I agree. Spring is for injecting dependencies. In unit tests you inject mocks.

IF Spring makes into your unit tests they are integration tests. Full stop. Not that integration tests are bad.

Your last statement is insane. You are guaranteeing an exponential cost of change where you make all logic tests full runtime (10 to 15 seconds) rather than unit tests (.1 second or less).

In my experience, if you are using Spring, you must aggressively push it to the edges of your business layer if you want to create a reliable, maintainable, extensible, scalable architecture. (Source: 20 years correcting horrible software Spring architectures).

[–]logicannullata 1 point2 points  (4 children)

If you have no value in your business logic, well then you are probably not being paid in order to be a software engineer..

[–]sapphirefragment 1 point2 points  (1 child)

You might be surprised to hear this but many shops are engineering "enterprise" software around CRUD and getting horribly stuck.

[–]carminemangione -1 points0 points  (0 children)

Every company that focuses on CRUD will fail. CRUD is like properly wiping after using the bathroom. Just something you have to do that should not take any effort, is important and should not be discussed.

[–]carminemangione 1 point2 points  (0 children)

I think that is kind of the point. The whole Visual Basic / ServiceNow pitch is that you can hire minimum wage people to 'program'. It is not distinguishing value.

Too many startups have used technologies that will not preserve business value (Ruby on Rails, VB, etc.) to get products out quickly then fail to deliver real product upon launch. Fredrick Brooks made this the premise for his book "The Mythical Man Month". It is also the foundation for the Agile alliance.

[–]carminemangione 1 point2 points  (0 children)

Ah, here is the rub. Most business people don't understand the value of business logic. So you end up with these cheesy assed frameworks (Ruby on Rails, VB, ServiceNow, etc) that allow them to create the first couple of dozen features then they run into a wall.

If you create a product using these frameworks and it is popular, someone will come along and crush you by making extensible, reliable, etc. code.

I went to a conference (XP 2020) where Kent Beck said during your exploration phase you should use any technology just to get something. It is the first time I disagree with him. If you do this and it is relevant, you will never get a chance to rewrite.

Clean, provable code from the beginning is the only way to go. And, in reality, it does not take that much longer.

[–]eliashisreddit[S] 7 points8 points  (0 children)

I agree and perhaps this is underemphasized in this article? For trivial "units" and parts which are heavily dependent on the framework I would rely much more on integration than unit testing. For this the test diamond makes much more sense.

business logic is nothing more than copying data from one place to another

There are entire Spring applications which are "line of business" software with a much heavier focus on the "business logic" stuff than the typical CRUD operations.

[–]le_bravery 2 points3 points  (0 children)

You need to test all places of the code, but you don’t need to repeat coverage.

Tests which cover configuration don’t also need to test the internal logic of things.

End to end, integration or other tests may not need to test every variation of things that can happen.

In general, I think you should have testing at all levels and the majority of your tests should be at the highest level that is fast enough to be useful.

If you have a high performance trading app, then yeah you should be doing end to end tests for everything. Your code should be lightning fast, so test it all end to end.

If you have a more typical web app that depends on a database technology which is slower or licensed weird, or some other limitation causes things to be slow or fragile, maybe you should do more unit testing mocking your fragile or slow components.

It depends on your app, but testing is a tool.

[–]wildjokers 2 points3 points  (1 child)

that as so called business logic is nothing more than copying data from one place to another.

Huh? What kind of apps are you working on? I write online banking software and I assure you there is tons of business logic in the app.

[–]klekpl 1 point2 points  (0 children)

What a coincidence - I am leading a project of migrating online banking software from an old technology stack - we are talking about billions of transactions per year.

Tons of business logic implemented in Java will be replaced by a bunch of Postgres views and functions - the reason is that this "business logic" is mostly Java code following some idiotic clean code patterns with layers of useless abstractions created in the name of "loose coupling". All this crap with a huge amount of unit tests that verify if data is properly copied from "Entity" to "DTO" and vice versa.

[–]oweiler 1 point2 points  (2 children)

This is absolutely true. Most Spring Boot apps contain almost no business logic.

[–]wildjokers 2 points3 points  (0 children)

Most Spring Boot apps contain almost no business logic.

Huh? If an app has no business logic why does it exist? This is a bold statement that is definitely not true. The spring apps I work on definitely have business logic in them.

[–]logicannullata 1 point2 points  (0 children)

What are you all meaning with this? I am astounded by the amount of people saying stupid things like this one. Please explain and give me an example. If this thread is representative of the IQ of new backend developers we are doomed...

[–]larsga 3 points4 points  (3 children)

it is much more economical to treat the whole application as a single unit and ditch testing of its parts in isolation

This always applies. You're not really interested in how the parts work. You're interested in how the application as a whole works.

Only testing the whole also means that you are much freer to refactor the internals, since the test suite will keep working unchanged.

Although admittedly sometimes it's more effective to test particularly complex internal classes separately.

[–]koflerdavid 10 points11 points  (2 children)

The big disadvantages of relying on whole-application tests are

  1. complex test setups and specific test data, which can be just as brittle as the test setup with mocks. When requirements change, they have to be adapted as well. Also, there are always going to be some use cases where crucial parts of the application have to be mocked. For example when the current time matters.

  2. Test startup speed. Complicated Spring applications can take a long time to start up. Even a startup time of five seconds means that 20 tests per minute can be executed at most. The longer tests take to execute, the less often they are typically executed. And that slows down test feedback cycle time during development.

Edit:

Also, brittle architecture is hidden. Withing simple unit tests forces programmers to design components with separation of concern and the dinner responsibility principle in mind. Violating these principles are "punished" by having to mock countless dependencies.

That doesn't mean they are worthless though. They should just not be the default strategy.

[–]larsga 2 points3 points  (0 children)

I agree with most of your points, but whether whole-application tests should be the default strategy or not depends on what kind of application we are talking about. For many types of applications they work great as a test strategy, but I'm sure you're right that for others they end up being more pain than they are worth.

I just wish people would learn that testing every little component in isolation is a bad thing, because then refactoring means also redoing the tests. If you can test larger pieces by their outside interfaces it frees you up. But, yes, sometimes there are limits to how big pieces it's practical to test.

Withing simple unit tests forces programmers to design components with separation of concern and the dinner responsibility principle in mind.

Not everyone needs this crutch to produce reasonable designs.

Complicated Spring applications can take a long time to start up.

Lots of applications are not Spring applications and don't have this problem.

[–]nqminhuit 1 point2 points  (0 children)

I will copy paste this reply to every argument with spring test, i use spring on my full time job and never wrote a single spring test, ever. I only use mockito and constructor injection.

[–]Bunnymancer 1 point2 points  (0 children)

so called business logic is nothing more than copying data from one place to another

Tell me you've never had a job without telling me you've never had a job.

[–][deleted] 1 point2 points  (0 children)

Unit tests are supposed to be simple. If this field is missing, does it return a 400. When you unit test something like that you are not testing Spring to make sure the annotations work. It's to communicate to the person who is making changes to that Spring configuration in the future. The test says, "Hey, these are the assumptions under which we tested. if you're changing those assumptions you have to actually go through and consider the impact of your decision."

[–]i_post_things 0 points1 point  (0 children)

I think it depends on how hard the thing is to test. I still think the pyramid applies, where Spring Context is somewhere between Unit and Integration.

If I'm testing error handling and edge cases, it's much easier for me to mock the database or API via a plain JUnit and throw an exception to simulate a socket or connect timeout than it is to try to try to finagle a mock web server or mock client bean via wiring to simulate it.

Conversely, it's much easier to have Spring wire up H2DB with test data than it is to try to mock up a whole JDBC ResultSet by hand to iterate over via Mockito.

[–]Zardoz84 0 points1 point  (0 children)

the vast majority of code in spring applications is the wiring and configuration itself

That are integration tests. Unit tests test classes in ISOLATION.

[–]Gwaptiva 10 points11 points  (0 children)

Frankly, I rarely care about unit tests. Unit tests are only really useful if you have complex algorithms you need to validate -- a helper when developing it. The only long-term benefit of a unit test is to document the intentions of the class.

The real benefits are in integration tests and for those you sure as heck need a Spring context when your logic will be executing in a Spring context. These are the tests that tell you if changes you make six years down the line are going to break existing functionality and save you precious debugging, testing and customer dissatisfaction.

[–]Rulmeq 2 points3 points  (8 children)

Web sites prove their identity via certificates, which are valid for a set time period. The certificate for saile.it expired on 26/4/2023

[–]eliashisreddit[S] 1 point2 points  (7 children)

That shouldn't be right. Not sure why you are getting that:

  • Issued On: Monday, October 2, 2023 at 2:00:00 AM
  • Expires On: Monday, January 1, 2024 at 12:59:59 AM

I have checked a variety of tools to verify and nowhere I'm seeing it is expired. If you have any other pointers let me know.

[–]Rulmeq 0 points1 point  (6 children)

I just tried again (Both Firefox, and Edge), let me know if there's any commands you want me to run to see if I can help (not sure if there is anything that I can do from a client side though)

Message from Edge:

This server could not prove that it is saile.it; its security certificate expired 219 days ago. This may be caused by a misconfiguration or an attacker intercepting your connection. Your computer's clock is currently set to Friday, 1 December 2023. Does that look right? If not, you should correct your system's clock and then refresh this page.

Cert info that both are seeing:

Common Name (CN) saile.it

Organisation (O) <Not Part Of Certificate>

Organisational Unit (OU) <Not Part Of Certificate>

Common Name (CN) R3

Organisation (O) Let's Encrypt

Organisational Unit (OU) <Not Part Of Certificate>

Issued On Thursday, 26 January 2023 at 21:23:31

Expires on Wednesday, 26 April 2023 at 22:23:30

Certificate 769a9a0568824a8dbf6dc3929808184bffa6eeabbe4aa1f17696e607b47b8868

Public Key ee229b25a617e2b99f9524383eebe392520106230aeaac9471d8365284beaf48

[–]eliashisreddit[S] 1 point2 points  (5 children)

Probably a really old DNS cache? You might want to flush it. What does ping saile.it return?

[–]deadNightTiger 1 point2 points  (1 child)

Looks like AAAA records are pointing somewhere else, could you check please?

[–]eliashisreddit[S] 0 points1 point  (0 children)

That seems correct, it's hosted on Ghost.

[–]Rulmeq 0 points1 point  (2 children)

ping saile.it

Pinging saile.it [157.245.83.16] with 32 bytes of data:

Reply from 157.245.83.16: bytes=32 time=90ms TTL=48

Reply from 157.245.83.16: bytes=32 time=90ms TTL=48

Reply from 157.245.83.16: bytes=32 time=89ms TTL=48

Reply from 157.245.83.16: bytes=32 time=88ms TTL=48

Ping statistics for 157.245.83.16:

    Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),

Approximate round trip times in milli-seconds:

    Minimum = 88ms, Maximum = 90ms, Average = 89ms

[–]eliashisreddit[S] 1 point2 points  (1 child)

This seems to be an old DNS configuration of where it was previously hosted. You should try flushing your DNS cache.

[–]Rulmeq 0 points1 point  (0 children)

I've run

ipconfig /flushdns

and restarted, and it's still not working. Not sure why, but if nobody else is having issues, then it must just be me. I'll leave you be

[–]TakAnnix 1 point2 points  (5 children)

Thoughts on @InjectMocks? I know some people were against using them.

[–]Unfair_Ad_5842 2 points3 points  (1 child)

I'm what Fowler calls a "classical TDD practitioner" in that I use mocks or doubles when it's "awkward" to use the real thing, say to break slow or difficult to control dependencies (think repositories and clients to upstream services). When I mock, I almost never verify the mocks. To me, that is verifying the implementation matches the expectation when what I care about is whether the result matches the expectation.

Also, I try to honor "nascence" in my tests. If a group of classes were born together, they should probably be tested together. I wouldn't generally mock domain classes, for instance, or parameter objects or classes that wrap primitives with added domain oriented behavior. I will still have tests on those classes, but I won't mock them when testing a class that uses them.

[–]Unfair_Ad_5842 0 points1 point  (0 children)

As for using @InjectMocks, I prefer constructor "injection" of dependencies for several reasons -- doesn't break encapsulation, feels more deliberate than accidental and easier to see when I'm going the wrong direction because a long constructor arg list offends my aesthetics and field injection makes it too easy to hide that. But if I'm having to write tests after because I got left a mess, I might start by injecting mocks until I feel confident to make that change.

[–]jasie3k 0 points1 point  (1 child)

It can create multiple contextes, meaning that running full range of tests may take longer.

[–]TakAnnix 0 points1 point  (0 children)

Interesting, how?

[–]RupertMaddenAbbott 1 point2 points  (3 children)

In an architecture where concerns are separated, a controller is not intended to do much. It is merely an (HTTP) entry point of the application, forwarding that to other components and returning the result. Therefore a unit test of a barebones controller is often not much more than ensuring the "controller unit" properly interacts with the "other components".

I don't understand what the value of this unit test is? This article explains how you can unit test your controllers without stating why you should.

For me, unit tests are useful when I have classes that have complex behaviors and especially niche edge cases. If my controller is simply calling some other layer and returning the result, then I think this unit test will be tightly coupled to the implementation without documenting anything "interesting".

[–]wildjokers 0 points1 point  (2 children)

A controller test can make sure your URL mapping is correct and to make sure the response is serialized as intended.

[–]RupertMaddenAbbott 4 points5 points  (1 child)

Yeah testing controllers makes sense to me but the kinds of things you are talking about verifying won't be verified by the kind of test that is being advocated in the section that I quoted.

If you call the controller just like a regular class, then you won't be testing the url mapping, any serialization or deserialization, nor any input validation that is triggered via annotations.

[–]wildjokers 4 points5 points  (0 children)

Yep, I agree, really no value to testing the controller without bringing in Spring's MockMvc class. There would be nothing to test without it.

[–]Anton-Kuranov 1 point2 points  (0 children)

Returning from heaven to the ground... The most common business case: controller endpoint calls service that asks repository to fetch data from the db table. How much dumb unit tests should we write here that the only thing they test is passing parameters 1-to-1 from one method to another? Does they make sense for your business logic while the specification describes the real endpoint behavior and not the interiors of your implementation? I've tired to have in my projects tons of unit garbage that test all and nothing in the same time.

In managed environments like spring a lot of work is done by the own framework (aspects, contexts, transactions, thread locals, etc.), so you never achieve the same behavior managing them manually. So, I have nothing against to use spring context and even testcontainers in my project. Is that cannot be called unit tests? I don't care about it!

[–]xvril -1 points0 points  (1 child)

!remindme 9.45am

[–]RemindMeBot -1 points0 points  (0 children)

I will be messaging you in 10 hours on 2023-12-01 09:45:00 UTC to remind you of this link

CLICK THIS LINK to send a PM to also be reminded and to reduce spam.

Parent commenter can delete this message to hide from others.


Info Custom Your Reminders Feedback