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

JUnit for JavaRush or a little 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 to the cat, I’ll tell you what you can do with it. The ultimate goal of this material will be to automate the launch of the task 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 rather simple matter will be JUnit . If you have not yet heard about unit testing and unit tests , I suggest you take a little break and familiarize yourself with these concepts, fortunately there is enough information on the Internet. No, you don't want to? Well, okay, I think this will not become a big problem for understanding what is happening. After all, you know what a test and testing in general are? You do this every time you launch your task, enter the initial data and compare the resulting result with what you expected to see.
Hello, world JUnit!
What is JUnit? On the project’s official website 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 whose methods will interact with our program, compare the resulting 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 takes two variables of type int and returns their sum: JUnit for JavaRush or a little 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 in the class declaration line, press "Alt + Enter" and select "Create Test" in the context menu: JUnit for JavaRush or a little about testing at home.  - 2 After you specify where you should create 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 need to click the “Fix” button), methods to be tested and additional options. JUnit for JavaRush or a little about testing at home.  - 3 The IDE will create a test class template: ClassName = TestClassName + "Test" MethodName = "test" + TestMethodName JUnit for JavaRush or a little about testing at home.  - 4 We just need to fill in the method body. The so-called “Assertions” , methods provided by JUnit , will help with this . In a simplified way, their work looks like this: the expected result and the result of calling the method under test are passed to the .assert* method; for convenience, you can add an explanatory message as the first parameter. If the parameters do not match during the test, you will be informed about this. You can launch a test class for execution just like a regular class, I prefer to use the key combination Ctrl+Shift+F10 JUnit for JavaRush or a little about testing at home.  - 5
Let's 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 go with real problems solved by JavaRush students; for example, I suggest taking the beloved level05.lesson12.bonus03.
/* Problem on algorithms 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, negative numbers and a mixed set.
The further into the forest...
This is where some surprises await us: 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 source data is not passed to the method, but is entered from the keyboard.
  • The main() method does not return the result, but outputs it to the console.
If the first point is not particularly problematic (we can call the main() method as usual), then the next two force us to go deeper into the topic and strain our brains. I found several solutions to the problem:
  1. Moving the logic for finding the maximum into a separate method.
    • Pros: Correct approach in terms of refactoring
    • Cons: The program becomes overgrown with code, unnecessary structures, at least an array or ArrayList is added (depending on taste and color...). Only the mechanism for finding the maximum is tested; data input and 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 being tested.
  3. Using additional libraries for tests.
    • Pros: Clean code in tests, relative ease of writing a test. The source code of the class under test is not changed.
    • Cons: 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 that this was what 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 required for system rules to work . We connect both libraries to our project (File -> Project Structure -> Libraries -> + -> Java) and start sculpting: After launch, our task asks you to enter N+1 numbers from the console, where the first number tells you how many numbers will follow him. In System Rules, the TextFromStandardInputStream class is used 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 we indicate 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 hyphen strings "\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. To do this, system rules provides us with the StandardOutputStreamLog class. We 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 you need to take into account the presence of newline characters, the final options can be like this: 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 layering, you need to clear log log.clear(); The 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 testing of the task on the server if there is an extraneous class in the package with the task. Before sending a task for verification to the server, remove everything extraneous: unnecessary files, unnecessary classes, commented-out code. Successful completion of the tests you have created does not guarantee successful completion of the tests on the server. I deliberately did not chew on the theoretical material: the theory of unit testing, JUnit annotations, assert, etc., 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