7 rules of good tone when writing Unit tests


    “One
    who has the least number of people
    in an awkward position has good manners .”
    J. Swift


    Hello colleagues! Today I would like to talk about Unit testing and some “rules” when writing them. Of course, they are informal and are not required to be performed, but if they are followed, it will be pleasant and easy for everyone to read and support the tests that you wrote. We at Wrike saw enough Unit tests to understand the main problems that arise when they are written and supported, and to formulate a few rules to prevent them.

    1. Unit tests need to be written. Yes, no matter how trite it may sound, but you need to write them. Every piece of application logic needs to be tested to avoid problems in the future. And they can occur when changing logic, refactoring, or even when updating the version of dependent libraries. And the more coverage of the code with tests, the faster the problem will be detected and fixed.

    2. This rule is very relevant for those who are forced to cover the code with tests, and it sounds like this: Tests are also code, and you need to treat it like a working code. This also applies to naming variables, and formatting code inside a test, and, especially, the names of test methods. Of course, writing an adequate variable name takes a little more time and typing than “int i = 0;”, but this increases the readability of the tests and the ease of their support.
    Guess what the fallen test method is testing?)
    image

    3. The third rule

    of a real gentleman - follow the paths . Of course, it’s always unpleasant to see the following error when starting the tests:

    And not even because your name is not Andrey, but because you have a poppy. And what about this situation, you ask? The answer is simple - Relative paths. Here is an example -

    It is best to use a Unix delimiter (/). This is much more concise, and less likely to get an unexpected error.

    4. More often use stubs (moki) instead of real objects.Moki is great! They can be controlled as needed in a particular test. But, of course, do not forget to reset the state of the stubs before each test method. The use of stubs increases the autonomy of the test and its flexibility. You do not need to adjust the state of the system for a specific case, but simply configured the stub to return the desired value when calling a specific method and that’s all. I want to check another situation - I fixed the return value to another. Simply and easily. And most importantly, the state of the entire system does not change at the same time - it does not write anything to disk, does not transfer over the network, does not recalculate data arrays, does not crawl into other services. Just a stub and return value.
    To use stubs in tests, I use the Mockito framework. Using it is easy to create stubs. Here's an example:

    Here the mock of the calendar object is created and passed to the calendarService object . Next, moki are initialized in the setUp method . Then, directly inside the test, the mock is configured and the test checks isModern if the calendar type is different or not set at all. There was no need to recreate the CalendarService , and creating mocks and generating return values ​​took only a few lines.

    5. Write meaningful messages in case the test crashes . The most common message I saw while parsing fallen tests on TeamCity is

    Well, immediately everything is clear! But it happens that there is still an error message, but there is some benefit from it ...

    But now there is a good, but not yet perfect, message that immediately describes what was checked. You

    can consider a message as ideal, which not only shows what we are checking, but and why do we expect it

    ? But here a fairly simple check is shown, and if you need to compare a pair of arrays with data in a different order? What message should I write here? For this, I recommend using the AssertJ framework. The link has many simple and understandable examples of use, after which you will want to use this framework! AssertJ allows you to think less about writing a message in case of an error, and also check a bunch of just one line, saving space. For example check:

    It will give us a wonderful error message:

    And everything is clear - what happened and for what reason! You can go and fix the mistakes.
    And again - Adequate messages in test errors save time and nerves of those who will parse these tests. Perhaps it will be you yourself in a year.

    6. Take out the trash (no, it's not about starting GarbageCollector-a). Did you create a file, write to the database, or pull the user creation handle? Do not be lazy and clean after yourself after the test. Files are accumulating, the base is overgrown with a bunch of garbage, and crowds of fake users appear in the system. Try to keep clean not only your workplace, but also your work environment. UPDAs correctly pointed out in the comments, this item applies only to integration testing.

    7. Verify that the test runs elsewhere besides your local machine . If you have a CI server or some other place where you run the tests, check that the test started there too. For example, tests on the CI server run from a specific package, and you put yours in another package. Or tests run by a specific name, for example * UTest , and you named your class TestUid . Or tests are run in groups, and you forgot to put down a specific group for your test. Or ... You can come up with many cases where a freshly written test so nirazu and will not start somewhere other than your local machine. And then the benefits of it are not so much!

    The result is a short list of good practice rules when writing Unit tests. I hope this article will help to raise a little more the culture of tests and testing in your team. If you have anything to add to my list, welcome to comment.


    Also popular now: