Как писать тесты на java
Перейти к содержимому

Как писать тесты на java

  • автор:

Тестирование в Java. JUnit


Сегодня все большую популярность приобретает test-driven development(TDD), техника разработки ПО, при которой сначала пишется тест на определенный функционал, а затем пишется реализация этого функционала. На практике все, конечно же, не настолько идеально, но в результате код не только написан и протестирован, но тесты как бы неявно задают требования к функционалу, а также показывают пример использования этого функционала.

Итак, техника довольно понятна, но встает вопрос, что использовать для написания этих самых тестов? В этой и других статьях я хотел бы поделиться своим опытом в использовании различных инструментов и техник для тестирования кода в Java.

Ну и начну с, пожалуй, самого известного, а потому и самого используемого фреймворка для тестирования — JUnit. Используется он в двух вариантах JUnit 3 и JUnit 4. Рассмотрю обе версии, так как в старых проектах до сих пор используется 3-я, которая поддерживает Java 1.4.

Я не претендую на автора каких-либо оригинальных идей, и возможно многим все, о чем будет рассказано в статье, знакомо. Но если вам все еще интересно, то добро пожаловать под кат.

JUnit 3

Для создания теста нужно унаследовать тест-класс от TestCase, переопределить методы setUp и tearDown если надо, ну и самое главное — создать тестовые методы(должны начинаться с test). При запуске теста сначала создается экземляр тест-класса(для каждого теста в классе отдельный экземпляр класса), затем выполняется метод setUp, запускается сам тест, ну и в завершение выполняется метод tearDown. Если какой-либо из методов выбрасывает исключение, тест считается провалившимся.

Примечание: тестовые методы должны быть public void, могут быть static.

Сами тесты состоят из выполнения некоторого кода и проверок. Проверки чаще всего выполняются с помощью класса Assert хотя иногда используют ключевое слово assert.

Рассмотрим пример. Есть утилита для работы со строками, есть методы для проверки пустой строки и представления последовательности байт в виде 16-ричной строки:

Напишем для нее тесты, используя JUnit 3. Удобнее всего, на мой взгляд, писать тесты, рассматривая нейкий класс как черный ящик, писать отдельный тест на каждый значимый метод в этом классе, для каждого набора входных параметров какой-то ожидаемый результат. Например, тест для isEmpty метода:

Можно разделить данные и логику теста, перенеся создание данных в метод setUp:

Дополнительные возможности

Кроме того, что было описано, есть еще несколько дополнительных возможностей. Например, можно группировать тесты. Для этого нужно использовать класс TestSuite:

Можно запустить один и тот же тест несколько раз. Для этого используем RepeatedTest:

Наследуя тест-класс от ExceptionTestCase, можно проверить что-либо на выброс исключения:

Как видно из примеров все довольно просто, ничего лишнего, минимум нужный для тестирования(хотя недостает и некоторых нужных вещей).

JUnit 4

Здесь была добавлена поддержка новых возможностей из Java 5, тесты теперь могут быть объявлены с помощью аннотаций. При этом существует обратная совместимость с предыдущей версией фреймворка, практически все рассмотренные выше примеры будут работать и здесь(за исключением RepeatedTest, его нет в новой версии).

Итак, что же поменялось?

Основные аннотации

Рассмотрим тот же пример, но уже используя новые возможности:

  • Для упрощения работы я предпочитаю наследоваться от класса Assert, хотя это необязательно.
  • Аннотация Before обозначает методы, которые будут вызваны до исполнения теста, методы должны быть public void. Здесь обычно размещаются предустановки для теста, в нашем случае это генерация тестовых данных (метод setUpToHexStringData).
  • Аннотация @BeforeClass обозначает методы, которые будут вызваны до создания экземпляра тест-класса, методы должны быть public static void. Имеет смысл размещать предустановки для теста в случае, когда класс содержит несколько тестов использующих различные предустановки, либо когда несколько тестов используют одни и те же данные, чтобы не тратить время на их создание для каждого теста.
  • Аннотация After обозначает методы, которые будут вызваны после выполнения теста, методы должны быть public void. Здесь размещаются операции освобождения ресурсов после теста, в нашем случае — очистка тестовых данных (метод tearDownToHexStringData).
  • Аннотация @AfterClass связана по смыслу с @BeforeClass, но выполняет методы после теста, как и в случае с @BeforeClass, методы должны быть public static void.
  • Аннотация Test обозначает тестовые методы. Как и ранее, эти методы должны быть public void. Здесь размещаются сами проверки. Кроме того, у данной аннотации есть два параметра, expected — задает ожидаемое исключение и timeout — задает время, по истечению которого тест считается провалившимся.

