Java 从单元测试中消除null的方法

Java 从单元测试中消除null的方法,java,unit-testing,mockito,Java,Unit Testing,Mockito,当我尝试连接此url时,我在方法中使用url变量获取NPE(使用givenTitle将其解析为null) 我应该怎么做才能使url不为空 堆栈跟踪: java.lang.NullPointerException at java.base/java.util.regex.Matcher.getTextLength(Matcher.java:1769) at java.base/java.util.regex.Matcher.reset(Matcher.java:416) a

当我尝试连接此url时,我在方法中使用
url
变量获取NPE(使用
givenTitle
将其解析为null)

我应该怎么做才能使url不为空

堆栈跟踪:

java.lang.NullPointerException
    at java.base/java.util.regex.Matcher.getTextLength(Matcher.java:1769)
    at java.base/java.util.regex.Matcher.reset(Matcher.java:416)
    at java.base/java.util.regex.Matcher.<init>(Matcher.java:253)
    at java.base/java.util.regex.Pattern.matcher(Pattern.java:1130)
    at java.base/java.util.Formatter.parse(Formatter.java:2698)
    at java.base/java.util.Formatter.format(Formatter.java:2653)
    at java.base/java.util.Formatter.format(Formatter.java:2607)
    at java.base/java.lang.String.format(String.java:2734)
    at bookstore.scraper.book.booksource.empik.EmpikSource.concatUrlWithTitle(EmpikSource.java:154)
    at bookstore.scraper.book.booksource.empik.EmpikSource.getMostPreciseBook(EmpikSource.java:74)
    at bookstore.scraper.book.booksource.empik.EmpikSourceTest.getMostPreciseBook(EmpikSourceTest.java:66)
备选案文1:

将新方法
getCategory
添加到
empikUrlProperties

公共字符串getCategory(CategoryType类别){
断言(类别!=null);
开关(类别){
案件类别类型.犯罪:
返回getCrime();
案例类别类型。畅销书:
返回畅销书();
案例类别类型。传记:
返回get传记();
case CategoryType.FANTASY:
返回getFantasy();
案例类别类型.GUIDES:
返回getGuides();
案例分类类型。最精确的书籍:
返回getMostPreciseBook();
案例类别类型.ROMANCES:
返回getRomances();
违约:
抛出新的IllegalArgumentException(“意外类别:”+类别)
}
}
对于现在的测试,您只需模拟它

when(empikUrlProperties.getCategory(CategoryType.CRIME))
.然后返回(“https://www.empik.com/%s,%s,ksiazka-p”);

备选案文2:

更改构造函数以接受来自外部的映射。 这意味着您将映射创建移动到另一个类/函数, 它在其他地方创建了地图

在这种情况下,您需要手动注入模拟(不要使用
@InjectMocks
), 因为你不应该嘲笑一个集合

(对于这一部分,我不确定@Autowired将如何处理它,也许您需要将它包装到另一个类中。setter可能是更合适的选项。)

@Autowired
公共EmpikSource(EmpikUrlProperties EmpikUrlProperties,JSoupConnector JSoupConnector,Map categoryToEmpikURL){
this.empikUrlProperties=empikUrlProperties;
this.jSoupConnector=jSoupConnector;
categoryToEmpikURL=categoryToEmpikURL;
}
选项1:

将新方法
getCategory
添加到
empikUrlProperties

公共字符串getCategory(CategoryType类别){
断言(类别!=null);
开关(类别){
案件类别类型.犯罪:
返回getCrime();
案例类别类型。畅销书:
返回畅销书();
案例类别类型。传记:
返回get传记();
case CategoryType.FANTASY:
返回getFantasy();
案例类别类型.GUIDES:
返回getGuides();
案例分类类型。最精确的书籍:
返回getMostPreciseBook();
案例类别类型.ROMANCES:
返回getRomances();
违约:
抛出新的IllegalArgumentException(“意外类别:”+类别)
}
}
对于现在的测试,您只需模拟它

when(empikUrlProperties.getCategory(CategoryType.CRIME))
.然后返回(“https://www.empik.com/%s,%s,ksiazka-p”);

备选案文2:

更改构造函数以接受来自外部的映射。 这意味着您将映射创建移动到另一个类/函数, 它在其他地方创建了地图

在这种情况下,您需要手动注入模拟(不要使用
@InjectMocks
), 因为你不应该嘲笑一个集合

(对于这一部分,我不确定@Autowired将如何处理它,也许您需要将它包装到另一个类中。setter可能是更合适的选项。)

@Autowired
公共EmpikSource(EmpikUrlProperties EmpikUrlProperties,JSoupConnector JSoupConnector,Map categoryToEmpikURL){
this.empikUrlProperties=empikUrlProperties;
this.jSoupConnector=jSoupConnector;
categoryToEmpikURL=categoryToEmpikURL;
}

空url来自地图类别ToEmpikURL。此映射由方法createCategoryToEmpikURLMap()创建。但是你没有发布tat方法。@JBNizet Ye,但是这个方法是私有的,因为我不需要在外部发布。我在考虑一些变通办法,但没有想出任何好主意。我应该把它换成公共的吗?@JBNizet啊,对不起。误解了你的评论。我已经添加了这个方法。所以,您需要再次查看您的代码在做什么。它在构造器内部的映射中填充来自属性的值。属性是一个构造函数参数。在测试中何时调用该构造函数?由于InjectMocks注释,它由Mockito调用。什么作为参数传递?嘲弄。默认情况下,mock返回null。因此构造函数在映射中存储空值。我会删除无用的映射,直接从属性中获取值。通过这种方式,您可以在调用测试中的方法之前告诉您的模拟返回什么。或者更好的是,甚至不要对属性使用模拟,因为它是一个POJO,您可以自己填充。空url来自Map categoryToEmpikURL。此映射由方法createCategoryToEmpikURLMap()创建。但是你没有发布tat方法。@JBNizet Ye,但是这个方法是私有的,因为我不需要在外部发布。我在考虑一些变通办法,但没有想出任何好主意。我应该把它换成公共的吗?@JBNizet啊,对不起。误解了你的评论。我已经添加了这个方法。所以,您需要再次查看您的代码在做什么。它在构造器内部的映射中填充来自属性的值。属性是一个构造函数参数。在测试中何时调用该构造函数?由于InjectMocks注释,它由Mockito调用。什么作为参数传递?嘲弄。默认情况下,mock返回null。因此构造函数在映射中存储空值。我会删除无用的映射,直接从属性中获取值。这样,您可以在调用被测方法之前告诉您的mock要返回什么。或者更好的是,甚至不要对属性使用mock,因为它是一个POJO,您可以只填充自己
package bookstore.scraper.book.booksource.empik;

import bookstore.scraper.book.Book;
import bookstore.scraper.enums.CategoryType;
import bookstore.scraper.urlproperties.EmpikUrlProperties;
import bookstore.scraper.utilities.JSoupConnector;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.util.List;

import static bookstore.scraper.dataprovider.EmpikBookProvider.prepare15CrimeBooks;
import static bookstore.scraper.dataprovider.EmpikBookProvider.prepareMostPreciseBook;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.when;

@RunWith(MockitoJUnitRunner.class)
public class EmpikSourceTest {

    @Mock
    JSoupConnector jSoupConnector;
    @Mock
    EmpikUrlProperties empikUrlProperties;

    @InjectMocks
    EmpikSource empikSource;

    /*@Before
    public void setUp() {
        when(empikUrlProperties.getConcreteBook()).thenReturn(anyString());
    }
*/

    @Test
    public void getMostPreciseBook() throws IOException {
        File in = getFile("/empik/MostPreciseBookEmpik.html");
        Document empikDocument = Jsoup.parse(in, "UTF-8");

        when(jSoupConnector.connect(any())).thenReturn(empikDocument);
        when(empikUrlProperties.getConcreteBook()).thenReturn("https://www.empik.com/%s,%s,ksiazka-p");

        Book actualBooks = empikSource.getMostPreciseBook("W pustyni i w puszczy. Lektura z opracowaniem - Henryk Sienkiewicz");
        Book expectedBooks = prepareMostPreciseBook();

        assertEquals(expectedBooks, actualBooks);
    }

    private File getFile(String resourceName) {
        try {
            return new File(EmpikSourceTest.class.getResource(resourceName).toURI());
        } catch (URISyntaxException e) {
            throw new IllegalStateException(e);
        }
    }

}
java.lang.NullPointerException
    at java.base/java.util.regex.Matcher.getTextLength(Matcher.java:1769)
    at java.base/java.util.regex.Matcher.reset(Matcher.java:416)
    at java.base/java.util.regex.Matcher.<init>(Matcher.java:253)
    at java.base/java.util.regex.Pattern.matcher(Pattern.java:1130)
    at java.base/java.util.Formatter.parse(Formatter.java:2698)
    at java.base/java.util.Formatter.format(Formatter.java:2653)
    at java.base/java.util.Formatter.format(Formatter.java:2607)
    at java.base/java.lang.String.format(String.java:2734)
    at bookstore.scraper.book.booksource.empik.EmpikSource.concatUrlWithTitle(EmpikSource.java:154)
    at bookstore.scraper.book.booksource.empik.EmpikSource.getMostPreciseBook(EmpikSource.java:74)
    at bookstore.scraper.book.booksource.empik.EmpikSourceTest.getMostPreciseBook(EmpikSourceTest.java:66)
package bookstore.scraper.urlproperties;

import lombok.Getter;
import lombok.Setter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Getter
@Setter
@Component
@ConfigurationProperties("external.library.url.empik")
public class EmpikUrlProperties {

    private String mostPreciseBook;
    private String bestSellers;
    private String concreteBook;
    private String romances;
    private String biographies;
    private String crime;
    private String guides;
    private String fantasy;
}