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
. C гетерами там ничего сложного нет, попросим помощи у 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
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