JavaRush /Java Blog /Random EN /JUnit for JavaRush or a bit about testing at home.
Sdu
Level 17

JUnit for JavaRush or a bit about testing at home.

Published in the Random EN group
Tired of typing test data into the console dozens of times to check your task? Welcome under the cut, I'll tell you what you can do with it. The ultimate goal of this material will be to automate the launch of the problem being solved with various parameters and checking the results without making changes to its source code. As you probably already understood from the title, our main assistant in this fairly simple matter will be JUnit . If you haven't heard of unit testing and unit tests yet , I suggest that you digress a little and get acquainted with these concepts on your own, since there is enough information on the Internet. No, don't you? Well, okay, I think this will not be a big problem for understanding what is happening. After all, you know what a test and testing is in general? You do this every time you run your task, enter the initial data and compare the result with what you expected to see.
Hello world JUnit!
What is JUnit? On the official website of the project, we can read the following description:
JUnit is a simple framework to write repeatable tests. It is an instance of the xUnit architecture for unit testing frameworks.
For us, this means the ability to write specially designed classes, the methods of which will interact with our program, check the result with the reference one, and inform us if they do not match. To understand the principle, consider a simple example. Suppose we have an auxiliary class, one of whose methods accepts two variables of the int type and returns their sum: JUnit for CodeGym or a bit about testing at home.  - 1 This is the functionality we will try to test. Fortunately, our favorite IDEA already has everything you need to quickly create tests, all we need is to place the cursor on the class declaration line, press "Alt + Enter" and select "Create Test" in the context menu: JUnit for CodeGym or a bit about testing at home.  - 2 After you specify where it is worth creating a test, IDEA suggests choosing a testing library (in this material I use JUnit4, in order for the library classes to be connected to the project, you must click the "Fix" button), test methods and additional options. JUnit for CodeGym or a bit about testing at home.  - 3 The IDE will create a test class template: ClassName = TestedClassName + "Test" MethodName = "test" + TestedMethodName JUnit for CodeGym or a bit about testing at home.  - 4 We just have to fill in the body of the method. The so-called "Assertions \ Assertions" will not help with this. , methods provided by JUnit. Simplified, their work is as follows: the expected result and the result of calling the method under test are passed to the .assert* method, for convenience, an explanatory message can be added as the first parameter. If the parameters do not match during the test, you will be informed about it. You can run a test class for execution like a regular class, I prefer to use the key combination Ctrl + Shift + F10 JUnit for CodeGym or a bit about testing at home.  - 5
We specify the task
In theory, everything is simple and beautiful, but in the context of the proposed example, it’s not really necessary, we can trust the computer to add two numbers. We are more interested in how things will be with real tasks solved by CodeGym students, for an example, I suggest taking my beloved level05.lesson12.bonus03.
/* Algorithm task Write a program that: 1. enters the number N > 0 from the console 2. then enters N numbers from the console 3. displays the maximum of the entered N numbers. */
We need to write three tests, for positive numbers, negative numbers, and a mixed set.
The further into the forest...
Here we are in for some surprises: public class UtilApp { public static void main(String[] args) throws Exception { BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); //напишите здесь ваш code int n; int maximum; /* Конечно же я не буду размещать решение задачи ;) Код приведенный тут переработан для наглядности, и не в коем случае не означает что он должен присутствовать в "правильном решении" */ System.out.println(maximum); } }
  • The program logic is placed in the main() method
  • The initial data is not passed to the method, but entered from the keyboard.
  • The main() method does not return the result, but prints it to the console.
If the first point is not particularly problematic (we can call the main () method as usual), then the next two make you delve into the topic and strain your convolutions. I found several solutions to the problem:
  1. Moving the logic of finding the maximum into a separate method.
    • Pros: Correct approach in terms of refactoring
    • Cons: The program is overgrown with code, extra structures, at least an array or ArrayList is added (to taste and color ...). Only the mechanism for finding the maximum is tested, the input data, as well as the output, are not checked.
  2. Writing wrappers for System.in/System.out.
    • Pros: We do not use third-party libraries.
    • Cons: The path is not for beginners. The relative complexity of the test implementation, the amount of code in the test may be greater than in the task under test.
  3. Using additional libraries for tests.
    • Pros: Code cleanliness in tests, relative ease of writing a test. The source code of the class under test is not changed.
    • Cons: The need to connect third-party libraries to the project.
To be honest, I liked the third option the most, so let's try to implement it.
System Rules
A short search led me to the page http://stefanbirkner.github.io/system-rules/ , and it immediately became clear what it was that I needed.
A collection of JUnit rules for testing code that uses java.lang.System.
So let's download the library . Download the Commons IO library necessary for system rules to work . We connect both libraries to our project (File -> Project Structure -> Libraries -> + -> Java) and start sculpting: After starting, our task asks to enter N + 1 numbers from the console, where the first number tells how many numbers will follow him. In System Rules, the TextFromStandardInputStream class serves for these purposes, initially we need to add a field of this type to our test class and mark it with the @Rule annotation: @Rule public final TextFromStandardInputStream systemInMock = emptyStandardInputStream(); Then, directly in the test method, specify the necessary data: systemInMock.provideText("4\n2\n6\n1\n3\n"); As you can see, the numbers are transmitted in text form and are separated by a line break character "\n". Based on this, it turns out that N will be equal to 4, and we will look for the maximum from the numbers {2, 6, 1, 3}. Next, we need to create an instance of the class under test and call the main() method. Our program reads the data from systemInMock, processes it and prints the result, and all we have to do is read it and compare it with the standard. For this, system rules provides us with the StandardOutputStreamLog class. Add a field of the specified type: @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog(); You can read the printed data using the .getLog() method, while taking into account the presence of newlines, the final options can be as follows: assertEquals("{2, 6, 1, 3}, max = 6", "6", log.getLog().trim()); // or assertEquals("{2, 6, 1, 3}, max = 6", "6\r\n", log.getLog()); Between tests, in order to avoid data overlap, it is necessary to clear the log log.clear(); Full text of my test class: import org.junit.Rule; import org.junit.Test; import org.junit.contrib.java.lang.system.StandardOutputStreamLog; import org.junit.contrib.java.lang.system.TextFromStandardInputStream; import static org.junit.Assert.*; import static org.junit.contrib.java.lang.system.TextFromStandardInputStream.emptyStandardInputStream; public class UtilAppTest { @Rule public final TextFromStandardInputStream systemInMock = emptyStandardInputStream(); @Rule public final StandardOutputStreamLog log = new StandardOutputStreamLog(); @Test public void testAddition() throws Exception { systemInMock.provideText("4\n2\n6\n1\n3\n"); UtilApp utilApp = new UtilApp(); utilApp.main(new String[]{}); assertEquals("{2, 6, 1, 3}, max = 6", "6", log.getLog().trim()); systemInMock.provideText("5\n-100\n-6\n-15\n-183\n-1\n"); log.clear(); utilApp.main(new String[]{}); assertEquals("{-100, -6, -15, -183, -1}, max = -1", "-1", log.getLog().trim()); systemInMock.provideText("3\n2\n0\n-1\n"); log.clear(); utilApp.main(new String[]{}); assertEquals("{2, 0, -1}, max = 2", "2", log.getLog().trim()); } } We launch and enjoy. -=!!! IMPORTANT !!!=- This material is provided for informational purposes ONLY, I do not guarantee successful completion of task testing on the server in case there is an extraneous class in the task package. Before sending a task to the server for verification, remove everything extraneous: extra files, extra classes, commented out code. Successful passing of the tests invented by you does not guarantee successful passing of tests on the server. I deliberately did not chew on the theoretical material: the theory of unit testing, JUnit annotations, assert, and so on, all the material is in the links provided in the text. Perhaps you have your own ways of testing tasks, I will be happy to discuss them with you in the comments.
Comments
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION