Переопределение зависимости в тесте Micronaut - PullRequest
0 голосов
/ 03 ноября 2018

Я тестирую класс Micronaut, в который вставлен боб. В моем тесте я предоставляю @MockBean класс для его переопределения. Тем не менее, похоже, что Micronaut все еще вводит реальную зависимость.

@MicronautTest
public class ClassUnderTestTest {

    @Inject ClassUnderTest classUnderTest;

    @Test
    public void test() {

    }

    @MockBean
    Dependency dependency() {
        return mock(Dependency.class);
    }

}

Я загрузил минимальную копию в Github: https://github.com/crummy/micronaut-test-dependencies. Реальная зависимость выдает исключение, и тест тоже. Я бы не ожидал, что это произойдет из-за моего @MockBean.

Если я изменю аннотацию на @MockBean(Dependency.class), я получу эту ошибку: Message: No bean of type [di.failure.example.Dependency] exists. Это кажется мне еще более запутанным - теперь это не решает мою реальную или ложную зависимость?

1 Ответ

0 голосов
/ 03 ноября 2018

Инъекция фиктивного компонента с аннотацией @MockBean работает, если ваша зависимость в ClassUnderTest представлена ​​интерфейсом. Допустим, Dependency - это простой интерфейс, подобный:

package di.failure.example;

public interface Dependency {
    void run();
}

Ваше приложение может предоставить реализацию для этого интерфейса, которая называется DependencyImpl:

package di.failure.example;

import javax.inject.Singleton;

@Singleton
public class DependencyImpl implements Dependency {
    @Override
    public void run() {
        throw new RuntimeException("I don't want this to load!");
    }
}

Теперь для целей теста вы можете определить макет, который заменит DependencyImpl:

package di.failure.example;

import io.micronaut.test.annotation.MicronautTest;
import io.micronaut.test.annotation.MockBean;
import org.junit.jupiter.api.Test;

import javax.inject.Inject;

import static org.mockito.Mockito.mock;

@MicronautTest
public class ClassUnderTestTest {

    @Inject
    ClassUnderTest classUnderTest;

    @Test
    public void test() {
        classUnderTest.run();
    }

    @MockBean(DependencyImpl.class)
    public Dependency dependency() {
        return mock(Dependency.class);
    }

}

Этот тест выполняется, и вместо DependencyImpl.

используется макет, возвращенный методом dependency().

Использование @Replaces аннотации

Как и Sergio , упомянутое в разделе комментариев, вы можете заменить зависимость bean-компонента на основе класса, используя аннотацию @Replaces. Рассмотрим следующий пример:

package di.failure.example;

import io.micronaut.context.annotation.Replaces;
import io.micronaut.test.annotation.MicronautTest;
import org.junit.jupiter.api.Test;

import javax.inject.Inject;
import javax.inject.Singleton;

@MicronautTest
public class ClassUnderTestTest {

    @Inject
    ClassUnderTest classUnderTest;

    @Test
    public void test() {
        classUnderTest.run();
    }

    @Replaces(Dependency.class)
    @Singleton
    public static class MockDependency extends Dependency {

        public MockDependency() {
            System.out.println("MockDependency.<init>");
        }

        @Override
        void run() {
            System.out.println("Does not throw any exception...");
        }
    }
}

В этом примере мы определили класс MockDependency и даем указание механизму DI Микронавта заменить боб Dependency на MockDependency. Однако есть одна важная вещь, о которой нам нужно помнить - поскольку наш MockDependency расширяет класс Dependency, вызывается родительская конструкция. Пример, который вы показали в вопросе, не будет работать в этом случае, потому что Dependency.<init> выдает RuntimeException и тест не пройден. В этом модифицированном примере я использовал такой класс:

package di.failure.example;

import javax.inject.Singleton;

@Singleton
public class Dependency {

    public Dependency() {
        System.out.println("Dependency.<init>");
    }

    void run() {
        throw new RuntimeException("I don't want this to load!");
    }
}

Когда я запускаю тест, он проходит, и я вижу следующий вывод консоли:

Dependency.<init>
MockDependency.<init>
Does not throw any exception...

Основное отличие от @MockBean состоит в том, что в случае @Replaces вы используете конкретный объект класса. В качестве обходного пути (если нам действительно нужен фиктивный объект Mockito) нужно создать макет внутри и делегировать вызовы этому объекту, что-то вроде этого:

@Replaces(Dependency.class)
@Singleton
public class MockDependency extends Dependency {

    private final Dependency delegate;

    public MockDependency() {
        this.delegate = mock(Dependency.class);
    }

    @Override
    void run() {
        delegate.run();
    }
}
...