[Java] [For newcomers] Introduction to JUnit

17 minute read

I wanted to introduce JUnit for newcomer education for a long time, but it was a disappointing article because I had a little volume when I was writing a draft. It’s a waste to throw it away as it is, so the company has a summer vacation and is for newcomers.


What is #JUnit JUnit is a tool commonly used in what is called unit testing. JUnit is software that allows you to execute a method at any time by creating a class with a method that describes how to use and verify it. When executing JUnit, write classes and methods according to the JUnit style.

JUnit flow

JUnit flow (overview)

The basic flow of JUnit is

  • Based on “how to use that function”
  • Verify “results of using that function”

Will be.

JUnit flow (simple sample)

For example, suppose you have a shopping cart class (Cart). Let’s say that there is a method called getSum() that gets the total price that reflects the price and quantity of all the items in the shopping cart. The usage is like this.

Cart cart = new Cart();
cart.add(items, quantity);

int result = cart.getSum(); // Get the current total amount in cart
assertEquals(expected amount, result); // Check if the obtained amount is the expected amount

**assertEquals is the validation part. ** **If the arguments are different here, the test process ends there. ** **The test will also be failed. If you pass assertEquals and go to the end, the test will be successful. **

You can write multiple assertEquals. For example, if you set multiple parameters in the constructor test and check if they are set correctly, the following will occur.

Product apple = new Product("apple", 400); // The variable apple is set to the product name apple and 400 yen

assertEquals("apple", apple.getName()); // Check if the name is set correctly
assertEquals(400, apple.getPrice()); // Check if the price was set correctly

I feel like The test passes if you pass two assertEquals.

Testing with JUnit

Class to be tested

The class below is the class to be tested.

public class Product {
    private String productId; // product number
    private String productName; // product name
    private Integer price; // price

    // getter
    public String getProductId() {return productId;}
    public String getProductName() {return productName;}
    public Integer getPrice() {return price;}
    
    // constructor
    public Product(String product number, String product name, Integer price) {/* Set the above properties */}

}
public class Cart {
  // property
  private List<Product> list; // Products and their quantity

  // method
  /**
   * Perform processing to add products
   */
  public void add(Product product) {/* abbreviation */}