Если какой-либо тест по какой-либо серьезной причине нужно отключить(например, этот тест постоянно валится, но его исправление отложено до светлого будущего) его можно зааннотировать @Ignore. Также, если поместить эту аннотацию на класс, то все тесты в этом классе будут отключены.

Правила

Кроме всего вышеперечисленного есть довольно интересная вещь — правила. Правила это некое подобие утилит для тестов, которые добавляют функционал до и после выполнения теста.

Например, есть встроенные правила для задания таймаута для теста(Timeout), для задания ожидаемых исключений(ExpectedException), для работы с временными файлами(TemporaryFolder) и д.р. Для объявления правила необходимо создать public не static поле типа производного от MethodRule и зааннотировать его с помощью Rule.

Также в сети можно найти и другие варианты использования. Например, здесь рассмотрена возможность параллельного запуска теста.

Запускалки

Но и на этом возможности фреймворка не заканчиваются. То, как запускается тест, тоже может быть сконфигурировано с помощью @RunWith. При этом класс, указанный в аннотации должен наследоваться от Runner. Рассмотрим запускалки, идущие в комплекте с самим фреймворком.

JUnit4 — запускалка по умолчанию, как понятно из названия, предназначена для запуска JUnit 4 тестов.

JUnit38ClassRunner предназначен для запуска тестов, написанных с использованием JUnit 3.

SuiteMethod либо AllTests тоже предназначены для запуска JUnit 3 тестов. В отличие от предыдущей запускалки, в эту передается класс со статическим методом suite возвращающим тест(последовательность всех тестов).

Suite — эквивалент предыдущего, только для JUnit 4 тестов. Для настройки запускаемых тестов используется аннотация @SuiteClasses.

Enclosed — то же, что и предыдущий вариант, но вместо настройки с помощью аннотации используются все внутренние классы.

Categories — попытка организовать тесты в категории(группы). Для этого тестам задается категория с помощью @Category, затем настраиваются запускаемые категории тестов в сюите. Это может выглядеть так:

Parameterized — довольно интересная запускалка, позволяет писать параметризированные тесты. Для этого в тест-классе объявляется статический метод возвращающий список данных, которые затем будут использованы в качестве аргументов конструктора класса.

Theories — чем-то схожа с предыдущей, но параметризирует тестовый метод, а не конструктор. Данные помечаются с помощью @DataPoints и @DataPoint, тестовый метод — с помощью Theory. Тест использующий этот функционал будет выглядеть примерно так:

Как и в случае с правилами, в сети можно найти и другие варианты использования. Например, здесь рассмотрена та же возможность паралельного запуска теста, но с использованием запускалок.

Вывод

Это, конечно же, не все, что можно было сказать по JUnit-у, но я старался вкратце и по делу. Как видно, фреймворк достаточно прост в использовании, дополнительных возможностей немного, но есть возможность расширения с помощью правил и запускалок. Но несмотря на все это я все же предпочитаю TestNG с его мощным функционалом, о котором и расскажу в следующей статье.

How to Write Unit Tests in Java

Kunal Nalawade

Kunal Nalawade

How to Write Unit Tests in Java

Let’s say you are developing an application. After long hours of coding, you manage to create some cool features. Now, you want to make sure the features are working as you want.

This involves testing if each and every piece of code works as expected. This procedure is known as Unit Testing. Different languages provide their own frameworks for testing.

In this article, I am going to show you how to write unit tests in Java. I’ll first explain what testing involves and some concepts you’ll need to know. Then, I’ll show a few examples to help you understand better.

For this article, I assume you are familiar with Java and the IntelliJ IDE.

Project Setup

For this project, I’ll be using IntelliJ IDE. If you don’t have it, follow this guide to install the IDE.

In this project, we’ll be using the JUnit and Mockito libraries for testing. These are the most commonly used libraries for testing in Java. You’ll understand how these libraries are used as you go through the post.

To setup JUnit, follow these steps as described in this guide:

  1. From the main menu, select File > New > Project.
  2. Select New Project. Specify a name for the project, I’ll give it junit-testing-tutorial.
  3. Select Maven as a build tool and in language, select Java.
  4. From the JDK list, select the JDK you want to use in the project.
  5. Click Create.
  6. Open pom.xml in the root directory of your project.
  7. In pom.xml, press ⌘ + N , and select Add dependency.
  8. This will open a tool window, type org.junit.jupiter:junit-jupiter in the search field. Locate the necessary dependency and click Add next to it.
  9. Now, click Load Maven Changes in the notification that appears in the top-right corner in the editor.

