JUnit :: або як полюбити валідатор JavaRush

Створюємо об'єкт
І так щоб щось тестувати, нам для початку потрібен об'єкт тестування. Перед нами стоїть завдання.- Нам потрібен об'єкт, який зберігатиме інформацію про Користувача.
- Id - потрібно рахувати по порядку додавання нового користувача.
- Ім'я користувача.
- Його вік.
- Підлога (male/female)
-
Потрібно передбачити збереження списку користувачів.
-
Клас має вміти.
- Формувати список усіх користувачів.
- Формувати список користувачів за статтю (MALE/FEMALE).
- Повертати кількість користувачів у загальному списку та порахувати кількість за ознакою статі користувача.
- Порахувати загальну суму за віком користувачів, так само врахувати за ознакою статі.
- Порахувати середній вік, як загальний і за ознакою статі.
User
він міститиме поля:
private int id;
private String name;
private int age;
private Sex sex;
Для зберігання даних про користувача цього достатньо, подивимося, що там ще потрібно за завданням. Нам потрібно якось зберігати всіх користувачів, зробимо в нашому класі статичне поле allUsers
, думаю нормально якщо це будеMap<Integer, User>
private static Map<Integer, User> allUsers;
Ще нам якось потрібно присвоювати порядковий номер користувачам, створимо статичне поле лічильник, який при створенні нового користувача присвоюватиме порядковий Id
користувачеві.
private static int countId = 0;
Так, з полями начебто розібралися, напишемо конструктор для нашого об'єкта, і гетери для полів id
, name
, age
, sex
. З гетерами там нічого складного немає, попросимо допомоги у IDEA , вона ніколи не відмовить, а конструктор зробимо трохи з хитрістю. Конструктор вмітиме. Ініціалізувати поля, перевіряти чи є такий об'єкт у allUsers
, якщо такого об'єкта немає, то збільшуємо наш лічильник countId++
і додаємо його до списку всіх користувачів. А також ініціалізувати поле allUsers
їли воно ще не було ініціалізоване. Для зручності пошуку однакових об'єктів, перевизначимо методи equals()
і hashCode()
знову попросимо допомоги у улюбленої IDEA і будемо порівнювати по полях name
, age
,sex
. Плюс створимо приватний метод hasUser()
, який перевірятиме чи є такий об'єкт у списку.
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age &&
Objects.equals(name, user.name) &&
sex == user.sex;
}
@Override
public int hashCode() {
return Objects.hash(name, age, sex);
}
Конструктор зрештою у мене вийшов такий.
public User(String name, int age, Sex sex) {
if (allUsers == null){
allUsers = new HashMap<>();
}
this.name = name;
this.age = age;
this.sex = sex;
if (!hasUser()){
countId++;
this.id = countId;
allUsers.put(id, this);
}
}
та допоміжний приватний метод
private boolean hasUser(){
for (User user : allUsers.values()){
if (user.equals(this) && user.hashCode() == this.hashCode()){
return true;
}
}
return false;
}
а також перевизначимоtoString()
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
", sex=" + sex +
'}';
}
Тепер настав час реалізувати логіку необхідних методів. Так як логіка в основному працюватиме зі статичними полями, методи теж зробимо статичними, для об'єктів вони не потрібні.
- Формувати список усіх користувачів.
- Формувати список користувачів за статтю (MALE/FEMALE).
-
Повертати кількість користувачів у загальному списку та порахувати кількість за ознакою статі користувача.
public static int getHowManyUsers(){ return allUsers.size(); } public static int getHowManyUsers(Sex sex){ return getAllUsers(sex).size(); }
-
Порахувати загальну суму за віком користувачів, так само врахувати за ознакою статі. Для цього завдання зробимо методи.
public static int getAllAgeUsers(){ int countAge = 0; for (User user : allUsers.values()){ countAge += user.age; } return countAge; } public static int getAllAgeUsers(Sex sex){ int countAge = 0; for (User user : getAllUsers(sex)){ countAge += user.age; } return countAge; }
-
Порахувати середній вік, як загальний і за ознакою статі.
public static int getAverageAgeOfAllUsers(){ return getAllAgeUsers() / getHowManyUsers(); } public static int getAverageAgeOfAllUsers(Sex sex){ return getAllAgeUsers(sex) / getHowManyUsers(sex); }
Відмінно, необхідний об'єкт та її поведінка ми описали. Тепер можна переходити до JUnit , але для початку покажу як приблизно буде виглядати простий тест, якщо ми його буде робити в main .
public static void main(String[] args) { new User("Євген", 35, Sex.MALE); new User("Марина", 34, Sex.FEMALE); new User("Аліна", 7, Sex.FEMALE); System.out.println("Всі користувачі:"); User.getAllUsers().forEach(System.out::println); System.out.println("Всі користувачі: MALE"); User.getAllUsers(Sex.MALE).forEach(System.out::println); System.out.println("Всі користувачі: FEMALE"); User.getAllUsers(Sex.FEMALE).forEach(System.out::println); System.out.println("================================================"); System.out.println(" всіх користувачів: " + User.getHowManyUsers()); System.out.println(" всіх користувачів MALE: " + User.getHowManyUsers(Sex.MALE)); System.out.println("всіх користувачів FEMALE: " + User.getHowManyUsers(Sex.FEMALE)); System.out.println("================================================"); System.out.println(" загальний вік всіх користувачів: " + User.getAllAgeUsers()); System.out.println(" загальний вік всіх користувачів MALE: " + User.getAllAgeUsers(Sex.MALE)); System.out.println("загальний вік усіх користувачів FEMALE: " + User.getAllAgeUsers(Sex.FEMALE)); System.out.println("================================================"); System.out.println(" середній вік всіх користувачів: " + User.getAverageAgeOfAllUsers()); System.out.println(" середній вік всіх користувачів MALE: " + User.getAverageAgeOfAllUsers(Sex.MALE)); System.out.println("середній вік усіх користувачів FEMALE: " + User.getAverageAgeOfAllUsers(Sex.FEMALE)); System.out.println("================================================"); }
Висновок у консоль отримаємо приблизно такий, а далі порівнюємо чи ми отримали нормальну роботу. Можна, звичайно, заглибитися, написати логіку порівняння, і подивитися, що скаже наше обчислення, при тому, що ми не будемо впевнені, що всі змогли передбачити.
//output Все пользователи: User{id=1, name='Євген', age=35, sex=MALE} User{id=2, name='Марина', age=34, sex=FEMALE} User{id=3, name='Аліна', age=7, sex=FEMALE} Все пользователи: MALE User{id=1, name='Євген', age=35, sex=MALE} Все пользователи: FEMALE User{id=2, name='Марина', age=34, sex=FEMALE} User{id=3, name='Аліна', age=7, sex=FEMALE} ================================================ всех пользователей: 3 всех пользователей MALE: 1 всех пользователей FEMALE: 2 ================================================ общий возраст всех пользователей: 76 общий возраст всех пользователей MALE: 35 общий возраст всех пользователей FEMALE: 41 ================================================ средний возраст всех пользователей: 25 средний возраст всех пользователей MALE: 35 средний возраст всех пользователей FEMALE: 20 ================================================ Process finished with exit code 0
Нас цей результат не влаштовує, геть тести main , нам потрібен JUnit .
З пунктами a і b добре впорається метод getAllUsers()
який повертатиме аркуш всіх User
, і перевантажений метод getAllUsers(Sex sex)
він повертатиме список, залежно від переданого параметра Sex
.
public static List<User> getAllUsers(){
return new ArrayList<>(allUsers.values());
}
public static List<User> getAllUsers(Sex sex){
List<User> listAllUsers = new ArrayList<>();
for (User user : allUsers.values()){
if (user.sex == sex){
listAllUsers.add(user);
}
}
return listAllUsers;
}
Як підключити JUnit до проекту
Постає питання, як його підключити до проекту. Для знаючих варіант з Maven брати не буду, тому що це зовсім інша історія. ;) Відкриваємо структуру проекту Ctrl + Alt + Shift + S -> Libraries -> тиснемо + (New Project Library) -> вибираємо from Maven далі бачимо таке вікно, в рядок


