[For newcomers] Introducing JUnit

This is a disappointing article that I had wanted to introduce JUnit for newcomer education for a long time, but I gave up because it became a little volume when I was writing a draft. It's a waste to throw it away as it is, so the company is also on summer vacation, and for newcomers.


What is JUnit

JUnit is a tool commonly used in what is commonly referred to as 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

Flow of JUnit (Introduction)

The basic flow of JUnit is

--Execute based on "How to use the function" --Verify "results of using that function"

Will be.

JUnit flow (simple sample)

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

Cart cart = new Cart();
cart.add(Product,Quantity);

int result = cart.getSum(); //You can get the current total amount in the cart
assertEquals(Amount that should have been, result); //Check if the acquired amount is the expected amount

** ʻassert Equalsis part of the validation. ** ** ** If the arguments are different here, the test process will end there. ** ** ** The test will also befailed. After passing ʻassertEquals and going to the end, the test will be successful. ** **

You can write multiple ʻassert Equals`. For example, if you want to set multiple parameters in the constructor test and check if they are set correctly, it will be as follows.

Product apple = new Product("Apple", 400); // appleという変数にAppleという商品名と400円を設定している

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

It's like that. The test is successful if you pass the two ʻassert Equals`.

Testing with JUnit

Class to be tested

The ** classes below are the classes to be tested **.

Product.java


public class Product {
    private String productId; //Item 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 Item No.,String Product name,Integer price) { /*Set the above properties*/ }

}

Cart.Java


public class Cart {
  //Property
  private List<Product> list; //Products included and their number

  //Method
  /** 
   *Perform the process of adding products
   */
  public void add(Product Product) { /*Abbreviation*/ }

  /**
   *Processing 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.

Product.java


public class Product {
	private String productId; //Item 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;
	}
}

Cart.java


import java.util.List;
import java.util.ArrayList;

public class Cart {
	//Property
	private List<Product> list; //Products included and their number

	public Cart() {
		this.list = new ArrayList<Product>();
	}
	//Method
	/**
	*Perform the process of adding products
	*/
	public void add(Product product) {
		this.list.add(product);
	}

	/**
	 *Processing to return the contained products and their quantity
	 */
	public List<Product> 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;
	}
}

Test method (overview)

Here I would like to test add. In that case, I thought about the following flow

How to use 0. Create a product to add to the cart

  1. Create a cart
  2. Add items to the cart

