Как запустить тестовое задание Junit для FileWriter?

Вопрос или проблема

Ниже приведённый код предназначен для записи содержимого в файл.

          public String saveSecretToFile() {
            File file = new File("test.txt");
            try (BufferedWriter writer = new BufferedWriter(new FileWriter(file))) {
                writer.write(secretValue);//secretValue — это содержимое
            } catch (IOException e) {
                LOGGER.error("Ошибка записи в файл: {}", e.getMessage());
            }
          }

Блок catch не охвачен в тестовом случае JUnit.

    @InjectMocks
    private MyHelper myHelper;

        try (MockedStatic<FileOutputStream> mockedStatic = Mockito.mockStatic(FileOutputStream.class)) {
            FileWriter mockFile = mock(FileWriter.class);
            OutputStream mockFileWriter = Mockito.spy(new FileOutputStream(String.valueOf(mockFile)));
            
            doThrow(new IOException("Ошибка ввода-вывода")).when(mockFileWriter).write(anyString().getBytes());
            
            IOException thrown = assertThrows(IOException.class, () -> myHelper.saveToFile("secretName"));  
            
        }

Я прочитал, что замена BufferedWriter на мок не сработает, поскольку внутренний OutputStream FileWriter должен быть замокирован. Ожидаемый результат теста — выброс исключения. Но результат показывает java.lang.AssertionError: ожидалось, что будет выброшено java.io.IOException, но ничего не было выброшено

Я бы сделал что-то подобное:

1- Сначала реализуйте запись в мок.

public class MyHelper {
    private final BufferedWriter writer;

    // Конструктор для внедрения зависимостей
    public MyHelper(BufferedWriter writer) {
        this.writer = writer;
    }

    public void saveSecretToFile(String secretValue) {
        try {
            writer.write(secretValue);
            writer.flush();
        } catch (IOException e) {
            LOGGER.error("Ошибка записи в файл: {}", e.getMessage());
            throw new RuntimeException("Ошибка записи в файл", e); // Исключение для обозначения ошибки
        }
    }
}

2- Напишите оба теста: для успешного сценария и для сценария с IOException, используя мок моего предыдущего сервиса, и проверьте, что метод был вызван.

Есть проверка, которая подтверждает, что метод был вызван.

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;

import java.io.BufferedWriter;
import java.io.IOException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;

class MyHelperTest {
    @Mock
    private BufferedWriter mockWriter;

    @InjectMocks
    private MyHelper myHelper;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void testSaveSecretToFile_Success() throws IOException {
        // Подготовка
        String secretValue = "secret";

        // Действие
        myHelper.saveSecretToFile(secretValue);

        // Проверка
        verify(mockWriter, times(1)).write(secretValue);
        verify(mockWriter, times(1)).flush();
    }

    @Test
    void testSaveSecretToFile_ThrowsException() throws IOException {
        // Подготовка
        String secretValue = "secret";
        doThrow(new IOException("Ошибка ввода-вывода")).when(mockWriter).write(anyString());

        // Действие и проверка
        Exception exception = assertThrows(RuntimeException.class, () -> myHelper.saveSecretToFile(secretValue));
        assertTrue(exception.getCause() instanceof IOException);
        assertEquals("Ошибка записи в файл", exception.getMessage());
        verify(mockWriter, times(1)).write(secretValue); // Проверка, что запись была вызвана перед исключением
    }
}

Ответ или решение

Чтобы протестировать метод saveSecretToFile, который использует BufferedWriter для записи данных в файл, мы можем использовать библиотеку JUnit и Mockito. В вашем коде основной проблемой является то, что метод записи не защищен от обработки исключений ввода-вывода, поэтому необходимо немного изменить метод и тест сделать корректным.

1. Изменение метода saveSecretToFile

Чтобы улучшить обработку исключений и упростить тестирование, сделаем следующий шаг:

  • Добавим возможность передачи BufferedWriter в конструктор, чтобы мы могли легко его замокировать в тестах.
  • Будем выбрасывать RuntimeException, если произошла ошибка, что даст возможность тесту поймать это исключение.
import java.io.BufferedWriter;
import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class MyHelper {
    private final BufferedWriter writer;
    private static final Logger LOGGER = LoggerFactory.getLogger(MyHelper.class);

    // Конструктор для внедрения зависимости
    public MyHelper(BufferedWriter writer) {
        this.writer = writer;
    }

    public void saveSecretToFile(String secretValue) {
        try {
            writer.write(secretValue);
            writer.flush();
        } catch (IOException e) {
            LOGGER.error("Error writing to file: {}", e.getMessage());
            throw new RuntimeException("Error writing to file", e); // Исключение для указания на ошибку
        }
    }
}

2. Написание юнит-тестов

Теперь мы можем написать тесты для обработки как успешного, так и неудачного сценариев:

import static org.mockito.Mockito.*;
import static org.junit.jupiter.api.Assertions.*;
import java.io.BufferedWriter;
import java.io.IOException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;

class MyHelperTest {
    @Mock
    private BufferedWriter mockWriter;

    @InjectMocks
    private MyHelper myHelper;

    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }

    @Test
    void testSaveSecretToFile_Success() throws IOException {
        // Arrange
        String secretValue = "secret";

        // Act
        myHelper.saveSecretToFile(secretValue);

        // Assert
        verify(mockWriter, times(1)).write(secretValue);
        verify(mockWriter, times(1)).flush();
    }

    @Test
    void testSaveSecretToFile_ThrowsException() throws IOException {
        // Arrange
        String secretValue = "secret";
        doThrow(new IOException("IO error")).when(mockWriter).write(anyString());

        // Act & Assert
        Exception exception = assertThrows(RuntimeException.class, () -> myHelper.saveSecretToFile(secretValue));
        assertTrue(exception.getCause() instanceof IOException);
        assertEquals("Error writing to file", exception.getMessage());
        verify(mockWriter, times(1)).write(secretValue); // Проверка, что метод write был вызван перед исключением
    }
}

Пояснение к тестам:

  1. Тест успеха (testSaveSecretToFile_Success):

    • Проверяет, что метод write был вызван один раз с правильным значением.
    • Проверяет, что метод flush был также вызван.
  2. Тест на выбрасывание исключения (testSaveSecretToFile_ThrowsException):

    • Настраивает mockWriter так, чтобы при вызове write выбрасывалось IOException.
    • Проверяет, что в результате вызова метода будет возбуждено RuntimeException, и в качестве причины этого исключения будет IOException.
    • Убедитесь, что метод write был вызван один раз перед тем, как исключение было выброшено.

Следуя этим шагам, вы сможете корректно протестировать свой метод saveSecretToFile, включая сценарии, когда происходит ошибка.

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

Капча загружается...