User
-> тиснемо Alt + Enter -> вибираємо create Test. Ми повинні побачити вікно, в якому нам потрібно вибрати бібліотеку JUnit4 -> вибрати методи, які збираємося тестувати -> OK 
UserTest
Це і є клас, в якому ми будемо покривати наш код тестами. Приступимо:
Наш перший @Test
Створимо наш перший @Test методgetAllUsers()
- це метод який повинен повернути всіх користувачів. Тест виглядатиме приблизно так:
@Test
public void getAllUsers() {
//створюємо тестові дані
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
//створюємо список expected та заповнюємо його даними нашого методу
List<User> expected = User.getAllUsers();
//створюємо список actual в нього розміщуємо дані для порівняння
//що ми припускаємо метод повинен повернути
List<User> actual = new ArrayList<>();
actual.add(user);
actual.add(user1);
actual.add(user2);
//запускаємо тест, якщо список expected і actual не будуть рівні
//тест буде провалений, про результати тесту читаємо у консолі
Assert.assertEquals(expected, actual);
}
Тут ми створюємо кілька тестових користувачів -> створюємо список expected
в який помістимо користувачів яких нам поверне метод getAllUsers()
-> створимо список actual
в який помістимо користувачів яких ми припускаємо, що метод getAllUsers()
Assert.assertEquals(actual, expected) йому ми і передамо списки, що інспектується та актуальний. Цей метод перевірить об'єкти у наданих списках та видасть результат тесту. Метод порівнюватиме всі поля об'єктів, навіть пройдеться полями батьків, якщо є успадкування. Запускаємо перший тест... 
actual
Тепер спробуємо зробити так, щоб тест був провалений, для цього нам потрібно змінити один зі списків тесту, зробимо це шляхом закоментувавши додавання одного користувача до списку
@Test
public void getAllUsers() {
//створюємо тестові дані
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
//створюємо список expected та заповнюємо його даними нашого методу
List<User> expected = User.getAllUsers();
//створюємо список actual в нього розміщуємо дані для порівняння
//що ми припускаємо метод повинен повернути
List<User> actual = new ArrayList<>();
actual.add(user);
actual.add(user1);
//actual.add(user2);
//запускаємо тест, якщо список expected і actual не будуть рівні
//тест буде провалений, про результати тесту читаємо у консолі
Assert.assertEquals(expected, actual);
}
запускаємо тест і бачимо наступне: 
@Test
public void getAllUsers() {
//створюємо тестові дані
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
//створюємо список expected та заповнюємо його даними нашого методу
List<User> expected = User.getAllUsers();
//створюємо список actual в нього розміщуємо дані для порівняння
//що ми припускаємо метод повинен повернути
List<User> actual = new ArrayList<>();
actual.add(new User("User1", 1, Sex.MALE));
actual.add(new User("User2", 2, Sex.FEMALE));
actual.add(new User("User3", 3, Sex.MALE));
//запускаємо тест, якщо список expected і actual не будуть рівні
//тест буде провалений, про результати тесту читаємо у консолі
Assert.assertEquals(expected, actual);
}
ось що буде в консолі: 

main
таке може? - Ні. JUnit : main = 2 : 0 Ну що, підемо далі у нас ще купа методів, які потрібно покрити тестами ), але зачекайте, а буде не погано, перевірити, а чи не буде нам метод повертати , адже приблизно так нас getAllUsers()
на null
задачах JavaRush ловить валідатор). Зробимо це, ділиш то на три копійки…
@Test
public void getAllUsers_NO_NULL() {
//додамо перевірку на null
List<User> expected = User.getAllUsers();
Assert.assertNotNull(expected);
}
Так, так приблизно так валідатор ловить наш null
;) Тепер запустимо цей тест, і подивимося, що він нам покаже. А покаже він помилку, як???? як же тут можна було припуститися помилки тесту))) 
allUsers
ми ініціалізували в конструкторі, і отже при виклику методу getAllUsers()
ми звернемося до об'єкта, який ще не був ініціалізований. Правитимемо, приберемо ініціалізацію з конструктора, і зробимо її при оголошенні поля.
private static Map<Integer, User> allUsers = new HashMap<>();
public User(String name, int age, Sex sex) {
this.name = name;
this.age = age;
this.sex = sex;
if (!hasUser()) {
countId++;
this.id = countId;
allUsers.put(id, this);
}
}
Запустимо тест, тепер усе гаразд. 
package user;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
public class UserTest {
@Test
public void getAllUsers() {
//створюємо тестові дані
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
//створюємо список expected та заповнюємо його даними нашого методу
List<User> expected = User.getAllUsers();
//створюємо список actual в нього розміщуємо дані для порівняння
//що ми припускаємо метод повинен повернути
List<User> actual = new ArrayList<>();
actual.add(user);
actual.add(user1);
actual.add(user2);
//запускаємо тест, якщо список expected і actual не будуть рівні
//тест буде провалений, про результати тесту читаємо у консолі
Assert.assertEquals(expected, actual);
}
@Test
public void getAllUsers_NO_NULL() {
//додамо перевірку на null
List<User> expected = User.getAllUsers();
Assert.assertNotNull(expected);
}
@Test
public void getAllUsers_MALE() {
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
List<User> expected = User.getAllUsers(Sex.MALE);
List<User> actual = new ArrayList<>();
actual.add(user);
Assert.assertEquals(expected, actual);
}
@Test
public void getAllUsers_MALE_NO_NULL() {
//додамо перевірку на null
List<User> expected = User.getAllUsers(Sex.MALE);
Assert.assertNotNull(expected);
}
@Test
public void getAllUsers_FEMALE() {
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
List<User> expected = User.getAllUsers(Sex.FEMALE);
List<User> actual = new ArrayList<>();
actual.add(user1);
actual.add(user2);
Assert.assertEquals(expected, actual);
}
@Test
public void getAllUsers_FEMALE_NO_NULL() {
//додамо перевірку на null
List<User> expected = User.getAllUsers(Sex.FEMALE);
Assert.assertNotNull(expected);
}
@Test
public void getHowManyUsers() {
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
int expected = User.getHowManyUsers();
int actual = 3;
Assert.assertEquals(expected, actual);
}
@Test
public void getHowManyUsers_MALE() {
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
int expected = User.getHowManyUsers(Sex.MALE);
int actual = 1;
Assert.assertEquals(expected, actual);
}
@Test
public void getHowManyUsers_FEMALE() {
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
int expected = User.getHowManyUsers(Sex.FEMALE);
int actual = 2;
Assert.assertEquals(expected, actual);
}
@Test
public void getAllAgeUsers() {
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
int expected = User.getAllAgeUsers();
int actual = 35 + 34 + 7;
Assert.assertEquals(expected, actual);
}
@Test
public void getAllAgeUsers_MALE() {
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
int expected = User.getAllAgeUsers(Sex.MALE);
int actual = 35;
Assert.assertEquals(expected, actual);
}
@Test
public void getAllAgeUsers_FEMALE() {
User user = new User("Євген", 35, Sex.MALE);
User user1 = new User("Марина", 34, Sex.FEMALE);
User user2 = new User("Аліна", 7, Sex.FEMALE);
int expected = User.getAllAgeUsers(Sex.FEMALE);
int actual = 34 + 7;
Assert.assertEquals(expected, actual);
}
}
Та не маленький вийшов, а що буде при роботі з великими проектами. Що ж тут можна скоротити, оцінивши все, можна помітити, що тестові дані ми створюємо в кожному тесті, і тут нам на допомогу приходять анотації. Візьмемо @Before
— Анотація @Before
вказує на те, що метод буде виконуватися перед кожним методом, що тестується @Test
. Ось так тепер буде виглядати наш клас тестів з анотацією @Before
:
package user;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.*;
public class UserTest {
private User user;
private User user1;
private User user2;
@Before
public void setUp() throws Exception {
user = new User("Євген", 35, Sex.MALE);
user1 = new User("Марина", 34, Sex.FEMALE);
user2 = new User("Аліна", 7, Sex.FEMALE);
}
@Test
public void getAllUsers() {
List<User> expected = User.getAllUsers();
List<User> actual = new ArrayList<>();
actual.add(user);
actual.add(user1);
actual.add(user2);
Assert.assertEquals(expected, actual);
}
@Test
public void getAllUsers_NO_NULL() {
List<User> expected = User.getAllUsers();
Assert.assertNotNull(expected);
}
@Test
public void getAllUsers_MALE() {
List<User> expected = User.getAllUsers(Sex.MALE);
List<User> actual = new ArrayList<>();
actual.add(user);
Assert.assertEquals(expected, actual);
}
@Test
public void getAllUsers_MALE_NO_NULL() {
//додамо перевірку на null
List<User> expected = User.getAllUsers(Sex.MALE);
Assert.assertNotNull(expected);
}
@Test
public void getAllUsers_FEMALE() {
List<User> expected = User.getAllUsers(Sex.FEMALE);
List<User> actual = new ArrayList<>();
actual.add(user1);
actual.add(user2);
Assert.assertEquals(expected, actual);
}
@Test
public void getAllUsers_FEMALE_NO_NULL() {
//додамо перевірку на null
List<User> expected = User.getAllUsers(Sex.FEMALE);
Assert.assertNotNull(expected);
}
@Test
public void getHowManyUsers() {
int expected = User.getHowManyUsers();
int actual = 3;
Assert.assertEquals(expected, actual);
}
@Test
public void getHowManyUsers_MALE() {
int expected = User.getHowManyUsers(Sex.MALE);
int actual = 1;
Assert.assertEquals(expected, actual);
}
@Test
public void getHowManyUsers_FEMALE() {
int expected = User.getHowManyUsers(Sex.FEMALE);
int actual = 2;
Assert.assertEquals(expected, actual);
}
@Test
public void getAllAgeUsers() {
int expected = User.getAllAgeUsers();
int actual = 35 + 34 + 7;
Assert.assertEquals(expected, actual);
}
@Test
public void getAllAgeUsers_MALE() {
int expected = User.getAllAgeUsers(Sex.MALE);
int actual = 35;
Assert.assertEquals(expected, actual);
}
@Test
public void getAllAgeUsers_FEMALE() {
int expected = User.getAllAgeUsers(Sex.FEMALE);
int actual = 34 + 7;
Assert.assertEquals(expected, actual);
}
}
Ну як вам, вже веселіше та легше читати ;) Ось список анотацій для JUnit з ними однозначно жити простіше.
@Test – определяет что метод method() является тестовым.
@Before – указывает на то, что метод будет выполнятся перед каждым тестируемым методом @Test.
@After – указывает на то что метод будет выполнятся после каждого тестируемого метода @Test
@BeforeClass – указывает на то, что метод будет выполнятся в начале всех тестов,
а точней в момент запуска тестов(перед всеми тестами @Test).
@AfterClass – указывает на то, что метод будет выполнятся после всех тестов.
@Ignore – говорит, что метод будет проигнорирован в момент проведения тестирования.
(expected = Exception.class) – указывает на то, что в данном тестовом методе
вы преднамеренно ожидаете Exception.
(timeout = 100) – указывает, что тестируемый метод не должен занимать больше чем 100 миллисекунд.
Основні методи класу Assert
для перевірки:
fail(String) – указывает на то что бы тестовый метод завалился при этом выводя текстовое сообщение.
assertTrue([message], boolean condition) – проверяет, что логическое умова истинно.
assertsEquals([String message], expected, actual) – проверяет, что два значения совпадают.
Примечание: для массивов проверяются ссылки, а не содержание массивов.
assertNull([message], object) – проверяет, что об'єкт является пустым null.
assertNotNull([message], object) – проверяет, что об'єкт не является пустым null.
assertSame([String], expected, actual) – проверяет, что обе переменные относятся к одному об'єкту.
assertNotSame([String], expected, actual) – проверяет, что обе переменные относятся к разным об'єктам.
Ось так ми можемо додати залежність JUnit 4.12 у Maven
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
продовження тут -> JUnit part II
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