Now, to set up Mockito, add these two dependencies in your pom.xml :

Note: The version may differ depending on the time you are reading this post.

To complete the setup, create a class Welcome and define your main function there.

Screenshot-2023-03-12-at-6.14.33-PM

Folder Structure and the Main Method

What is Unit Testing?

Unit Testing involves testing each and every component of your code to see if they work as expected. It isolates each individual method of your code and performs tests on it. Unit tests helps make sure that your software is working as expected before releasing it.

As a developer, you’ll write unit tests as soon as you finish writing a piece of code. Now, you might ask, isn’t that the job of a tester? In a way, yes, a tester is responsible for testing software. But, covering every line of code adds a lot of pressure on the tester. So, it’s a best practice for developers to write tests for their own code as well.

The goal of unit testing is to make sure that any new functionality does not break the existing functionality. It also helps identify any issues or bugs earlier in the development process and helps ensure that code meets quality standards set by the organisation.

Do’s and Don’ts of Unit Testing

Remember the following guidelines while writing tests for your methods:

  • Do test if the expected output of a method matches the actual one.
  • Do test whether the function calls made inside the method are occurring the desired number of times.
  • Do not try to test code that is not a part of the method under test.
  • Do not make API calls, database connections, or network requests while writing your tests.

Now, let’s go over some concepts you need to know before we jump into writing tests.

Assertions

Assertions determine whether your test passes or fails. They compare the expected return value of a method with the actual one. There are a number of assertions you can make at the end of your test.

The Assertions class in JUnit consists of static methods that provide various conditions deciding whether the test passes or not. We’ll see these methods as I walk you through each example.

Mocking

The class whose methods you are testing may have some external dependencies. As mentioned before, you should not try to test code that is not part of the function under test.

But in cases where your function uses an external class, it is a good practice to mock that class – that is, have mock values instead of the actual ones. We’ll use the Mockito library for this purpose.

Method Stubbing

External dependencies may not be limited to just classes, but to certain methods as well. Method stubbing should be done when your function is calling an external function in its code. In this case, you make that function return the value you want instead of calling the actual method.

For instance, the method you’re testing (A) is calling an external method (B) in its implementation. B makes a database query, fetching all students with marks greater than 80. Making an actual database call isn’t a good practice here. So, you stub the method and make it return a dummy list of students you need for testing.

You’ll understand this better with examples. There are many other concepts that are a part of testing in Java. But, for now these three are enough for you to get started.

Steps to Perform While Testing

  1. Initialize the necessary parameters you’ll need to perform the test.
  2. Create mock objects and stub any methods if required.
  3. Call the method you are testing with the parameters you initialized in Step 1.
  4. Add an assertion to check the outcome of your test. This will decide if the test passes.

You’ll understand these steps more with examples. Let’s start with a basic test first.

How to Write the First Test

Let’s write a simple function that compares two numbers. It returns 1 if the first number is greater than the second and returns -1 otherwise.

We’ll put this function inside a Basics class:

Pretty simple! Let’s write the test for this class. All your tests should be located inside the test folder.

Screenshot-2023-03-13-at-5.12.04-PM

Folder Structure

Inside the test folder, create a class BasicTests where you’ll write your tests for this class. The name of the class doesn’t matter, but it is a good practice to segregate tests according to each class. Also, follow a similar folder structure as the one in your main code.

Unit tests are basically a set of methods you define that test each method of your class. Inside the above class, create a method compare() with a return type of void . Again, you can name the method anything you want.

The @Test annotation indicates that this method is to be run as a test case.

Now, to test the method, you need to create the object of the above class and call the method by passing some values.

Now, use the assertEquals() method of the Assertions class to check if the expected value matches the expected one.

Our test should pass, as the value returned by the method matches the expected one. To check it, run the test by right clicking the green arrow next to the test method.

Screenshot-2023-03-13-at-5.30.14-PM

Running a Test

Your test results will be shown below.

Screenshot-2023-03-13-at-5.32.06-PM

Test Results

More Test Examples

In the above test, we only tested one scenario. When there’s branching in the function, you need to write tests for each condition. Let’s introduce some more branching in the above function.

We have already tested the first branch, so let’s write tests for the other two.

The @DisplayName annotation displays the text instead of the method name below. Let’s run the test.