To verify that add is correct with the above usage, I thought (** Note: This is just an example. You don't necessarily have to do the following with similar tests **) 0. Remove the list from the cart (this is a preparatory step and will not be tested)

  1. Check if the number of items in the cart is the same as the number added
  2. Check in order whether the items in the cart are correct

Test method (pseudo code)

The test code above would look like this

// 0.Create a product to add to the cart
Product apple = new Product("A001", "Apple", 158);
Product orange = new Product("A002", "Orange", 258);

// 1.Create a cart
Cart cart = new Cart();
//Items in the cart(Apples and oranges)To add
cart.add(apple);
cart.add(orange);

//Verify from here
// 0.Remove the list from the cart
List<Product> list = cart.getList(); //Get the list in the cart

// 1.The number of items in the list added to the cart(2 pieces)Check if it is the same as
assertEquals(2, list.size());

// 2.Check in order whether the items in the cart are correct
Product item;
// 2.1 Check if the first product is the same as defined by apple
item= list.get(0);  //Take out the first item in the cart
assertEquals("A001", item.getProductId()); //Verification of the product ID of the extracted product
assertEquals("Apple", item.getProductName()); //Verification of the product name of the extracted product
assertEquals((Integer)158, item.getPrice()); //Verification of the product name of the extracted product

// 2.1 Check if the second product is the same as the one defined in orange
item = list.get(1); //Take out the second item in the cart and start verification
assertEquals("A002", item.getProductId()); //Verification of the product ID of the extracted product
assertEquals("Orange", item.getProductName()); //Verification of the product name of the extracted product
assertEquals((Integer)258, item.getPrice()); //Verification of the product name of the extracted product

By the way, the part written with " A001 " etc. can be ʻapple.getProductName ()`.

Test method (pseudo code supplement)

As I wrote at the beginning, the flow of JUnit is

--Execute based on "How to use the function" --Verify "results of using that function"

Is the basic. Its validation is validated with a method that begins with ʻassert, such as ʻassertEquals. And ** assertEquals will fail ** if both arguments are not the same, and no further processing will be done **.

For example ʻAssert Equals ("apple", item.getProductName ()); If the" apple "of the is changed to a" apple ", it becomes a failure, and thereafter assertEquals((Integer)158, item.getPrice());` Etc. will not be implemented.

And if all the assert Equals are passed and the process proceeds to the end, it will be a success.

In addition, write * ʻassertXXX (expect, actual)` in the order of correct value and test value *.

How to test with JUnit (Eclipse)

Although it is edited for Eclipse, the original is pleiades. By the way, I was exhausted with this alone ... If you have the energy, I will add VS Code etc. someday. When doing JUnit in Eclipse, do the following:

If there is no test folder under the project folder, create it.

Select File menu → New → Source Folder.

「ファイル」メニュー→「新規」→「ソースフォルダー」を選択します

Enter "test" in "Folder name:" on the next screen and click "Finish".

表示される画面の「フォルダー名:」に「test」と入力し「完了」をクリックします

Right-click on the test folder you just checked or created and select New> Other.

testフォルダを右クリックし、「新規」→「その他」を選択

A new screen will be displayed. Select "Java"-> "JUnit"-> "JUnit Test Case" and then select "Next". Alternatively, enter "junit" in the wizard field at the top of the new screen. Select "JUnit Test Cases" from the remaining ones, and then select "Next".

ウィザード画面が表示されますので「junit」と入力。JUnit テスト・ケースを選択する

The next depends on JUnit 3, 4 or 5. Use 4 or 5 if you don't have any concerns about newcomer education. Select 3 if you are in a disappointing situation where you are assigned to 3. Unfortunately, 5 will be included in 4. I didn't have enough time ...

Create a JUnit 3 test case

Create a JUnit3 test case
  1. Select New JUnit 3 Test
  2. Enter the name of the class to write the test case in Name (red line). "Test target Test" seems to be good.
  3. For the package name (blue line), 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.
パッケージ名と名前を書くクラスの名前を入力します。入力したら「完了」をクリックします

There is no library for JUnit3 at first, so you will be asked if you want to add it. Select "Perform next action", select "Add JUnit 3 library to build path", and press OK.

「次のアクションを実行」を選択し、「JUnit 3 ライブラリーをビルド・パスに追加」を選択し、OKを押します
The JUnit test case created is just a Java program file. This time, write as follows.

CartTest.java


package myStore;

import junit.framework.TestCase;

import java.util.List;

public class CartTest extends TestCase {
public void test Check if it was added to the cart() {
				//Make the products to be introduced for the test 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);
		
		//Verify from here
		List<Product> list = cart.getList(); //Get the list in the cart
		
		//Verify if the cart contains two
		assertEquals(2, list.size());
		
		//Check if the contents of the cart are correct
		Product item;
		item= list.get(0);  //Take out the first item in the cart and start verification
		assertEquals("Verification of whether the first product ID in the cart is A001", "A001", item.getProductId());
		assertEquals("Verification of whether the first product name of the cart is apple", "Apple", item.getProductName());
		assertEquals("Verification of whether the first price of the cart is 158", (Integer)158, item.getPrice());
		
		item = list.get(1); //Take out the second item in the cart and start verification
		assertEquals("Verification of whether the first product ID in the cart is A002", "A002", item.getProductId());
		assertEquals("Verification that the first product name in the cart is orange", "Orange", item.getProductName());
		assertEquals("Verification of whether the first price of the cart is 258", (Integer)258, item.getPrice());
	}
}

The promises of JUnit 3 are as follows. --ʻImport junit.framework.TestCase; write --Test class inherits TestCase class in extends TestCase --Test method qualifiers should start with public void and name start with test`

Create according to the above rules. You can create as many test methods as you like by starting with test, so please create tests with various patterns. Also, Japanese is included in the method name, but this is not a problem for Java (there is no problem in terms of specifications). In JUnit, Japanese is often used so that you can easily understand what you are testing [^ 1]. Isn't this easier to understand than writing testNo1?

You've also noticed that the first argument to assertEquals contains a string, but think of it as a comment. If you leave this in place, when you test with JUnit, this first argument will be displayed as a message to tell you what the intended test failed.

[^ 1]: However, if there is a half-width space, an error will occur, so choose a word that can express the test in one word.

Create a JUnit 4 test case

Create a JUnit4 test case
Create a JUnit4 test case
1. Select New JUnit 4 Test 2. Enter the name of the class to write the test case in Name (red line). "Test target Test" seems to be good. 3. For the package name (blue line), 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. パッケージ名と名前を書くクラスの名前を入力します。入力したら「完了」をクリックします

There is no library for JUnit 4 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.

「次のアクションを実行」を選択し、「JUnit 4 ライブラリーをビルド・パスに追加」を選択し、OKを押します
The JUnit test case created is just a Java program file. This time, write as follows.

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 to be introduced for the test 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);

		//Verify from here
		List<Product> list = cart.getList(); //Get the list in the cart

		//Verify if the cart contains two
		assertEquals(2, list.size());

		//Check if the contents of the cart are correct
		Product item;
		item= list.get(0);  //Take out the first item in the cart
		assertThat(item.getProductId(), is("A001")); //Verification of the product ID of the extracted product
		assertThat(item.getProductName(), is("Apple")); //Verification of the product name of the extracted product
		assertThat(item.getPrice(), is(158)); //Verification of the product name of the extracted product

		item = list.get(1); //Take out the second item in the cart and start verification
		assertThat(item.getProductId(), is("A002")); //Verification of the product ID of the extracted product
		assertThat(item.getProductName(), is("Orange")); //Verification of the product name of the extracted product
		assertThat(item.getPrice(), is(258)); //Verification of the product name of the extracted product
	}
}

