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.
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.
The basic flow of JUnit is
--Execute based on "How to use the function" --Verify "results of using that function"
Will be.
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 be
failed. 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`.
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*/ }
}
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;
}
}
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
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)
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 ()`.
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 *.
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:
Select File menu → New → Source Folder.
Enter "test" in "Folder name:" on the next screen and click "Finish".
Right-click on the test folder you just checked or created and select New> Other.
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".
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 ...
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.
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.
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.
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.
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());
}
}
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)
Execute the created test case. Select Run menu → Run → JUnit Test.
Then, the following screen will appear somewhere on the screen.
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.
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".
Well, it's a long story, but that's it.
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