
Hi Habra!
I will not offer you another trendy framework for tests, but just show you the approach to tests and documentation that I use in projects developed in i-Free. Perhaps you will like it and you will start organizing projects in the same way or point me to obvious problems.
Many web developers do not like writing tests, and I am no exception. But tests reduce the number of bugs, and if your application gets bigger and bigger, you won’t leave the tests. In addition, in small companies I often met juniors, who generally prefer to write code in text editors (this increases the number of errors, because the editors do not check the code). How to start slowly using tests without pain and suffering ?! There is a way out - connect autotests.
')
The essence of the post in the picture on the left. This is what I used to lack in my daily work. I wanted to have a tool that you can very easily poke into the code and make a general conclusion about its vitality and suitability.
Auto tests
Problems:- The developer is too lazy to write the tests themselves.
- Developer too lazy to write extra code for tests
- Developer is generally too lazy to think about tests.
Solution: Connect to the application through the API of any framework that the developer used, and run the auto-tests through it.
Where to connect:- Event hook functions (like $ ("# button) .click (), core.addEvent (), helper.onClick (), etc.)
- Listener functions (like $ ("# button) .on (), event.listen (), mediator.listen (), etc.)
- Initialization functions (like $ (body). Ready (), utils.ready (), etc.)
- Callback functions in AJAX requests
What to check:- Performance with invalid arguments
- The presence of random bugs due to typos (such as missed;)
- Presence of all DOM elements necessary for the script
You can call all functions in try / catch with a bunch of random parameters and without them. See which of the functions fall off. In addition, run through all the links and queries of DOM elements and check their presence in the layout.
What does this give us:It is clear that this is quite meaningless testing from the point of view of the logic of the web application. But on the other hand, we can effortlessly and quickly recheck the assembly from the already stupid typos and absurdities.
Bonuses:- It can be argued that this autotest can cover 90% of the functions of the application
- Such a test requires absolutely no effort from the developer in terms of support, updates, etc. Roughly speaking, he may not think about them at all.
- There will still be bugs, so there is no unnecessary check.
- To test, we don’t need to change the structure of our application at all.
Once again:- This does not cancel unit tests!
- This is meaningless in terms of application logic!
- Tests for the sake of tests - bad
But! It is free and easy, and it's better than nothing. If you show this to your juniors, they will make sure that it is actually simple and without pain, which means they will be more loyal to the tests, which means they will come to the thought of writing unit tests faster.
Scheme of work:
We have our application consisting of heaps of modules. To check its work, we need to write a unit test for each module. It is good if we do this, but, in fact, most developers will not waste time on it, especially in small applications. Also, if you take any ready-made solution for testing that requires changing the code of the modules - no one will do that. On the other hand, it is very easy to insert a couple of functions into the layer between the libraries and the application (if you do not have such a layer, you can insert these functions into the library itself). And at the exit, we already get at least some testing (you can call it “fool testing”, which will poke every button). Well, if you still had a layer - generally wonderful. And you can also make a substitution of the function AJAX requests.
If your code is broken into modules and is designed as:
(function(global) { var module = { _methodA: function() {}, _methodB: function() {}, _methodC: function() {}, init: function() {} } module.init(); })(this);
or similar structures - then the whole module can be exported to experiments at once.
I got a test object in my room and everything that I could - I started shoving it:
test.add();
And then everything is simple. Inside this test there are several arrays in which a bunch of callbacks and modules are collected. At the exit there is another method:
test.start();
At this point, the test starts all that gathered in the arrays. And this is all checked in try / catch constructs. If someone is dying, a notification pops up in the console and the next victim is taken from the array.
If we check functions, then besides a simple call, they are also called with parameters. In fact, there is a search from zero to 4 parameters. Each parameter takes a series of values ​​in order (-1, 0, 1, “string”, true, false, [], {}, etc.)

If we take a module, then it is an object. We run over the properties of the object, and if we stumble upon a function, we check it using the algorithm above. Since all my modules have the same structure, you can check a few more points. For example, find out if all DOM elements were found / created. References to them are stored in the _nodes property, which almost all modules have. For example:
var module = { _nodes: { table: DOM_element, link: DOM_element }, _methodA: function() {}, _methodB: function() {}, _methodC: function() {}, init: function() {} }
If, running over the module._nodes object, we suddenly find out null, then something has prevented the module. Probably, initialization did not work, or some elements disappeared in the DOM structure of the page. Immediately I will make a reservation that I only pull the link to the item once. The rest of the time it is stored inside the module and instead of:
$("#name").html("");
I will have:
var node = this._nodes.name; if(node) node.innerHTML = "";

Stubs and Moki
I will quote Sergey Teplyakov:
Honestly, I'm not a big fan of design changes just for the sake of code “testability”. As practice shows, a normal OO design either is already sufficiently “testable” or requires only minimal gestures to make it so. Some additional thoughts about this can be found in the article “Ideal architecture”.
The original can be viewed here
http://habrahabr.ru/post/134836/If you have never heard of them, then a stub is like a server, or some external fake object that produces different results when it is accessed. So say the object is a stub for tests. Moki is the same thing, only they still consider the statistics that they jerked and how many times.
Need to change something on the test server?- No, we just put the stub in place of the AJAX request.
Need to change something in the place of an AJAX request in the application code?- No, we can change the request function in the library without touching our code. And therefore it doesn’t matter where and how many times the function will be called - it will always pull the stub.
For example, you have the code:
$.ajax({ url: "ajax/test.html", success: function(data) { alert(data.message); } });
Instead of parsing it for tests and pulling out a callback, it's better to parse ajax. We again come from the library API, without touching our code. Yes, of course, it is not easy to disassemble jQuery or another library and “stick” our probes into it, but you can always write your own thin layer between the library and the code. This will not only allow you to push through tests without pain and make the substitution of real objects for stubs, but with an additional bonus you will be able to roll from one framework to another.
It is clear that such tests are hardcorn and can only show some obvious bugs. But this is better than nothing. In addition, they do not require any effort to support them and writing. Launched for a tick, made sure that everything seems to have worked, write on. Another plus of such autotests is that they can be taught juniors, and when they get used and stop being afraid of tests, start writing normal unit tests.
Auto documentation
Getting yourself to write documentation is difficult. Making others write documentation is almost impossible. But you can start small, make reasonable arguments and give tools that will slowly help. In my projects in i-Free, I added another solution. As mentioned above, all modules have test.add () at the end; And after the application is initialized, a certain array is obtained that stores references to all modules. You can run through this array, and since the modules are objects, make up their tree. For example:
module _methodA -> function _methodB -> function _methodC -> function init -> function
And also get information on statistics: a list of external methods, a list of internal methods, etc. In the simplest case, we just need to sign this tree:
module _methodA -> . _methodB -> . _methodC -> . init -> .
Since we also have access to the list of all assigned events, we can filter them and get lists of what the module listens and publishes. An example of how it looks in life:

According to the received logs, you can briefly compile a module description:

By such an analysis of the code, you can show Junior the moments that he should describe in the documentation and explain in the code. And viewing the structure of a module in a short form can show you and colleagues that, perhaps, some methods of the object should be swapped, because their order is somewhat not logical for understanding the process.