The promises of JUnit 4 are as follows.

--Write the following first - import static org.hamcrest.CoreMatchers.*; - import static org.junit.Assert.*; - import org.junit.Test; --The 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 as many test methods as you like by adding @Test, so please create tests with various patterns. Also, Japanese is included in the method name, but this is not a problem for Java (there is no problem in terms of specifications). In JUnit, Japanese is often used so that you can easily understand what you are testing [^ 1]. Isn't this easier to understand than writing testNo1?

You've also noticed that the first argument to assertEquals contains a string, but think of it as a comment. If you leave this in place, when you test with JUnit, this first argument will be displayed as a message to tell you what the intended test failed.

It's a chord, but you can write almost the same code as JUnit 3

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 to be introduced for the test 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);

		//Verify from here
		List<Product> list = cart.getList(); //Get the list in the cart

		//Verify if the cart contains two
		assertEquals(2, list.size());

		//Check if the contents of the cart are correct
		Product item;
		item= list.get(0);  //Take out the first item in the cart and start verification
		assertEquals("Verification of whether the first product ID in the cart is A001", "A001", item.getProductId());
		assertEquals("Verification of whether the first product name of the cart is apple", "Apple", item.getProductName());
		assertEquals("Verification of whether the first price of the cart is 158", (Integer)158, item.getPrice());

		item = list.get(1); //Take out the second item in the cart and start verification
		assertEquals("Verification of whether the second product ID in the cart is A002", "A002", item.getProductId());
		assertEquals("Verification that the second product name in the cart is orange", "Orange", item.getProductName());
		assertEquals("Verify that the second price in the cart is 258", (Integer)258, item.getPrice());
	}
}

How to write JUnit4

