【问题标题】:Testing Spring Controller class public method测试 Spring Controller 类公共方法
【发布时间】:2020-06-19 19:34:47
【问题描述】:

我需要在 Spring Controller 类中测试一个公共方法。不幸的是,我对 Spring 的理解微乎其微。我只知道有 2 个存储库类连接到数据库,我想测试的方法从两个数据库中检索记录,如示例中所示:

@Controller
public class CalculateController {
    static double ZERO = 0.00;
    static double PROFIT_BOUND = 0.01;
    static int DECIMAL_PLACES = 2;
    static double MARGIN_PERCENT = 10;
    static double PERCENT_DIVIDER = 100;
    static boolean renderTable = false;

    @Autowired (required = false)
    private StateRepository stateRepository;


    @Autowired (required = false)
    private ProductRepository productRepository;


    @Autowired (required = false)
    private InternationalTransportCostsRepository internationalTransportCostsRepository;
// some other code unrelated to topic

 public Double[] calculateProfitUSA(State state, Product product, Double finalPrice) {
        Double[] tab;
        if (getTax(state.getId(), product.getCategory()) == ZERO) {
            tab = calculateMarginWithoutTaxUSA(state, product, finalPrice);
        } else {
            tab = calculateMarginWithTaxUSA(state, product, finalPrice);
        }
        return tab;
    }
private Double[] calculateMarginWithTaxUSA(State state, Product product, Double finalPrice) {
        Double[] tab = new Double[3];
        double profit = calcUSA(state, product, finalPrice);
        double estimatedPrice = (product.getCurrentPrice() + state.getTransportCost());
        estimatedPrice = estimatedPrice + (estimatedPrice * (getTax(state.getId(), product.getCategory()) / PERCENT_DIVIDER));
        double margin = estimatedPrice * MARGIN_PERCENT / PERCENT_DIVIDER;
        estimatedPrice = estimatedPrice + (estimatedPrice * MARGIN_PERCENT / PERCENT_DIVIDER);

        double currentProfit = calcUSA(state, product, estimatedPrice);
        while (currentProfit < margin) {
            estimatedPrice += PROFIT_BOUND;
            currentProfit = calcUSA(state, product, estimatedPrice);
        }
        tab[0] = Precision.round(profit, DECIMAL_PLACES);
        tab[1] = Precision.round(estimatedPrice, DECIMAL_PLACES);
        tab[2] = Precision.round(margin, DECIMAL_PLACES);
        return tab;
    }
  public Double getTax(Long id, String categoryName) {
        if (categoryName.equals("Groceries")) {
            return stateRepository.findById(id).get().getGroceriesTax();
        } else if (categoryName.equals("Prepared-food")) {
            return stateRepository.findById(id).get().getPreparedFoodTax();
        } else if (categoryName.equals("Prescription-drug")) {
            return stateRepository.findById(id).get().getPrescriptionDrugTax();
        } else if (categoryName.equals("Non-prescription-drug")) {
            return stateRepository.findById(id).get().getNonPrescriptionDrugTax();
        } else if (categoryName.equals("Clothing")) {
            return stateRepository.findById(id).get().getClothingTax();
        } else if (categoryName.equals("Intangibles")) {
            return stateRepository.findById(id).get().getIntangiblesTax();
        }
        return null;
    }

我已经尝试在 google 上搜索 2 天的解决方案,但我失败了。我设法想出的是:

@WebMvcTest(CalculateController.class)
public class CalculateAlgorithmsTests {

    @InjectMocks
    private CalculateController calc;
    @Mock
    private StateRepository stateRepository;
    @Mock
    private ProductRepository productRepository;
    private List<State> states;
    private List<Product> products;


    @Test
    public void calculateProfitUSATest(){
        states = CSVReader.readStatesFromCSV("csvFiles/states.csv");
        products = CSVReader.readProductsFromCSV("csvFiles/products.csv");
        productRepository.saveAll(CSVReader.readProductsFromCSV("csvFiles/products.csv"));
        stateRepository.saveAll(CSVReader.readStatesFromCSV("csvFiles/states.csv"));
        State Connecticut = states.get(5);
        Product Apple = products.get(0);
        double [] expected = {25,12,32};
        Assertions.assertEquals(expected,calc.calculateProfitUSA(Connecticut,Apple,30.23));
    }
}

我从控制台得到这个响应:

java.util.NoSuchElementException: No value present

    at java.base/java.util.Optional.get(Optional.java:141)
    at com.taxcalc.controllers.CalculateController.getTax(CalculateController.java:215)
    at com.taxcalc.controllers.CalculateController.calculateProfitUSA(CalculateController.java:167)
    at com.taxcalc.CalculateAlgorithmsTests.calculateProfitUSATest(CalculateAlgorithmsTests.java:41)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:567)
    at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:117)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:184)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:180)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:127)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:135)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1507)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:38)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$5(NodeTestTask.java:139)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$7(NodeTestTask.java:125)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:135)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:123)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:122)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:80)
    at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:32)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
    at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:51)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:229)
    at org.junit.platform.launcher.core.DefaultLauncher.lambda$execute$6(DefaultLauncher.java:197)
    at org.junit.platform.launcher.core.DefaultLauncher.withInterceptedStreams(DefaultLauncher.java:211)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:191)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:128)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:69)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

【问题讨论】:

    标签: spring database testing junit controller


    【解决方案1】:

    在调用stateRepository.findById(id) 时,您给定的 id 似乎没有结果。

    你应该在你的测试中使用类似的东西来模拟它

    State someState = new State()
    Mockito.when(stateRepository.findById(someId)).thenReturn(Optional.of(someState))
    

    您需要确保someId 与您调用公共方法时使用的State 具有相同的值。

    然后可以省略 stateRepository.saveAll() 的调用,因为您将显式模拟行为而不是使用 CSV 中的给定数据。 这只是一个建议。 另一个建议是将这种逻辑转移到服务中。控制器通常定义 HTTP 相关的行为而不是业务逻辑。

    【讨论】:

    • 效果很好!我只需要将 someState 参数转换为 java.util.Optional.ofNullable 并且它工作得很好。谢谢。
    猜你喜欢
    • 2015-12-17
    • 1970-01-01
    • 1970-01-01
    • 2010-10-01
    • 1970-01-01
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多