⬆️ ⬇️

Tests for tests

One of the most frequent answers to the question “Why do I not write unit tests?” Is the question “Who will write tests for my tests? Where is the guarantee that there won't be an error in my tests either? ”, Which indicates a serious misunderstanding of the essence of unit tests.



The purpose of this note is to briefly and clearly fix this point so that there will be no more disagreement.



So unit test is a set of several examples showing what comes to the input of your program, and what happens at the output. For example, if the program calculates the length of the hypotenuse, then in the unit test one line is enough:

assertEquals(5, hypotenuse(3, 4)); 


You should come up with these test values ​​yourself: count as a bar on a piece of paper or on a calculator.

Of course, whether this test is enough is up to you. If it is very important in your specific task that the function works correctly on very small numbers, then a separate test should be added.
 assertEquals(0.00005, hypotenuse(0.00003, 0.00004)); 
it is also important to describe the behavior of the function in the case of negative or some other parameters. In general, you decide, but the basic idea is that in a unit test there should be several important examples.


')

rules



So, the unit test differs from the program in that:

The first point is the answer to the question “Who will write the tests for my tests?”. Since a unit test is an order of magnitude simpler than a program, a test for it would have to be written an order of magnitude simpler, but this is practically unrealistic, since it is already extremely simple.



The answer to the second question “Where is the guarantee that there will be no error in my tests?” Is this: there is no guarantee and cannot be. But the fact that the “correct” answers written in the test are obtained in a different way than the answers given by the program allows you to be more confident that both ways are correct.

UPD: kalantyr tells you that unit tests and code test each other as they are interrelated. That is, in a sense, the tests for the tests are the code itself.



Example



Let's try to give here a minimal unit test example that illustrates all of the above. Suppose we need to write a function that decides whether a given year is a leap year. Let me remind you that a leap year is a year that is divisible by 4, except for those years that are divided by 100, but not divided by 400.



 public class LeapYear { public static boolean isLeap(int year) { return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; } } import org.junit.Test; import static junit.framework.Assert.*; public class LeapYearTest { @Test public void yearDividing4IsLeap() throws Exception { assertTrue(isLeap(2008)); assertTrue(isLeap(2012)); assertFalse(isLeap(2011)); } @Test public void exceptYearDividing100WhichIsNotLeap() throws Exception { assertFalse(isLeap(1900)); assertFalse(isLeap(2100)); } @Test public void exceptYearDividing400WhichIsLeap() throws Exception { assertTrue(isLeap(1600)); assertTrue(isLeap(2000)); } } 




As you can see, inside the test cases themselves are specific simple examples: 2000, 2008, 1011. I invented them myself, from the head. And the names of test cases explain them (summarize) in human language. Note that the one-to-one test method names match the description of the term “leap year” given above. So it should be: the test should be read as documentation. And this is how it looks, for example, in IDEA:

5.97 KB



Errors

Because of the lack of understanding of the essence of unit tests, when writing them, major mistakes are often made that make unit tests useless, time consuming and lead to endless discussions about whether unit tests are needed at all.



The first common mistake is to check nothing at all. For example:
 @Test public void testLeapYear() throws Exception { int year = 2004; assertEquals(2004, year); } 
This unit test is meaningless, as it does not check our program. The maximum that he checks is that Java works correctly, but this is already paranoia. Let Oracle do this.

Despite the seeming absurdity, it is from such tests that everyone who takes JUnit in their hands for the first time begins.



The second typical mistake is to try to repeat in the test the same logic that is in the program. For example:
 @Test public void testLeapYear() throws Exception { for (int i=0; i<100000; i++) { assertEquals(i % 4 == 0 && i % 100 != 0 || i % 400 == 0, isLeap(i)); } } 
This unit test is bad just because a programmer can make the same mistakes as in the code itself.



Smells



Signs that a unit test is wrong:





Open source



It is always useful to study the unit tests of well-known open-source projects. This topic is worth a separate article, but the first thing that came to mind was Apache Velocity and the Spring Framework . I didn’t like the test from the first UnicodeInputStreamTestCase project, since the names of the test methods do not really describe the program's behavior (for example, “testSimpleStream ()”). But I liked the test for Spring, for example, for example, for the CollectionUtils class there is the CollectionUtilsTests unit test, and for the Assert class there are AssertTests unit tests.



I hope this article will stop the eternal debate that unit tests are useless, time consuming, etc., and can serve as a starting point for further discussions about how to write, how much to write, what to write and so on. And someone, perhaps, will even have to break away from Habr and read serious books, such as " Test Driven Development (Kent Beck) ", " xUnit Test Patterns (Gerard Meszaros) " and my favorite " Clean Code (Robert C. Martin ) ".



To test or not to test - that is not a question ...



Source: https://habr.com/ru/post/112851/



All Articles