I used ʻassert Equals` in the early stages, so here is a supplementary course. In JUnit 4, you can write as follows. From now on, this will be the main.

assertThat( target, is("target") );

It's almost the same as ʻassert Equals, but it has the big advantage of being able to read from left to right. It's 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 --Introduction to JUnit Basic usage How to use JUnit (Beginner)

Run JUnit

Execute the created test case. Select Run menu → Run → JUnit Test.

スクリーンショット 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-> "View View"-> "Other" and then select "Java"-> "JUnit" (red line).
「ウィンドウ」メニュー→「ビューの表示」→「その他」を選択 「Java」→「JUnit」(赤線部)を選択
JUnitの実行画面

Now click on the triangle to the left of myStore.CartTest. You can see what the test was done as below. Successful tests are displayed in green, and unsuccessful tests are displayed in red. クラス内で実行したテストを表示

By the way, let's change "A001" on the 28th line of the test program to "A003" and execute it again. You can right-click to run it in the same way as before, but if you have a JUnit view, you can run it again by pressing "Rerun Test" (red line). 「テストの再実行」(赤線部)を押すと再実行可能

Since the file has been changed by changing "A001" to "A003", a dialog will appear asking if you want to save it. If there is no problem, click "OK". If you uncheck the box next to the file name, the program will be executed with the file in the pre-saved state. 保存して起動ダイアログが表示されたら「OK」を押す

Then, an ultramarine blue cross will be attached to the lower left of the icon. This is a sign of failure. Then click on the failed test to see where it failed in the test case. Double-click on it to see the line in question. 失敗したテストをクリックすると、テストケースのどこで失敗したか表示されます。これをダブルクリックすると対象の行が表示されます。

Also, if you look behind the junit.framework.ComparisonFailure: of the failure trace, you can see the description of "There is a confirmation that the first product ID of the cart is A001" in the error details. .. Also, the correct value is displayed in "Expected: is" and the actual value (execution result) is displayed in "but: was". エラーの詳細にカートの1つめの商品IDがA001であるかの確認があることが確認できる

Well, it's a long story, but that's it.


What is a column test? What is quality? What to inspect and how?

It's just a poem

JUnit wrote that execution and verification are two important things. Of these, I would like to touch on verification. It's super complicated here, so you can skip it. Validation is about checking if the value of the result of the run is the same as the expected result, but there is controversy as to what to validate. There may be a definite correct answer, but I don't know (though @t_wada seems to know ...).

For example, suppose you have the following class.

SomeClass.java


class SomeClass {
  private String somethingvalue = "";
  public void setSomethingValue(String somethingvalue) {
    this.somethingvalue = somethingvalue;
  }
}

This class has only the ability to hold data for future extensions. There is a setSomethingValue that sets the data somethingvalue to keep. However, there is no getSomethingValue to get the value of value. What should I do with this setSomethingValue test? My personal answer is "based on project guidelines and specifications." First of all, even if it is a private variable (property), you can know the value of value by using Java's reflection function. [^ 2] On top of that, about projects and specifications

――If the project or specification says "set to value", it is likely to be tested. --If the policy is "Do not check private variables" or "Check with properties and methods published by the class", it will not be tested (because there is no other way to access value than reflection).

I would like to adopt the latter perspective, but in this case there is a somewhat annoying problem. When adding a method that repeatedly displays the following values for the specified number of times

public String repeatSpeak(int times) {
  String result = "";
  for (int i=0; i < times; i++) {
    result += this.value;
  }
  return result;
}

This repeatSpeak is a public (= assumed to be used by someone) method, so it will be tested. At this time, the value changes depending on value. So you would also write a test that uses setValue to change value. I think it is possible to test indirectly rather than 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 test it in team development. Ask if you want to test. If you haven't decided, let the team members share a unified perspective at the beginning of development.

[^ 2]: See the following for reflection: Samurai blog-[Introduction to Java] Executing methods and changing fields with reflection, [Hishidama] 's technical memo page-reflection](https://www.ne.jp/asahi/hishidama/home/tech/java/reflection.html),

Recommended Posts

[For newcomers] Introducing JUnit
Gradle settings for using JUnit 5
Introducing "" Monitoring Tool "" for monitoring Docker
[For our newcomers] About isXXX methods
Introducing Java tips GreenMail to Junit5
junit
Key points for introducing gRPC in Java
Introducing Docker Desktop for Windows on WSL2
Ideas for introducing symbols for OOP in flowcharts