4 ms·
> Steve correctly notes that front-end programming is hard, but leaves out maybe the single most important reason why: It’s super-hard to unit-test prop
by zachrose 9y ago
> Steve correctly notes that front-end programming is hard, but leaves out maybe the single most important reason why: It’s super-hard to unit-test properly.
Can anyone elaborate on this?
- contingencies 9y agoSee Selenium, Chrome headless, Firefox headless, etc.
- hittaruki 9y agoAFIK, thats not unit testing. When you can write unit tests, they are usually lighter, faster to run and easier to maintain. You would use Selenium for integration testing.
- ender7 9y ago- UI code spends most of its time interacting with a UI API (Android view framework, whatever iOS uses, DOM/CSS in web). These APIs are enormous and are rarely designed with testing in mind. Mocking or faking the pieces of API that you need to interact with is frequently a daunting or impossible task. - Followup to the above: because so much of the "work" involved with UI code involves interacting with another API, usage of that API tends to spread itself through the entire codebase like cancer. You can create a careful separation of concerns, but it takes a lot of upfront work, verbose boilerplate code, and constant vigilance -- if you're just handed an existing UI codebase you won't have that luxury. - UI code is highly stateful and highly mutable. A core skill in UI programming is managing your app's enormous state profile. Single-path, whole-world rendering techniques like React/other shadow DOM techniques really help here. - UI is highly temporal. It needs to change over time. Testing anything to do with time is an enormous headache. Animations can force you to temporarily separate your underlying state from its representation, which is a testing nightmare. But just in general, writing unit tests that involve time passing is infuriating and fragile (even if you can control time passing via a mocked clock object). - A lot of core functionality in UI involves different pieces handing off to each other -- one window transitions to another window, etc. To test that properly you need integration tests, but now your integration tests also need to interact with the underlying OS/browser (because you can't separate any of that stuff out). So your integration tests are in general way flakier than you can get on UI-less systems that really only need to worry about faking out a DB of some kind.
- ocdtrekkie 9y agoThis comment is probably missing the biggest part of the problem: The vast number of end devices that will need to present the UI. Even if you're building for iOS, there's several different devices that you need to account for, each with their own quirks, shapes (notches?). But once you go talking Android or web, you're talking about hundreds of potential niche cases, and all of these can change on a yearly basis.
- steadicat 9y agoNot to mention that UIs are visual, i.e. defining what is “correct” in terms a computer can understand is very hard. There are dozens of criterias (colors, spacing, borders, shadows, fonts, behavior, etc.), and often what is accetable is fuzzy and sometimes even subjective. Take spacing: if something moves 200px it's probably broken. But a couple pixels off are usually fine. Unless those 2px are the vertical position of an icon inside a button or two things that need to align. Where do you draw the line? Humans are pretty good at spotting when things look off just by looking at a screen. Computers, not so much (yet).
- jeremiep 9y agore-frame in ClojureScript is probably the best frontend library I've ever worked with. Solves a lot of the unit-testability problem of UIs. However, after spending some time with it, literally everything else now feels dramatically inferior.
- danans 9y agoI'll try. Unit testing frontends is hard because to create a test case, you must simulate a human user's behavior (clicks/taps on a UI) which is inherently complex, and has a lot of possible variations, and then test the correctness of the response the system displays as a human would perceive it. Just because the system sent back the correct data doesn't mean it was rendered in a way that the user can see/understand. With non-user-facing systems, you can just send the input, and test computationally that the output fits the expected value or range of correct responses. Of course, one could create a vision system that tested the response of a UI for correctness, but that would be ... super-hard.
- mattmanser 9y agoIt's wrong anyway, if you're someone who doesn't bother with unit tests writing front-end code is still a nightmare compared to back-end programming. So it can't be the unit tests that are the problem. It's more a problem that iterating/debugging/testing is generally so much harder for front-end, however you do it and regardless of whether you're dogmatic about unit testing or not. Focusing on the difficulty of unit testing is simply focusing on one of the symptoms rather than the root cause. Like in backend code, I can back out of a method, rollback to the start of a call and re-run it all again when debugging. In front-end code, that's simply not possible because there were a bunch of user interactions and you can't re-run them, you have to restart and click, click, click.
- andy_ppp 9y agoIt got a lot better unit testing UI recently especially being able to snapshot test different scenarios. In the end if you have stateless React components testing with jest and enzyme is easy. Again supplying state hooks for the various different interactions a more complex component might have is perfectly possible. Additionally separate your components logic from rendering and test that; e.g. a calendar widget I just created has a load of tested functionality for creating lists of dates with various attributes (isCurrentMonth, isToday, isSelectable, isSelected, etc). This can be unit tested just fine. Finally I’d strongly recommend cypress for doing e2e testing.
- mikekchar 9y agoThis is a great question. The answer is that it depends on what you mean by "unit test". I will actually state that it is not hard to unit test front-end (or more generally UI) code, but I think that the reason for my differing answer is far more interesting than the answer itself. First, why do many people think that unit testing UI is difficult? How do you unit test UI? There are a couple of schools of thought about "unit tests". A very prevalent one is that you write a "unit" (usually a class) and then you stress test the interface. Often this is coupled with faking/mocking collaborators because we are only interested in testing the interface. This is kind of problematic with UI code because imagine that I have a class that is derived from a dialog box. How do I test (for example) that the desired functionality is run when I hit the "OK" button? The problem is that the "OK" button is often not a programmable interface on my dialog box. Someone clicks on the button, whatever UI framework I'm using takes over and then magically some callback (hopefully) get's called. I can (sometimes) hack into the depths of my framework and maybe mock the callback to make sure it gets called when a button is pressed, but usually frameworks are not built to accommodate this kind of work. I can also use some kind of UI simulator to press buttons and that would work OK. But I'm going to back up here and suggest that this is not a unit test. It's an automated regression test. Part of the problem is the framework is "hiding" the internal functioning of the UI. I can't test the OK button, because I just can't get access to it. For me this is a big hint that "I'm doing it wrong". In fact, I don't subscribe to the common view that "unit testing" is about testing the interfaces on a class -- in other words, black box testing. Unit tests are specifically different than other kinds of test: they are white box tests. I've described this a few times and have yet to find a way that works particularly well, so forgive me if I fail yet again (you can happily walk away with only the first half of this message :-) ). Imagine that instead of "testing", we simply want to insert "probes" into our code. The probes will measure the operation of the code in a specific place and warn us if something is outside of our expectations. For example, imagine cooking a roast beef (sorry if you are vegetarian -- you can imagine some other kind of roast). We want to cook the roast to a certain internal temperature. There are many ways to do it. A "black box" approach would be to weigh the roast, measure the temperature in the oven and then time how long we are cooking. We can then derive the probable internal temperature from these external parameters. The "white box" approach would be to stick a thermal probe (thermometer) into the roast and measure the temperature directly. This allows us to see exactly what's happening on the inside, but at the cost of violating the roast's encapsulation. My opinion is that "unit testing" is this latter kind of testing. We want to expose the internal state of things and to measure it directly -- rather than deriving what we assume the state to be from some defined interface. I furthermore believe that "TDD" refers to the systematic act of finding appropriate internal state and exposing it. Test first is a good way to do that because you end up probing the state before you have written any interfaces -- it forces you to open up that internal state. There are other ways to do it, though. All of that to say that UI code is not actually any different than any other code. Our difficulty is not that the events driving the system are generated by a user. Our difficulty is that the frameworks hide the internals and stop you from writing unit tests (by my definition). This happens frequently when you are trying to TDD legacy code. The legacy code was not written in a way that allows you to write unit test -- you are forced to write integration tests because you don't have access to the internals. This is exactly the same with most UI frameworks -- they were not written to allow unit testing. And since you can't (or really don't want to) start refactoring the framework, you are stuck. Having said that, I have found some frameworks to be amenable to unit testing. For me, the best I've found in the web world is React. Interestingly, though, I do not use the React testing framework because it is not amenable to unit testing (by my definition). I build my own. Hope you found that interesting!
- Sophistifunk 9y agoIt's wrong. UI code isn't hard to unit test. It's borderline-impossible to integration test the state of the art in "controlling a browser to poke at your UI" is woefully inadequate, as are the debugging mechanisms available when anything unexpected happens. And I'm talking about a single-browser suite of tests, not even trying to assess cross-browser testing, testing on the nigh-infinite number of (device-spec * OS version * "value added" OEM android garbage libraries * custom build of chrome) matrix that is mobile devices.