Screenshot-2023-03-13-at-6.01.15-PM

Test Passed

For the case where the two numbers are equal:

Sorting an Array

Now, let’s write the test for the following code that sorts an array.

To write the test for this, we’ll follow a similar procedure: call the method and pass an array to it. Use the assertArrayEquals() to write your assertion.

One challenge for you: write code that reverses a string and write a test case for it.

How to Create Mocks and Stubs for Testing

We have seen a few basic examples of unit tests where you made simple assertions. However, the functions you are testing may contain external dependencies like model classes and database or network connections.

Now, you can’t make actual connections in your tests, as it would be very time consuming. In such cases, you mock such implementations. Let’s see a few examples of mocking.

Mocking a Class

Let’s have a class User with the following properties:

Click on ⌘ + N to generate getters and setters for the above properties.

Screenshot-2023-03-18-at-8.34.34-PM

Generate Options

Let’s take a new class Mocking that uses the above object.

This class has a method that assigns certain permissions based on the user’s role. It returns 1 if the permission is assigned successfully, else it returns -1 .

For demo purposes, I have only added println() statements. The actual implementation may involve setting certain properties.

In the tests file, we’ll add an @ExtendWith annotation at the top since we are using Mockito. I have not shown the imports here as IntelliJ does them automatically.

So how do we write the test for the method? We’ll need to mock the User object. You can do this by adding a @Mock annotation while declaring the object.

You can also use the mock() method, as it’s similar.

Let’s write the test method.

When you run the test, it throws a NullPointerException .

Screenshot-2023-03-18-at-8.51.26-PM

User Object is Null

This is because the user object hasn’t been initialised yet. The method you called wasn’t able to use the mocked object. For this, you’ll need to call the setUser method.

Now, the test gives the following error as the mocked object is initially filled with null values.

Screenshot-2023-03-18-at-8.53.53-PM

Return Value of getRole() is Null

Does this mean you need to fill actual values in the mock object? No, you just need the getRole() method to return a non-null value. For that, we’ll use method stubbing.

Using when(). thenReturn() tells the test to return a value when a method is called. You should stub methods only for mocked objects.

We’ll do the same for the getUsername() method.

Now, if you run the test, it will pass.

Screenshot-2023-03-18-at-9.00.21-PM

Test Passed

Method Stubbing Example

In the above example, I simply stubbed getter methods to demonstrate method stubbing. Instead of stubbing getters, you could set the role and username with a parameterised constructor or setter methods if they are available.

But what if the user class has a method that returns all the posts containing a certain word in them?

We want this method to return all posts containing the word «awesome». If you call the actual implementation of this method, it might take a long time as the number of posts could be huge. Also, if you are mocking the User object, the posts array would be null.

In this case, you stub the method and make it return the list you want.

Method Stubbing in Database Queries

Let’s see how to test methods that involve making database connections. First, create a class ApplicationDao that contains all the methods performing database queries.

Define a method that fetches the user by id and returns null if the user is not found.

Create another method to save a user into the database. This method throws an exception if the user object you are trying to save is null .

Our Mocking class will use these methods to implement its own functionalities. We’ll implement one function that updates the name of a user.

The method implementation is pretty straightforward. First, get the user by id , change its username and save the updated user object. We’ll write the test cases for this method.

There are two cases we need to test. The first is when the user is updated successfully. The second is when the update fails, that is when an exception is thrown.

Before writing the tests, create a mock of the ApplicationDao object as we do not want to make actual database connections.

Let’s write our first test.

Create a user object for testing.

Since we are calling an external method, let’s stub the method so that it returns the above User object.

Pass Mockito.anyString() to the method as we want the stub to work for any string parameter. Now, add an assertion to check if the method is working correctly.

The method returns 1 on successful update, so the test passes.

Now, let’s test another scenario where the method fails and throws an exception. Simulate this scenario by making the method getUserById() return null.

This value is then passed to the save() method which in turn throws an exception. In our assertion, we’ll use assertThrows() method to test whether an exception was thrown. This method takes the type of the exception and a lambda expression as parameters.

Since the exception is thrown, our test passes.

Screenshot-2023-03-26-at-4.27.07-PM

Tests Passed

You can find the complete code here on GitHub.

Conclusion

As a developer, writing unit tests for your code is important. It helps you identify bugs earlier in the development process.

In this post, I started by introducing Unit Testing and explained three important concepts involved in the testing process. These gave you some background before jumping into the code.

After that, I showed you, with examples, how you can test different scenarios by using the same basic techniques in testing. I also showed how to use mock classes and methods for testing complex implementations.

