【问题标题】:How to mock a service for controller in mockito如何在 mockito 中模拟控制器的服务
【发布时间】:2026-02-21 22:55:01
【问题描述】:

全部,

感谢您的帮助。我对 Mockito 很陌生,如果我有一个服务类、一个控制器类(通过传入 Map 参数使用该服务),我该如何模拟该服务方法?

Helloworldservice.java

    package service;

    import java.util.Map;

    public class Helloworldservice {
        public String greeting() {
            return "Hello, World";
        }
        public String greetingSB(Map<String, String> sb) {
            return "Hello," + sb.get("name");
        }
    }

Helloworldcontroller.java

    package controller;

    import java.util.HashMap;
    import java.util.Map;

    import service.Helloworldservice;

    public class Helloworldcontroller {
        private Helloworldservice hservice;

        public Helloworldcontroller() {
            // TODO Auto-generated constructor stub
            hservice = new Helloworldservice();
        }

        public String sayHello() {
            return hservice.greeting();
        }

        public String sayHelloSB() {
            Map<String, String> sb = new HashMap<String, String>();
            sb.put("name", "somebody");
            return hservice.greetingSB(sb);
        }
    }

HelloworldcontrollerTest.java

    package unit.controller;

    import org.junit.Assert;
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.InjectMocks;
    import org.mockito.Mock;
    import org.mockito.Mockito;
    import org.mockito.junit.MockitoJUnitRunner;

    import controller.Helloworldcontroller;
    import service.Helloworldservice;

    @RunWith(MockitoJUnitRunner.class)
    public class HelloworldcontrollerTest {
        @InjectMocks
        private Helloworldcontroller hcontroller;

        private Helloworldservice hservice = new Helloworldservice();
        @Mock
        private Helloworldservice hservice_mock;
        @Before
        public void setup() {
            hservice_mock = Mockito.spy(hservice);
            /**  I am not sure how to mock here for that param sb
            Mockito.when(hservice_mock.greetingSB(.......))
                    .thenReturn("Hello, somebody");

            **/
        }

        @Test
        public void testGreeting() {
            String h = hcontroller.sayHelloSB();
            Assert.assertEquals(h, "Hello, sombody!!!");
        }
    }

服务总是返回null,我不确定是什么问题。

【问题讨论】:

  • 控制器创建自己的真实服务。它不使用您的测试创建的模拟。使用依赖注入,即让控制器构造函数接受服务作为参数。然后将 mocj 服务传递给测试中的控制器。

标签: java mockito


【解决方案1】:

您的示例代码有点偏离,因为它可以在不涉及任何模拟的情况下工作。

public class HelloworldcontrollerTest {
    private Helloworldcontroller hcontroller = new Helloworldcontroller();

    @Test
    public void testGreeting() {
        String h = hcontroller.sayHelloSB();
        Assert.assertEquals(h, "Hello,somebody");
    }
}

总之,大概就是这样,一个样本。

模拟的问题在于这一行

hservice_mock = Mockito.spy(hservice);

首先,您让 Mockito 创建您的模拟 (@Mock Helloworldservice hservice_mock) 并将其注入控制器 (@InjectMocks Helloworldcontroller hcontroller),然后您自己创建一个间谍 (hservice_mock = Mockito.spy(hservice))期望(when(hservice_mock.greetingSB(...)))。设置对间谍的期望需要不同的方法调用链,因此当前会出现NullPointerException(参见Important gotcha on spying real objects!)。即使它可以工作,它也不会影响已经注入的模拟。

这将按预期工作:

@RunWith(MockitoJUnitRunner.class)
public class HelloworldcontrollerTest {
    @InjectMocks
    private Helloworldcontroller hcontroller = new Helloworldcontroller();

    @Mock
    private Helloworldservice hservice_mock;

    @Before
    public void setup() {
        Mockito.when(hservice_mock.greetingSB(any(Map.class)))
                .thenReturn("Hello, somebody!!!");
    }

    @Test
    public void testGreeting() {
        String h = hcontroller.sayHelloSB();
        Assert.assertEquals(h, "Hello, somebody!!!");
    }
}

测试设置中的其他一些 cmets:

  • Helloworldcontroller 依赖于 Helloworldservice。您应该考虑使用构造函数依赖注入,而不是在构造函数中创建实例。您的示例代码有效,但如果它变得更复杂,事情就会失控。想想数据库访问。
  • @InjectMocks 是代码异味的标志,表明要测试的代码存在更深层次的问题。尽量避免。
  • assertEquals 的第一个参数与assertEquals(h, "Hello, sombody!!!") 一样,是预期值,然后是实际值。听起来不是很重要,但会影响断言冲突错误消息。

让我们解决这些问题,看看我们如何改进代码。

首先,使用构造函数注入。

public class Helloworldcontroller {
    private final Helloworldservice hservice;

    public Helloworldcontroller(Helloworldservice hservice) {
        this.hservice = hservice;
    }

    public String sayHello() {
        return hservice.greeting();
    }

    public String sayHelloSB() {
        Map<String, String> sb = new HashMap<String, String>();
        sb.put("name", "somebody");
        return hservice.greetingSB(sb);
    }
}

测试代码变成现在

@RunWith(MockitoJUnitRunner.class)
public class HelloworldcontrollerTest {
    private Helloworldcontroller hcontroller;

    @Mock
    private Helloworldservice hservice;

    @Before
    public void setup() {
        hcontroller = new Helloworldcontroller(hservice);

        Mockito.when(hservice.greetingSB(any(Map.class)))
                .thenReturn("Hello, somebody!!!");
    }

    @Test
    public void testGreeting() {
        Assert.assertEquals("Hello, somebody!!!", hcontroller.sayHelloSB());
    }
}

【讨论】:

    【解决方案2】:

    在你的测试类中有几个地方需要修复,如数字 1、2 ...

    @RunWith(MockitoJUnitRunner.class)
    public class HelloworldcontrollerTest {
        @InjectMocks
        private Helloworldcontroller hcontroller;
    
        private Helloworldservice hservice = new Helloworldservice();
        @Mock
        private Helloworldservice hservice_mock;
        @Before
        public void setup() {
        // 1. Comment out this line, because you've already created a mock instance using @Mock
    //        hservice_mock = Mockito.spy(hservice);
            /**  I am not sure how to mock here for that param sb
             Mockito.when(hservice_mock.greetingSB(.......))
             .thenReturn("Hello, somebody");
    
             **/
        }
    
        @Test
        public void testGreeting() {
            // 2. This's why program output null. If you create an entire mock(not a real mock or a partial mock) object, then you should give the specific expectation for the method. 
            Mockito.when(hservice_mock.greetingSB(Mockito.any())).thenReturn("Hello world!!");
            String h = hcontroller.sayHelloSB();
            Assert.assertEquals(h, "Hello, sombody!!!");
        }
    }
    

    整个mock和部分mock的区别可以参考this link

    综上,使用mock的基本步骤是:

    1. 创建实例(@Mock)
    2. 注入它(@InjectMocks)
    3. 创建期望(Mockito.when().thenReturn)
    4. 调用模拟方法(或重放期望)

    【讨论】:

      最近更新 更多