This 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!