  /**
   * A process to return the contained products and their quantity
   */
  public List<Product> getList() {/* abbreviation */}
  /**
   * Processing to return the total of the contained products
   */
  public int getSum() {/* abbreviation */}
}
By the way, the whole picture is as follows.
```java:Product.java public class Product { private String productId; // product number private String productName; // product name private Integer price; // price public Product(String productId, String productName, Integer price) { this.productId = productId; this.productName = productName; this.price = price; } public String getProductId() { return productId; } public String getProductName() { return productName; } public Integer getPrice() { return price; } } ``` ```java:Cart.java import java.util.List; import java.util.ArrayList; public class Cart { // property private List list; // Products and their quantity public Cart() { this.list = new ArrayList(); } // method /** * Perform processing to add products */ public void add(Product product) { this.list.add(product); } /** * A process to return the contained products and their quantity */ public List getList() { return this.list; } /** * Processing to return the total of the contained products */ public int getSum() { int sum = 0; for (Product p: this.list) { sum += p.getPrice(); } return sum; } } ``` </div></details> ## Test method (Overview) I want to test add here. In that case, I considered the following flow How to use 0. Create products to add to cart 1. Create a cart 2. Add item to cart Using the above usage, the validation of whether add is correct was thought as follows (** Note: This is an example. You do not have to do the same test as below ** ). 0. Get the list from the cart (this is a preliminary test and will not go into the test) 1. Check if the number of items in the cart is the same as the number added 2. Make sure that the items in your cart are correct ## Test method (pseudo code) The test code above would be something like ```java // 0. Create products to add to cart Product apple = new Product("A001", "apple", 158); Product orange = new Product("A002", "orange", 258); // 1. Create a cart Cart cart = new Cart(); // Add products (apple and orange) to the cart cart.add(apple); cart.add(orange); // verify from here // 0. Get list from cart List list = cart.getList(); // Get the list in the cart // 1. Check if the number of items in the cart is the same as the added number (2) assertEquals(2, list.size()); // 2. Make sure that the items in your cart are correct Product item; // 2.1 Check if the first product is the same as defined in apple item= list.get(0); // fetch the first item in the cart assertEquals("A001", item.getProductId()); // Verification of the product ID of the retrieved product assertEquals("apple", item.getProductName()); // Validate the product name of the retrieved product assertEquals((Integer)158, item.getPrice()); // Validate the product name of the retrieved product // 2.1 Check if the second product is the same as the one defined in orange item = list.get(1); // retrieve the second item in the cart and start verification assertEquals("A002", item.getProductId()); // Verification of the product ID of the retrieved product assertEquals("Orange", item.getProductName()); // Validate the product name of the retrieved product assertEquals((Integer)258, item.getPrice()); // Validate the product name of the retrieved product ``` By the way, the part written with `"A001"` etc. can be used as `apple.getProductName()`. ## Testing method (pseudo code supplement) As I wrote at the beginning, the flow of JUnit is -Based on "how to use that function" -Verify "results of using that function" Is the basics. The validation is validated with methods that start with `assert`, such as `assertEquals`. And **assertEquals will be a test failure there if both arguments are not the same** and no further processing is done**. For example `assertEquals("apple", item.getProductName());` If the `"apple"` in "" is changed to ""apple"", it will be a failure. `assertEquals((Integer)158, item.getPrice());` Will not be implemented. If all assertEquals are passed and the process proceeds to the end, it will be successful. In addition, write *`assertXXX(expect, actual)` in the order of correct value and value to be tested*. ## JUnit test method (Eclipse) Although it is an Eclipse edition, the original author is [pleiades](https://mergedoc.osdn.jp).By the way, I was exhausted just by doing this. If I am willing, I will add VS Code etc. one day. To do JUnit in Eclipse, do the following:
Create the test folder under the project folder if it doesn't exist.
Select [File] menu -> [New] -> [Source Folder]. Select File menu -> New -> Source Folder Enter "test" in "Folder name:" on the next screen and click "Finish". <img width="591" alt="Enter "test" in "Folder name:" on the screen that appears and click "Finish"" src="https://qiita-image-store.s3. ap-northeast-1.amazonaws.com/0/166866/94e57934-6f15-5343-5504-0e40551a75b4.png">
Right-click the test folder you just checked or created and select New → Other. <img width="638" alt="test Right-click the folder and select "New" → "Other"" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws. com/0/166866/32695e87-0da8-06a7-a5fd-26e8b0c1d1d5.png"> A new screen will be displayed. Select [Java] -> [JUnit] -> [JUnit Test Case] and select [Next]. Alternatively, type "junit" in the wizard field at the top of the new screen. Select JUnit Test Case from the remaining ones and select Next. <img width="591" alt="The wizard screen is displayed. Enter "junit". Select JUnit test case "src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166866/c2bb3885-75e0-68b5-df57-19ff7542254c.png"> Next depends on JUnit3, 4 or 5. If you are new to education and have nothing to do with it, use 4 or 5. Select 3 if you are in a disappointing situation where you will be assigned to 3. Unfortunately, 5 will be included in 4. I didn't have enough time, so... ## Create a test case for JUnit3
Create a JUnit3 test case
1. Select New JUnit 3 Test 2. Enter the name of the class that writes the test case in the name (redline part). "Test object Test" seems to be good. 3. For the package name (blue line part), specify the same package as the target of the test class (the "package XXX;" part written at the beginning of the Java program to be tested). 4. Click Finish. Enter the package name and the name of the class you want to write the name in. Enter it and click Finish Initially there is no library for JUnit3, so you will be asked if you want to add it, so select "Perform next action", select "Add JUnit 3 library to build path" and press OK. <img width="571" alt="Select "Perform the following action", select "Add JUnit 3 libraries to build path" and press OK" src="https://qiita -image-store.s3.ap-northeast-1.amazonaws.com/0/166866/a6c630a1-a4c3-48df-9577-44329d58d619.png">
The entity of the created JUnit test case is just a Java program file. This time, describe as follows.
```java:CartTest.java package myStore; import junit.framework.TestCase; import java.util.List; public class CartTest extends TestCase { public void test Confirm if it was added to the cart () { // Make the products to be input for testing first Product apple = new Product("A001", "apple", 158); Product orange = new Product("A002", "orange", 258); // create cart Cart cart = new Cart(); // add apples and oranges cart.add(apple); cart.add(orange); It's a sequel. // verify from here List list = cart.getList(); // Get the list in the cart It's a sequel. // Verify if there are 2 items in the cart assertEquals(2, list.size()); It's a sequel. // Check if the cart contents are correct Product item; item= list.get(0); // fetch the first item in the cart and start verification assertEquals("Verify if the first product ID in the cart is A001", "A001", item.getProductId()); assertEquals("Verify if the first product name in the cart is an apple", "apple", item.getProductName()); assertEquals("Verify if the first cart price is 158", (Integer)158, item.getPrice()); It's a sequel. item = list.get(1); // retrieve the second item in the cart and start verification assertEquals("Verify if the first product ID in the cart is A002", "A002", item.getProductId()); assertEquals("Verify if the first product name in the cart is orange", "orange", item.getProductName()); assertEquals("Validate if the first cart price is 258", (Integer)258, item.getPrice()); } } ``` </div> </details> The promise of JUnit3 is as follows. - write `import junit.framework.TestCase;` - Test class inherits TestCase class with extends TestCase - Test method modifiers should start with `public void` and name with `test` Create according to the above rules. You can create as many test methods as you want by starting from `test`, so please create tests of various patterns. Also, Japanese is included in the method name, but this is not a problem for Java (specifically no problem). In JUnit, Japanese is often used so that you can easily understand what test you are doing [^1]. This is easier to understand than writing `testNo1`. You'll also notice that the first argument to assertEquals is a string, but think of it as a comment. If you leave it out, when you test with JUnit, this first argument will be displayed as a message and you can see what the intended test failed. Even if you say [^1]:, it will be an error if there is a space, so let's choose a word that the test can express in one word </div></details> ## Create a test case for JUnit4
Create a JUnit4 test case
Create a JUnit4 test case
1. Select New JUnit 4 Test 2. In Name, enter the name of the class that writes the test case (redline part). "Test object Test" seems to be good. 3. For the package name (blue line part), specify the same package as the target of the test class (the part of "package XXX;" written at the beginning of the Java program to be tested). 4. Click Finish. <img width="597" alt="Enter the package name and the name of the class to write the name, and then click "Finish"" src="https://qiita-image-store.s3.ap -northeast-1.amazonaws.com/0/166866/c54f2c43-944d-a439-f5f2-e70704932e11.png"> There is no library for JUnit4 at first, so you will be asked if you want to add it. Select "Perform next action", select "Add JUnit 4 library to build path", and press OK. <img width="579" alt="Select "Perform the following action", select "Add JUnit 4 libraries to build path" and press OK" src="https://qiita -image-store.s3.ap-northeast-1.amazonaws.com/0/166866/d1cc54cd-ec70-e0b0-cd44-afeb7ff932d9.png">
The entity of the created JUnit test case is just a Java program file. This time, describe as follows.
```java:CartTest2.java package myStore; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import java.util.List; import org.junit.Test; public class CartTest { @Test public void Check if it was added to the cart () { // Make the products you need to test in advanceProduct apple = new Product("A001", "apple", 158); Product orange = new Product("A002", "orange", 258); // create cart Cart cart = new Cart(); // add apples and oranges cart.add(apple); cart.add(orange); // verify from here List list = cart.getList(); // Get the list in the cart // Verify if there are 2 items in the cart assertEquals(2, list.size()); // Check if the cart contents are correct Product item; item= list.get(0); // fetch the first item in the cart assertThat(item.getProductId(), is("A001")); // Verification of the product ID of the retrieved product assertThat(item.getProductName(), is("apple")); // Verify the product name of the retrieved product assertThat(item.getPrice(), is(158)); // Verification of the product name of the retrieved product item = list.get(1); // retrieve the second item in the cart and start verification assertThat(item.getProductId(), is("A002")); // Verification of the product ID of the retrieved product assertThat(item.getProductName(), is("Orange")); // Verify the product name of the retrieved product assertThat(item.getPrice(), is(258)); // Verify the product name of the retrieved product } } ``` </div></details> The promise of JUnit4 is as follows. - Write the following first -`import static org.hamcrest.CoreMatchers.*;` -`import static org.junit.Assert.*;` -`import org.junit.Test;` - Test method modifier is `public void` - Write `@Test` before the test method - Verification should be written as `assertThat( verification target, is( desired value) )` Create according to the above rules. You can create any number of test methods if you start by adding `@Test`, so please create tests of various patterns. Also, Japanese is included in the method name, but this is not a problem for Java (specifically no problem). In JUnit, Japanese is often used so that you can easily understand what test you are doing [^1]. This is easier to understand than writing `testNo1`. You'll also notice that the first argument to assertEquals is a string, but think of it as a comment. If you leave this out, when you test with JUnit, this first argument will be displayed as a message, telling you what your intended test failed.
Although you can write code similar to JUnit 3
```java:CartTest.java package myStore; import static org.junit.Assert.*; import org.junit.Test; import java.util.List; public class CartTest { @Test public void Check if it was added to the cart () { // Make the products you need to test in advance Product apple = new Product("A001", "apple", 158); Product orange = new Product("A002", "orange", 258); // create cart Cart cart = new Cart(); // add apples and oranges cart.add(apple); cart.add(orange); // verify from here List list = cart.getList(); // Get the list in the cart // Verify if there are 2 items in the cart assertEquals(2, list.size()); // Check if the cart contents are correct Product item; item= list.get(0); // fetch the first item in the cart and start verification assertEquals("Verify if the first product ID in the cart is A001", "A001", item.getProductId()); assertEquals("Verify if the first product name in the cart is an apple", "apple", item.getProductName()); assertEquals("Validate if the first cart price is 158", (Integer)158, item.getPrice()); item = list.get(1); // retrieve the second item in the cart and start verification assertEquals("Verify if the second product ID in the cart is A002", "A002", item.getProductId()); assertEquals("Verify that the second product name in the cart is orange", "orange", item.getProductName()); assertEquals("Valid second cart price is 258", (Integer)258, item.getPrice()); } } ``` </div> </details> ## About writing JUnit 4 Since I used `assertEquals` in the early stage, here is a supplementary lecture. With JUnit4, you can now write as follows. From now on, this will be the main. `assertThat( target, is("target") );` It's almost the same as doing `assertEquals`, but the advantage is that you can read from left to right. It feels like assert that target is "target" (verify that target is "target"). You can also write like this. `is` and `not` are called Matchers. `assertThat( target, is( not( "not target")) )` reference: [Java Master-Getting Started with JUnit Basic Usage](https://carey.link/java/junit-use) [How to use JUnit (Beginner)](https://qiita.com/YutaSaito1991/items/46707049d0a26086b443) </div> </details> ## JUnit execution Execute the created test case. Choose Run menu> Run> JUnit Test. Screenshot 2020-08-10 11.27.02.png Then, the following screen will appear somewhere on the screen.
If it does not appear, select [Window] menu -> [Show View] -> [Other] and select [Java] -> [JUnit](redline).
Select Window menu -> Show View -> Other <img width="324" alt=""Java"→"JUnit" (redline part) is selected" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/ 0/166866/319a63b4-9db8-90c1-bd2d-7c5d6ea7ef81.png">
JUnit execution screen Now click on the triangle to the left of myStore.CartTest. You can see what test was done as below. Successful tests are displayed in green and failed tests are displayed in red. Show tests run in class By the way, let's change "A001" in the 28th line of the test program to "A003" and try again. You can execute it by right-clicking in the same way as before, but if you have a JUnit view, you can re-execute it by pressing "Rerun test" (redline part). <img width="511" alt="It can be re-executed by clicking "Re-execute test" (redline part)" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws .com/0/166866/05f56fda-b837-c6e4-7a1d-8d853a6c0114.png"> Since the file has been changed by changing "A001" to "A003", a dialog appears asking if you want to save it. If there is no problem, click "OK". If you remove the check next to the file name, the program in the state before saving the file will be executed. <img width="290" alt="Save and press "OK" when the startup dialog appears. "src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166866/9614a91d-6e0e-f3d8-2ec9-7f6869181576.png"> Then, an ultramarine cross will be attached to the bottom left of the icon. This is a sign of failure.Then click on the failed test to see where in the test case it failed. If you double-click this, the target line will be displayed. Click on a failed test to see where it failed in the test case. Double-click it to see the line of interest. Also, if you look at the back side of `junit.framework.ComparisionFailure:` in the fault trace, you can see the description "There is confirmation whether the first product ID of the cart is A001" in the error details. .. In addition, the correct value is displayed in "Expected: is" and the actual value (execution result) is displayed in "but: was". You can confirm that there is a confirmation that the first product ID of the cart is A001 in the error details Well, it's a long story, but that's it. - --- ## What is a column test? What is quality? What to inspect
It's just poem
I wrote that JUnit is important for execution and verification. Of these, I would like to touch on verification. It's super complicated, so you can skip it. Validation is to make sure that the value of the result of the run is the same as the expected result, but there is controversy about what to validate. There may be a definite answer, but I don't know (although I know around @t_wada...). For example, if you have the following class: ```java:SomeClass.java class SomeClass { private String somethingvalue = ""; public void setSomethingValue(String somethingvalue) { this.somethingvalue = somethingvalue; } } ``` This class has only the ability to hold data as it is a future extension. There is a `setSomethingValue` that sets the data `somethingvalue` to hold. However, there is no `getSomethingValue` to get the value of `value`. What should we do with this `setSomethingValue` test? The private answer is "Based on the project guidelines and specifications". As mentioned earlier, the value of value can be known using the Java reflection function, even if it is a private variable (property). [^2] Then about the project and specifications - I think there is a high possibility that it will be a test target if "Set to value" is written in the project or specification - If it is a policy that "do not check private variables" "check with properties and methods exposed by the class", it will not be tested (because there is no way other than reflection to access value) I would like to adopt the latter perspective, but in this case there is a bit of a complication. If you add a method that repeatedly displays the following values a specified number of times ```java public String repeatSpeak(int times) { String result = ""; for (int i=0; i <times; i++) { result += this.value; } return result; } ``` This `repeatSpeak` is a public (= presupposed to be used by somebody) method, so it will be tested. At this time, the value changes depending on the `value`. So you will also write a test that uses `setValue` to change the `value`. I think it is possible to test indirectly, not directly like this. Based on the viewpoints such as "what", "how", and "who", it is necessary to have a unified viewpoint ** in the project and to test it in team development. Ask if you want to test. If not, let team members share a unified perspective at the beginning of development. [^2]: Refer to the following for reflection: [Samurai blog-[Introduction to Java] Method execution and field change by reflection](https://www.sejuku.net/blog/33252),[Hishida'sTechMemoPage-Reflection](https://www.ne.jp/asahi/hishidama/home/tech/java/reflection.html),