There is a big distinction between the purpose of your distro package manager and the purpose of a languange dependency manager such as npm, pip, rubygems, cargo or composer.
While the first is for installing applications on your system (and also used for c libraries, because c has no official dependency manager), the second serves much more specialized purposes tailored for the language, such as
- os-agnostic (imagine the work in getting all them distros update just one package)
- compiles/postprocesses dependencies in deterministic ways
There are some things that are implausible to test without injecting not-production-code. Many of these look like errors (bad inputs, expired sessions, etc.) that should be intelligently handled.
A mock is a fake. Just so we are using the same terminology I'll define my terms:
Fake: a test implementation of something that stands in for production code to solve certain testing problems.
A fake can implement an API and provide simplified behaviour. A good example might be a piece of code that stands in for a hardware device driver that you use when you don't have access to the hardware. Another very common use of a fake is to fake out a UI layer. There are lots of other common places for pure fakes.
There are 2 special kinds of fakes:
- Stub: An implementation that returns canned data.
- Mock: An implementation that encapsulates an expectation.
The most common type of mock is a mock that encapsulates the expectation that a certain function is called.
Sometimes you don't want to write a pure fake because it's more than you need. Most of the time you can adequately test what you want without talking directly to some inconvenient real API with a stub.
There are many good reasons to use pure fakes or stubs. Like I mentioned, it might be because hardware is involved. Sometimes it's because you are using some SaaS that you don't want to really access in your tests. Very often it's because the real service is just too slow (I'll need another post to describe why this is super important, but even if you don't agree I hope you will see that it's not the only reason to use a fake or a stub).
As soon as you use a fake or a stub, though, you leave a hole in your tests -- you have no way of knowing that the production code uses the real service in the production code. This is when you need a mock. You can go without testing it, but mocking it in this circumstance is almost always a better solution.
Of course, there is a school of thought that reaches for mocks first. This school of thought is often referred to as the "London School" because it is very popular (and probably originated from) several very famous people who work in London. The GOOSE book, which describes outside-in programming is probably the best description of this school of thought. I've met quite a few of the people who are standard bearers for this school of thought and they are very talented developers. Again, I would need another post to describe why I don't think it's the best technique to reach for first, but it's an area where reasonable people can disagree.
Outside-in and promiscuous mocking has several advantages -- especially when you are less experienced. It provides a framework for reasoning about how to do design. Personally, I am not a big fan in general, but there are times I use it -- I just replace the mocks with non-faked tests after the fact.
So, while I agree that one should have the mindset to use mocks as a technique of last resort, it's still a valuable tool in your arsenal.
>Outside-in and promiscuous mocking has several advantages -- especially when you are less experienced. It provides a framework for reasoning about how to do design.
I find that any additional couplings to your code makes the couplings you do have more painful to work with.
Some people think that by adding this kind of 'tight coupling' - low level mocks / unit tests - you feel that pain earlier on in the design process and that pain drives you to make a better, more loosely coupled code design.
Personally, I think that this approach is rather like forcing kids to smoke cigarettes every day to show them how much of a filthy dirty habit it is.
Very similar to cigarettes, the really nasty effects don't usually materialise until quite far down the road -- when you don't have much opportunity to deal with them.
However, I don't think it has to be like this. I sometimes call mocking "wish based design". Sometimes you don't really know what shape is going to be good. You know ahead of time that TDDing a solution is likely to churn a lot of time writing stuff you are going to ultimately throw away. You can spike a solution to get some more information, but there is often a lot of pressure to ship whatever you spiked, no matter how successful you were (or how bad your tests ended up being).
With a mocking approach you think to yourself, "If this were already written and I was using it, ideally how would it work?" You mock your wish and you write the use side of the code in your tests. Quite frequently this reveals most of your naivety and allows you to design what it is you need. At that point, you replace the mocks with real implementation. It's at this point where I differ from the main proponents of the style (or at least last time I talked to them, which is admittedly several years ago). I will TDD my implementation, removing the mocks as I go.
So, on reflection, I guess I use it in places where I would otherwise spike a solution. Again, for fairly junior people, it can be a great technique to help them understand where to start. Often I find that junior people can't do TDD because they simply can't envision what they are building. They will spike a solution, jam a couple of tests in to show that it's basically working and call it a day. A mocked solution often ends up exposing state in the places where it needs to be. This is frequently better than the spiked solution which is usually an encapsulated ball of mud.
As I said before, though, I use it as a technique of last resort, not a technique of first resort.
>However, I don't think it has to be like this. I sometimes call mocking "wish based design". Sometimes you don't really know what shape is going to be good.
That's why I write executable specs at a high level with my "wish based design", make that "test" pass and then refactor. Wish first -> code next -> only well designed code after that.
I don't feel the need to dive down another level and mock out real modules that are already covered by this high level test. That's just creating unnecessary work for myself and introducing a potential source of test bugs (where the mock doesn't match the reality).
If you don't mind performance, using socket.io gives you stay-alive on the ws connection. If you don't mind writing the 5 lines of code it takes to implement stay-alive for normal websockets then socket.io makes no sense anymore.
It provides fallback connection methods if WS connection fails, as it can do if you have, for example, a HTTP/1.0 only proxy between you and the client. For example it was not that long ago that nginx did not support proxying HTTP/1.1. It is getting less and less relevant but at least for me the socket.io code has worked for a long time and there hasn't been a need to rewrite it.