Search code examples
javaspring-boottestingspring-boot-test

java.lang.UnsupportedOperationException: remove


I'm building an ATM with spring boot and I'm trying to include some tests with a mock repository. But when I try to use one of my test methods I get the "java.lang.UnsupportedOperationException: remove" error. I'm new to this so I really don't understand why this is happening.

It is the withdrawOneThousand() test method that isn't working. The getAllBills() test method works fine.

Before I made a mock repository in the test class I tried the test method directly on the database and It worked fine.

My controller:

@RestController
@RequestMapping("/bills")
public class BillController {

    private int calculate;

    @Autowired
    private BillRepository billRepository;



    @GetMapping(path = "/withdraw2/{value}")
    public String withdraw2(@PathVariable int value){
        List<Bill> billsToWithdraw = new ArrayList<>();
        List<Bill> dbCopy = (List<Bill>) billRepository.findAll();
        calculate = value;
        System.out.println("calc start " + calculate);

        try {
            while (calculate != 0) {

                if (calculate >= 1000 && searchBillList(1000, dbCopy)) {
                    billTransfer(1000, billsToWithdraw, dbCopy);

                } else if (calculate >= 500 && searchBillList(500, dbCopy)) {
                    billTransfer(500, billsToWithdraw, dbCopy);

                } else if (calculate >= 100 && searchBillList(100, dbCopy)) {
                    billTransfer(100, billsToWithdraw, dbCopy);

                }
                else {
                    return "No bills left";
                }
            }

            deleteFromDB(billsToWithdraw);

            System.out.println("returning");
            return "Bills: " + billsToWithdraw;

        } catch (NullPointerException e) {
            System.out.println(e);
            return "Not enough bills";
        } catch (Exception e){
            return e.toString();
        }

    }


    public void deleteFromDB(List<Bill> billsToWithdraw){
        billsToWithdraw.forEach((bill) -> billRepository.delete(bill));
    }

    public Boolean searchBillList(int valueToCheck, List<Bill> tempList) {
        return tempList.stream().anyMatch((bill -> bill.getValue() == valueToCheck));
    }

    public void billTransfer(int value, List<Bill> billsToWithdraw, List<Bill> tempList){
        Bill bill = tempList.stream().filter((currentBill) -> currentBill.getValue() == value).findFirst().get();
        calculate = calculate - bill.getValue();
        billsToWithdraw.add(bill);
        tempList.remove(bill);
    }

My repository:

public interface BillRepository extends CrudRepository<Bill, Long> {
    Iterable<Bill> findByValue(int value);
}

My test:

@SpringBootTest
@AutoConfigureMockMvc
public class BillControllerTest2 {

    @Autowired
    private MockMvc mvc;

    @MockBean
    private BillRepository mockRepo;

    @BeforeEach
    public void init(){
        Bill b1 = new Bill(1000, 1L);
        Bill b2 = new Bill(500, 2L);
        Bill b3 = new Bill(100, 3L);
        when(mockRepo.findByValue(1000)).thenReturn(List.of(b1));
        when(mockRepo.findByValue(500)).thenReturn(Arrays.asList(b2));
        when(mockRepo.findByValue(100)).thenReturn(Arrays.asList(b3));
        when(mockRepo.findAll()).thenReturn(Arrays.asList(b1, b2, b3));
    }

    @Test
    public void withdrawOneThousand() throws Exception {

        mvc.perform(MockMvcRequestBuilders.get("/bills/withdraw2/1000").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().string("Bills: [Bill{id=1, number=1000}]"));
    }

    @Test
    public void getAllBills() throws Exception {
        mvc.perform(MockMvcRequestBuilders.get("/bills/all").accept(MediaType.APPLICATION_JSON))
                .andExpect(status().isOk())
                .andExpect(content().json("[{\"id\": 1, \"value\": 1000}, " +
                        "{\"id\": 2, \"value\": 500 }, " +
                        "{\"id\": 3, \"value\": 100}]"));
    }

The error I'm getting:

java.lang.AssertionError: Response content expected:<Bills: [Bill{id=1, number=1000}]> but was:<java.lang.UnsupportedOperationException: remove>
Expected :Bills: [Bill{id=1, number=1000}]
Actual   :java.lang.UnsupportedOperationException: remove
<Click to see difference>


    at org.springframework.test.util.AssertionErrors.fail(AssertionErrors.java:59)
    at org.springframework.test.util.AssertionErrors.assertEquals(AssertionErrors.java:122)
    at org.springframework.test.web.servlet.result.ContentResultMatchers.lambda$string$4(ContentResultMatchers.java:136)
    at org.springframework.test.web.servlet.MockMvc$1.andExpect(MockMvc.java:196)
    at com.example.demo.controllers.BillControllerTest2.withdrawOneThousand(BillControllerTest2.java:51)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78)
    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:688)
    at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131)
    at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:149)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:140)
    at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:84)
    at org.junit.jupiter.engine.execution.ExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(ExecutableInvoker.java:115)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.lambda$invoke$0(ExecutableInvoker.java:105)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45)
    at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:104)
    at org.junit.jupiter.engine.execution.ExecutableInvoker.invoke(ExecutableInvoker.java:98)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$6(TestMethodTestDescriptor.java:210)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:206)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:131)
    at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:65)
    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:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    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:143)
    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:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
    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:143)
    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:129)
    at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:127)
    at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:126)
    at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:84)
    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.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:108)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
    at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:96)
    at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:75)
    at com.intellij.junit5.JUnit5IdeaTestRunner.startRunnerWithArgs(JUnit5IdeaTestRunner.java:71)
    at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
    at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:220)
    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:53)

Solution

  • List.of(b1) that you return in mock returns unmodifiable list. Btw Arrays.asList doesn't support remove too. You should change return of your mock to something like if you need removal from collection

    new ArrayList<>(List.of(b1));
    new ArrayList<>(Arrays.asList(b2));
    

    So the complete code should look like this:

    when(mockRepo.findByValue(1000)).thenReturn(new ArrayList<>(List.of(b1)));
    when(mockRepo.findByValue(500)).thenReturn(new ArrayList<>(Arrays.asList(b2)));
    when(mockRepo.findByValue(100)).thenReturn(new ArrayList<>(Arrays.asList(b3)));
    when(mockRepo.findAll()).thenReturn(new ArrayList<>(Arrays.asList(b1, b2, b3)));