JUnit part I

JUnit part I - 1

Кратко о том, зачем этот зверь нам нужен? JUnit — это фреймворк автоматического тестирования вашего хорошего или не совсем хорошего кода. Можно сказать: — зачем мне эти качели, я и так смогу легко, и просто протестировать свой хороший Java код. Можно много писать вступительной лирики, но поэт из меня никакой, перейдём лучше к делу…

Создаем объект

  1. Нам нужен объект, который будет хранить информацию о Пользователе.
    1. Id — нужно считать по порядку добавления нового пользователя.
    2. Имя пользователя.
    3. Его возраст.
    4. Пол (male/female)

    Нужно предусмотреть хранение списка пользователей.

    Класс должен уметь.

    1. Формировать список всех пользователей.
    2. Формировать список пользователей по полу (MALE/FEMALE).
    3. Возвращать количество пользователей в общем списке, и посчитать количество по признаку пола пользователя.
    4. Посчитать общую сумму по возрасту пользователей, так же учесть по признаку пола.
    5. Посчитать средний возраст, как общий так и по признаку пола.

    Возвращать количество пользователей в общем списке, и посчитать количество по признаку пола пользователя.

    Посчитать общую сумму по возрасту пользователей, так же учесть по признаку пола. Для этой задачи сделаем методы.

    Посчитать средний возраст, как общий так и по признаку пола.

    Отлично, требуемый объект и его поведение мы описали. Теперь можно переходить к JUnit, но для начала покажу как примерно будет выглядеть простой тест если мы его будет делать в main.

    Нас этот исход не устраивает, долой тесты main, нам нужен JUnit.

    Как подключить JUnit к проекту

    Возникает вопрос, как его подключить к проекту. Для знающих вариант с Mavenбрать не буду, так как это совсем другая история. 😉 Открываем структуру проекта Ctrl + Alt + Shift + S -> Libraries -> жмем + (New Project Library) -> выбираем from Maven JUnit part I - 2дальше видим такое окно, в строку поиска вводим “ junit:junit:4.12 ” ждем пока найдет -> OK! -> OK! JUnit part I - 3Должен получиться такой результат JUnit part I - 4Жмем OK, поздравлю JUnit добавлен к проекту. Едем дальше. Теперь нам нужно создать тесты для нашего Java класса, ставим курсор на название класса User -> жмем Alt + Enter -> выбираем create Test. Мы должны увидеть окно, в котором нам нужно выбрать библиотеку JUnit4 -> выбрать методы которые собираемся тестировать -> OK JUnit part I - 5Идея сама создаст класс UserTest , это и есть класс, в котором мы будем покрывать наш код тестами. Приступим:

    Наш первый @Test

    Создадим наш первый @Test метода getAllUsers() – это метод который должен вернуть всех пользователей. Тест будет выглядеть примерно так: Тут мы создаем несколько тестовых пользователей -> создаем список expected в который поместим пользователей которых нам вернет метод getAllUsers() -> создадим список actual в который поместим пользователей которых мы предполагаем что метод getAllUsers() Assert.assertEquals(actual, expected) ему мы и передадим списки, инспектируемый и актуальный. Этот метод проверит объекты в предоставленных списках и выдаст результат теста. Метод будет сравнивать все поля объектов, даже пройдется по полям родителей, если есть наследование. Запускаем первый тест. JUnit part I - 6Тест выполнен успешно. Теперь попробуем сделать так, чтобы тест был провален, для этого нам нужно изменить один из списков теста, сделаем это путем, закомментировав добавление одного пользователя в список actual , запускаем тест и видим следующее: JUnit part I - 7Теперь мы можем немного разобрать причину провала теста. Тут мы видим, что в инспектируемом списке больше пользователей чем в актуальном. Это и есть причина провала. А в main мы можем проверить так? JUnit : main = 1 : 0. Давайте посмотрим как будет выглядеть тест, если в нем будут полностью разные объекты, сделаем это так: вот что будет в консоли: JUnit part I - 8тут сразу видно что в сравниваемых списках разные пользователи, еще мы можем кликнуть на &ltClick to see difference> получим такое окно, где можно посмотреть подробно с какими данными у нас проблема. IDEA подсветит все поля в которых есть различия. JUnit part I - 9main такое может? — нет. JUnit : main = 2 : 0 Ну что, пойдем дальше у нас еще куча методов, которые нужно покрыть тестами ), но подождите, а ведь будет не плохо, проверить, а не будет ли нам метод getAllUsers() возвращать null , ведь примерно так нас на задачах JavaRush ловит валидатор ). Сделаем это, делов то на три копейки … Да, да примерно так валидатор ловит наш говно код на null 😉 Теперь запустим этот тест, и посмотрим, что он нам покажет. А покажет он ошибку, как . как же тут можно было допустить ошибку теста))) JUnit part I - 10И тут мы можем пожинать первые плоды покрытия своего кода тестами. Как вы помните, поле allUsers мы инициализировали в конструкторе, и значит при вызове метода getAllUsers() , мы обратимся к объекту, который еще не был инициализирован. Будем править, уберем инициализацию из конструктора, и сделаем ее при объявлении поля. Запустим тест, теперь все хорошо. JUnit part I - 11не думаю что в main легко будет отловить NPE, думаю вы согласитесь что счет JUnit : main = 3 : 0 Дальше я все методы покрою тестами, и дам вам посмотреть, как это будет выглядеть. Теперь класс тестов у нас выглядит так: Да не маленький получился, а что же будет при работе с большими проектами. Что же тут можно сократить, оценив все можно заметить, что тестовые данные мы создаем в каждом тесте, и тут нам на помощь приходят аннотации. Возьмем @Before — Аннотация @Before указывает на то, что метод будет выполнятся перед каждым тестируемым методом @Test . Вот так теперь будет выглядеть наш класс тестов с аннотацией @Before : Ну как вам, уже веселее и легче читать 😉 Вот список аннотаций для JUnit с ними однозначно жить проще. Основные методы класса Assert для проверки: Вот так мы можем добавить зависимость JUnit 4.12 в Maven продолжение тут -> JUnit part II

    Тестирование JUnit. Примеры

    В системах программирования на языке Java, в том числе Java Eclipse, введены средства тестирования, которые предусматривают создание и использование автоматических тестов для разработанных программ. Правильно построенное тестирование позволяет свести количество ошибок в программе к нулю.

    При разработке современных программных систем популярна методология TDD (Test Driven Development). Основная идея применения TDD заключается в том, что сначала разрабатываются тесты на желаемое поведение программы (системы), а затем пишется рабочий программный код, который с помощью средств рефакторинга проверяется уже созданными тестами. Как следствие, заранее подготовленные тесты позволяют избежать сдачи в эксплуатацию программы, содержащей ошибки.

    Для проведения тестирования в Java Eclipse применяется так называемое JUnit-тестирование, которое является фактическим стандартом для тестирования Java-приложений. JUnit — это обычная среда (framework), позволяющая писать тесты в виде классов Java. Эти классы могут запускаться как единое целое с помощью средства выполнения тестов.

    JUnit имеет много расширений, которые внедрены во всех современных средах разработки на языке Java. На сегодняшний день JUnit используется в таких направлениях как:

    • модульное тестирование;
    • тестирование веб-сайтов;
    • автоматическая подстановка интерфейсов для тестирования;
    • тестирование параллельного выполнения приложений;
    • тестирование производительности.

    JUnit-тестирование используется многими группами тестировщиков (quality assurance) на специально-разработанных веб-сайтах, обеспечивающих автоматизацию применения сквозных функциональных тестов.

    В Java Eclipse использование механизма JUnit не требует работающего сервера приложений или действующей базы данных.

    2. Понятие тестового примера ( Test Case ). Класс, содержащий тестовые примеры (методы). Запуск теста

    При использовании механизма JUnit, рассматриваются две основные единицы (классы):

    • класс, методы которого нужно протестировать;
    • класс, который представляет собой JUnit тест. Этот класс построен по специальным правилам.

    Значит, JUnit-тест представляет собой специально разработанный класс, содержащий методы, которые тестируют программный код других классов. В системе Java Eclipse этот класс создается с помощью команды JUnit Test Case (смотрите примеры далее).

    Класс содержит ряд методов. Каждый из методов тестирующего класса рассматривается как отдельный тестовый пример — Test Case .

    Тестовые примеры (методы) JUnit-класса могут быть объявлены с использованием следующих аннотаций (рассматривается версия Java Eclipse 2018-09):

    • @BeforeAll — статический метод (тестовый пример), объявленный с этой инструкцией, вызывается в начале тестирования;
    • @AfterAll — статический метод, объявленный с этой инструкцией, вызывается в конце тестирования. Здесь можно задавать, например, освобождение ресурсов используемых при тестировании;
    • @Test — метод, который объявлен с этой инструкцией, является тестовым примером. Он содержит непосредственно код тестирования. Этот метод использует методы класса org.junit.jupiter.api.Assertions . В этом методе используются методы сравнения, которые начинаются с префикса assert*() . Например, для сравнения двух значений любого примитивного типа используется перегруженный метод assertEquals() . В одном тестовой классе JUnit может быть произвольное количество методов (примеров) тестирования, которые объявлены с аннотацией @Test ;
    • @BeforeEach — вызывается перед вызовом каждого тестового примера (метода), который объявлен с аннотацией @Test ;
    • @AfterEach — вызывается после завершения каждого тестового примера (метода), который объявлен с аннотацией @Test .

    Допускается произвольное количество методов со всеми вышеприведенными аннотациями.

    В наиболее общем случае, примерный вид класса, сгенерированный системой Java Eclipse, может быть таким

    В методе testMethod() нужно вставить собственный код работы методов другого класса (смотрите примеры ниже). После этого, можно запускать тест с помощью специальной команды Java Eclipse.

    3. Пример, демонстрирующий использование JUnit для проверки правильности решения квадратного уравнения. Пошаговая инструкция
    3.1. Условие задачи

    Используя технологию JUnit разработать Unit-тест для проверки работы класса SquareEquation , который содержит средства решения квадратного уравнения.

    Тест должен размещаться в классе SquareEquationTest и содержать соответствующие методы проверки правильности полученного решения.

    Корни квадратного уравнения возвращаются в виде экземпляра класса Roots .

    Класс SquareEquationTest содержит соответствующие средства решения квадратного уравнения.

    3.2. Решение
    3.2.1. Создание классов SquareEquation и Roots

    Перед созданием классов создается проект стандартным для Java Eclipse способом. В проект добавляются два класса с именами SquareEquation и Roots и формируется код этих классов. Соответственно создается файл SquareEquation.java . После выполненных действий, окно Java Eclipse имеет вид как показано на рисунке 1.

    Окно Java Eclipse. Классы Roots и SquareEquation

    Рисунок 1. Окно Java Eclipse. Классы Roots и SquareEquation

    Классы имеют следующее назначение:

    • Roots — используется для сохранения корней квадратного уравнения (x1, x2) в случае, если уравнение имеет эти корни;
    • SquareEquation — реализует методы решения квадратного уравнения.
    • Класс SquareEquation имеет следующие составляющие:
    • внутренние переменные a , b , c что являются коэффициентами квадратного уравнения;
    • конструктор, инициализирует значения внутренних полей a , b , c ;
    • метод Solution() , который возвращает экземпляр класса Roots в случае, если уравнение имеет корни. Если уравнение не имеет корней, то генерируется исключение типа ArithmeticException .

    Полный текст классов Roots и SquareEquation выглядит так

    После создания классов можно переходить к созданию Unit-теста.

    3.2.2. Создание класса, необходимого для реализации JUnit тестирования

    Для создания Unit-теста в Java Eclipse нужно выбрать последовательность команд

    как показано на рисунке 2.

    Java Eclipse. Команда создания Unit-теста в Java

    Рисунок 2. Команда создания Unit-теста в Java

    В результате откроется окно «New JUnit Test Case» (рисунок 3) в котором нужно указать необходимую информацию о тесте.

    Java Eclipse. JUnit-тестирование. Окно создания нового теста

    Рисунок 3. Окно создания нового теста. Указание параметров теста

    При создании окна система автоматически заполнит некоторые поля. В окне указывается следующая информация.

    1. В поле Source Folder указывается имя папки с исходными файлами проекта. Система автоматически «подтягивает» имя папки с текущим проектом.
    2. В поле Name — задается имя класса, в котором будут размещаться методы тестирования (с аннотациями @BeforeAll , @Test т.д.). В нашем случае предлагается SquareEquationTest (к имени класса SquareEquation добавляется окончание Test ). Предложенное имя можно оставить без изменений.
    3. В поле «Which method stubs would you like to create?» с помощью опций указываются методы, которые нужно сформировать в классе. Согласно выбранной опции формируются следующие методы:
      • setUpBeforeClass() — имя метода аннотации @BeforeAll (см п.2);
      • setUp() — имя метода аннотации @BeforeEach ;
      • tearDownAfterClass() — имя метода аннотации @AfterAll ;
      • tearDown() — имя метода аннотации @AfterEach .

    Для нашего теста достаточно активировать только одно поле setUp() . Это позволит добавить метод аннотации @BeforeEach , что будет вызываться перед тестом. В этом методе будет создаваться экземпляр класса SquareEquation (смотрите коды ниже).

    1. В поле «Class under test:» указывается имя класса SquareEquation , методы которого нужно протестировать.
    2. Дополнительно задаются другие опции:
      • выбор модели тестирования «New JUnit Jupiter test» ;
      • возможность добавления комментариев;
      • другое.

    В нашем случае можно оставить все без изменений.

    После выбора кнопки «Next >» откроется следующее окно «New JUnit Test Case» в котором нужно задать перечень методов, которые будут тестироваться. Вид окна показан на рисунке 4. Избранные методы будут объявляться с аннотацией @Test . Это непосредственно тестовые примеры. Все остальные опции в окне можно оставить по умолчанию и перейти далее с помощью кнопки Finish .

    Java Eclipse. JUnit-тестирование. Окно выбора методов для тестирования

    Рисунок 4. Выбор методов которые будут тестироваться. Выбран метод Solution()

    3.2.3. Код класса SquareEquationTest

    После создания JUnit теста с помощью оконного интерфейса Java Eclipse сгенерирует код класса SquareEquationTest

    В этом коде сформированы два метода:

    • метод setUp() с аннотацией @BeforeEach . Этот метод будет вызываться первым при тестировании;
    • метод testSolution() с аннотацией @Test . В этом методе будет помещен непосредственно тест функции Solution() класса SquareEquation .
    3.2.4. Модификация класса SquareEquationTest . Программирование теста

    Чтобы протестировать работу класса SquareEquation нужно создать экземпляр этого класса. Поэтому, в текст класса SquareEquationTest дополнительно вводится ссылка на класс SquareEquation

    Затем в методе setUp() создается экземпляр класса SquareEquation , на который указывает ссылка se

    После этого, в методе TestSolution() вписывается код проверки правильности полученного решения для заданных коэффициентов

    в экземпляре se

    Проверка решения осуществляется с помощью перегруженного метода assertEquals() класса org.junit.jupiter.api.Assertions . Происходит сравнение решения в переменных rt.x1 и rt.x2 с заранее вычисленным (вручную) решением текущего варианта (a = 2, b = 1, c = -3) квадратного уравнения. Если есть совпадение, то решение правильное. О том, что решение правильное, нужна сказать система после запуска теста (следующий пункт).

    В целом текст всего модуля тестирования следующий

    3.3. Запуск теста средствами Java Eclipse. Просмотр результата тестирования

    После того, как сформирован код класса SquareEquationTest , разработанный тест можно запускать на выполнение. Это осуществляется командами

    или вызовом последовательности комбинаций клавиш Alt + Shift + X, T как показано на рисунке 5.

    Java Eclipse. Запуск JUnit-теста

    Рисунок 5. Запуск JUnit-теста

    Существует другой способ с помощью команды быстрого меню (рисунок 6).

    Java Eclipse. Команда запуска JUnit-теста

    Рисунок 6. Команда запуска JUnit-теста

    После запуска в левой части Java Eclipse в окне JUnit отобразится результат тестирования (рисунок 7).

    Java Eclipse. Результат JUnit-тестирования

    Рисунок 7. Результат тестирования

    Как видно из результата, получено подтверждение правильного решения, то есть тест сдан. Значит, метод Solution() класса SquareEquation дает правильный результат для случая когда a = 2, b = 1, c = -3.

    Если в методе TestSolution() при вызове assertEquals() специально указать неверный ответ, например

    то после повторного запуска теста, в окне JUnit отобразится ошибка как видно на рисунке 8. Хотя это не ошибка программы, а умышленная ошибка в тесте.

    Java Eclipse. JUnit-тестирование. Демонстрация провала теста

    Рисунок 8. Демонстрация провала теста

    4. Пример, демонстрирующий последовательность вызова методов в JUnit-тесте

    Пусть в системе Java Eclipse создан класс с именем TestClass для проведения тестирования некоторого другого класса. В данном примере, суть тестируемого класса не имеет значения.

    Программный код класса следующий

    После запуска теста на выполнение, программа выдаст следующий результат

    Проанализировав этот результат, можно прийти к выводу, что методы в тестовом классе вызываются в порядке, определяемом их аннотациями. Первыми вызываются методы аннотации @BeforeAll .

    Следующими рассматриваются тестовые примеры, объявленные с аннотацией @Test . Перед каждым тестовым примером вызываются все методы с аннотацией @BeforeEach . После каждого тестового примера вызываются все методы с аннотацией @AfterEach .

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *