Испорченное исключение EhCache NullPointerException в тесте JUnit 5 - PullRequest
1 голос
/ 13 марта 2019

Я пишу юнит-тесты для сервиса, который хочу протестировать. Несколько методов пытаются извлечь значения из EhCache. Я пытался насмехаться над ними с помощью Mockito и просто get(String key) метод Cache возвращает ноль, так как я хочу игнорировать кэширование для этих тестов.

Мой тестовый класс:

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.util.Arrays;
import java.util.List;

import javax.annotation.Resource;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import com.jysk.dbl.esldataservice.model.Preis;
import com.jysk.dbl.esldataservice.service.PreisService;
import com.jysk.dbl.esldataservice.service.external.PimDataService;
import com.jysk.dbl.esldataservice.service.external.SapCarService;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;

public class PreisServiceTest {

    @Mock
    private SapCarService sapCarService;
    @Mock
    private ArticleDataService articleDataService;
    @Mock
    private CacheManager cacheManager;
    @Mock
    private Cache cache;

    @InjectMocks
    @Resource
    private PreisService preisService;

    @BeforeEach
    public void setup() {

        MockitoAnnotations.initMocks(this);

        when(this.cacheManager.getCache(anyString())).thenReturn(this.cache);
        when(this.cache.get(anyString())).then(null);
    }

    protected static final String TEST_STORE_IDENTIFIER = "1234";
    private static final String ARTICLE_IDENTIFIER_1 = "12345001";
    private static final String ARTICLE_IDENTIFIER_2 = "54321001";

    private final Preis p1 = new Preis(ARTICLE_IDENTIFIER_1, 10.00, 15.00, "01", "01", "01");
    private final Preis p2 = new Preis(ARTICLE_IDENTIFIER_2, 20.00, 25.00, "02", "02", "02");

    @Test
    void testGetPreisReturnsOneCorrectPreis() {

        when(this.sapCarService.getPreise(Arrays.asList(ARTICLE_IDENTIFIER_1), TEST_STORE_IDENTIFIER, true)).thenReturn(Arrays.asList(this.p1));

        final List<Preis> actual = this.preisService.getPreis(ARTICLE_IDENTIFIER_1, TEST_STORE_IDENTIFIER);

        verify(this.sapCarService, times(1)).getPreise(anyList(), anyString(), anyBoolean());

        assertNotNull(actual);
        assertEquals(1, actual.size());
        assertEquals(this.p1, actual);
    }
}

Моя реализация:

private Preis searchPreisInCache(String key) {

    final Element preisOptional = this.cacheManager.getCache("preis").get(key); // NPE here
    if (preisOptional != null) {

        final Preis preis = (Preis) preisOptional.getObjectValue();
        logger.info(String.format("Preis with key '%s' found in cache 'preis'.", key));
        return preis;
    }
    return null;
}

StackTrace показало, что NPE выбрасывается внутри net.sf.ehcache.Cache класса:

public final Element get(Object key) throws IllegalStateException, CacheException {
    getObserver.begin(); // NPE thrown here
    checkStatus();

    if (disabled) {
        getObserver.end(GetOutcome.MISS_NOT_FOUND);
        return null;
    }

    Element element = compoundStore.get(key);
    if (element == null) {
        getObserver.end(GetOutcome.MISS_NOT_FOUND);
        return null;
    } else if (isExpired(element)) {
        tryRemoveImmediately(key, true);
        getObserver.end(GetOutcome.MISS_EXPIRED);
        return null;
    } else if (!skipUpdateAccessStatistics(element)) {
        element.updateAccessStatistics();
    }
    getObserver.end(GetOutcome.HIT);
    return element;
}

Есть ли простое решение этой проблемы, если я просто хочу, чтобы Cache возвращал null, когда бы он ни вызывался?

1 Ответ

1 голос
/ 21 марта 2019

Mockito не может издеваться над final методами и классами без какой-либо настройки. Как указал Морфик, это возможно с Mockito v2.x, как объяснено здесь и здесь .

По сути, вам нужно добавить текстовый файл с именем org.mockito.plugins.MockMaker в каталог src/test/resources/mockito-extensions с содержимым mock-maker-inline и tada, Mockito может высмеивать конечные методы и классы.

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

...