单元测试Mock

一、maven依赖

有些数据mock需要额外的工具进行(比如需要mock静态、final方法),如Mockito.MockedStatic,PowerMock等等,文档下面的例子全部都是使用PowerMock进行

maven依赖:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.2</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.7.7</version>
    <scope>test</scope>
</dependency>

其中mockito与PowerMock版本依赖关系为:

二、mock小技巧

1. mock静态方法

为什么mockito无法mock静态方法?

Mockito使用继承的方式实现mock的,用CGLIB生成mock对象代替真实的对象进行执行,为了mock实例的方法,可以在subclass中覆盖它,而static方法是不能被子类覆盖的,所以Mockito不能mock静态方法。

PowerMock如何解决这个问题?

PowerMock有着两个非常重要的依赖,一个是javassist,另外一个就是objenesis。其中javassist是一个修改java字节码的工具包;objenesis是一个绕过构造方法来实例化一个对象的工具包。PowerMock的本质是通过修改字节码来实现对静态和final等方法的mock的

在某一个测试方法被注解@PrepareForTest标注之后,在运行测试用例的时候,会创建一个新的org.powermock.core.classloader.MockClassLoader实例,之后,加载这个测试用例使用到的类,注意这里的话,系统类要除外。PowerMock会依据mock要求,对在注解@PrepareForTest里的class文件进行修改(测试类会自己主动的加入注解当中),以此来满足特殊的mock需求。比如说去除final方法的final标识,在静态方法的最前面加入自己的虚拟实现等等。假如,需要mock的是系统类的final方法和静态方法,PowerMock不能直接修改系统类的class文件,而是去修改调用系统类的class文件,以此来满足mock需求。

使用powerMock步骤:

(1)在被测试类上加上注解:@PrepareForTest(Static.class)

(2)mock:PowerMockito.mockStatic(Static.class);

(3)Mockito.when(Static.firstStaticMethod(param)).thenReturn(value);

被测试类

public class StaticClass {
    public static String str(){
        return "aaa";
    }
}

测试用例

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*","javax.script.*"})
@PrepareForTest({StaticClass.class})
public class MockStatic {
    @Test
    public void testStaticClass(){
        PowerMockito.mockStatic(StaticClass.class);
        PowerMockito.when(StaticClass.str()).thenReturn("bb");
        Assert.assertNotEquals(StaticClass.str(),"aaa");
    }
}

mock带参数的静态方法:

completableFutureMockedStatic.when(() -> CompletableFuture.runAsync(Mockito.any(),Mockito.any())).thenReturn(new CompletableFuture<>());

2. mock私有方法

被测试类

public class PrivateClass {
    public String str(String ss){
        return privateStr()+ss;
    }
    private String privateStr(){
        return "aaa";
    }
}

测试用例

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*","javax.script.*"})
@PrepareForTest({PrivateClass.class})
public class MockPrivate {
    @InjectMocks
    private PrivateClass privateClass;
    @Test
    public void testPrivate() throws Exception {
        PrivateClass spy = PowerMockito.spy(privateClass);
        // mock本地方法
        PowerMockito.doReturn("qqq").when(spy,"privateStr");
        Assert.assertEquals(spy.str("a"),"qqqa");
    }
}

3. mock final方法

被测试类

public final class FinalClass {
    public final String str() {
        return "aaa";
    }
}

测试用例

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*","javax.script.*"})
@PrepareForTest({FinalClass.class})
public class MockFinal {
    @Test
    public void testFinal(){
        FinalClass mock = PowerMockito.mock(FinalClass.class);
        PowerMockito.when(mock.str()).thenReturn("qqq");
        Assert.assertEquals(mock.str(),"qqq");
    }
}

4. mock 线程池

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*","javax.script.*"})
public class MockAsync {
    @InjectMocks
    private AsyncClass asyncClass;
    @Mock
    private ExecutorService executorService;
    @Test
    public void asyncTest() throws ExecutionException, InterruptedException {
        Future<String> future = new Future<String>() {
            @Override
            public boolean cancel(boolean mayInterruptIfRunning) {
                return false;
            }
            @Override
            public boolean isCancelled() {
                return false;
            }
            @Override
            public boolean isDone() {
                return false;
            }
            @Override
            public String get() throws InterruptedException, ExecutionException {
                return "sss";
            }
            @Override
            public String get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException {
                return null;
            }
        };
        //数据mock
        c
        // 实际方法调用
        String s = asyncClass.doInvoke();
        Assert.assertEquals(s,"sss");
    }
}

5. Apollo Mock

方式一:直接mock掉Apollo的静态方法,使其使用本地配置方法
@BeforeClass
public static void mockApollo(){
    PowerMockito.mockStatic(EnvUtils.class);
    when(EnvUtils.transformEnv(any())).thenReturn(Env.LOCAL);
}

这种方式的运行原理本质上是mock掉源码的调用流程,使其直接使用本地配置

方式二:修改环境属性

可以通过设置环境属性 apollo.env=Local (注意大小写)

在调用阿波罗配置的时候,不会去远程加载,直接本地使用默认值

如:

ConfigService.getAppConfig().getProperty("key","defaultValue")

直接获取到的值为“defaultValue”

但是有时候我们不仅仅只是使用apollo配置中心的默认值,还需要对配置中心返回值进行配置,如果执行测试用例时,不希望使用代码中的默认值,希望使用自己定义的配置值,参考链接中的配置方式。

public class TestBase {
    @BeforeClass
    public static void initApolloLocalConfig(){
        /**
         * 初始化本地化 阿波罗配置中心,不进行网络请求
         * 在调用阿波罗配置的时候,就会使用默认值
         * 如:
         * ConfigService.getAppConfig().getProperty("key","defaultValue")
         * 直接获取到的值为“defaultValue”
         */
         System.setProperty("apollo.env","Local");
    }
}

注:建议将以上几种方式都写在@BeforeClass标注的方法中,如果写在@Before标注的方法中,可能存在以下几种问题:

1. 每次运行一个测试用例,都会执行@Before方法,实际上是没有必要的

2. 如果被测试类在属性初始化或者静态方法中使用了Apollo配置,@Before无法生效

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*","javax.script.*"})
public class ThreadServiceTest {


    // 测试类运行时,会初始化ThreadService,然后将@Mock标注的属性注入到ThreadService 中,在初始化中如果有静态方法用到了Apollo配置或者像例子中实例化一个属性用到Apollo配置都会导致再次拉取Apollo
    @InjectMocks
    private ThreadService threadService;


    @Mock
    private ThreadDetailInterveneCache threadDetailInterveneCache;


    @Mock
    private ThreadRecommendService threadRecommendService;


    @Mock
    private ThreadParser threadParser;


    @Before
    public static void baseMockApollo(){
      System.setProperty("apollo.env","Local");
    }
}


------------------------------------------------------
@Service
public class ThreadService {
  private ExecutorService batchGetThreadExecutorService = new TraceExecutorService(new ThreadPoolExecutor(getCoreThreadNum(), getMaxThreadNum(), getThreadAliveTime(), TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(getQueueCapacity()), threadFactory, new ThreadPoolExecutor.CallerRunsPolicy()));


    private static int getThreadAliveTime() {
        return ConfigService.getAppConfig().getIntProperty("tribe.batch.get.thread.time", 120);
    }


    private static int getCoreThreadNum() {
        return ConfigService.getAppConfig().getIntProperty("tribe.batch.get.thread.core.thread.num", 16);
    }


    private static int getQueueCapacity() {
        return ConfigService.getAppConfig().getIntProperty("tribe.batch.get.thread.queue.capacity", 1000);
    }


    private static int getMaxThreadNum() {
        return ConfigService.getAppConfig().getIntProperty("tribe.batch.get.thread.max.num", 16);
    }
}

但是有时候我们不仅仅只是使用apollo配置中心的默认值,还需要对配置中心返回值进行配置,按自定义的配置值来跑用例

第一步:在设置环境属性

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*","javax.script.*"})
public class DemoTest extends TestBase {
    
    // 该单元测试类中需要自定义的配置内容, 可以直接通过原来的方式获取到该内容
    @Before
    public void initApolloConfig(){

        System.setProperty("对应阿波罗配置中心的key","阿波罗配置中心的value");

        System.setProperty("对应阿波罗配置中心的key","阿波罗配置中心的value");

        System.setProperty("对应阿波罗配置中心的key","阿波罗配置中心的value");

    }


    @Test

    public void testMethod(){
        // do test things

    }

}
方式三:mock apollo
//mock Apollo
Config application = mock(Config.class);
when(application.getIntProperty(eq("splash.batch.getBuoy.left.time"), anyInt())).thenReturn(500);
PowerMockito.mockStatic(ConfigService.class);
when(ConfigService.getAppConfig()).thenReturn(application);

6. mock Dubbo服务调用

1. mock dubbo服务的异步调用

dubbo服务调用实质也是对于静态方法的mock

被测试类

@Service
public class MessageEngineService {
    private static final Logger logger = LoggerFactory.getLogger(MessageEngineService.class);
    @Autowired
    private MessageEngineFacade messageEngineFacade;


    /**
     * 创建消息主体内容
     **/
    public MessageContentDto createMessageContent(MessageContentDto messageContentDto){
        if(messageContentDto == null){
            logger.warn("MessageEngineService.createMessageContent param is empty. messageContentDto:{}", JSON.toJSONString(messageContentDto));
            return null;
        }
        try {
            messageEngineFacade.createMessageContent(messageContentDto);
            CompletableFuture<ResultDto<MessageContentDto>> future = RpcContext.getContext().getCompletableFuture();
            ResultDto<MessageContentDto> result = future.get();
            if(result.isSuccess()){
                return result.getT();
            }
            logger.info("createMessageContent result failed. messageContentDto:{}",JSON.toJSONString(messageContentDto));
            return null;
        }catch (Exception e){
            logger.error("MessageEngineService.createMessageContent error,messageContentDto:{},e={}", JSON.toJSONString(messageContentDto), e);
            return null;
        }
    }
}

测试类

@PrepareForTest({RpcContext.class})
public class MessageEngineServiceTest extends BaseMock {
    @InjectMocks
    private MessageEngineService messageEngineService;
    @Mock
    private RpcContext rpcContext;
    @Mock
    private MessageEngineFacade messageEngineFacade;
    @Test
    public void createMessageContent() throws ExecutionException, InterruptedException {
        PowerMockito.doReturn(null).when(messageEngineFacade).createMessageContent(Mockito.any());
        // mock 异步调用
        PowerMockito.mockStatic(RpcContext.class);
        PowerMockito.when(RpcContext.getContext()).thenReturn(rpcContext);
        MessageContentDto messageContentDto = new MessageContentDto();
        messageContentDto.setBody("aaa");
        ResultDto resultDto = new ResultDto();
        resultDto.setT(messageContentDto);
        resultDto.setSuccess(true);
        PowerMockito.doReturn(buildCompletableFuture(resultDto)).when(rpcContext).getCompletableFuture();
        MessageContentDto messageContent = messageEngineService.createMessageContent(new MessageContentDto());
        Assert.assertEquals(messageContent.getBody(),"aaa");
    }


    private <T> CompletableFuture<T> buildCompletableFuture(T value){
        return CompletableFuture.completedFuture(value);
    }
}

示例二:

private AppCatInfo appRemote(Long appId) {
    AppCatInfo info = null;
    try {
        appCatService.queryAppCatInfo(null, appId);
        if (info == null) {
            Future<AppCatInfo> future = RpcContext.getContext().getFuture();
            info = future.get();
        }
    } catch (Exception e) {
        logger.error("获取资源信息出错, appId:{}", appId, e);
    }
    return info;
}

测试类:

import org.apache.dubbo.rpc.RpcContext;
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.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;

import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Future;

import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyLong;


@RunWith(PowerMockRunner.class)
@PrepareForTest({RpcContext.class})
@PowerMockIgnore({"javax.management.*", "javax.script.*"})
public class CatManagerTest extends BaseJunitCase {
    @InjectMocks
    private CatManager catManager;
    @Mock
    private RpcContext rpcContext;
    @Mock
    private AppCatService appCatService;
    @Before
    public void init() throws Exception {
        PowerMockito.doReturn(null).when(appCatService).queryAppCatInfo(any(), anyLong());
        // mock 异步调用
        PowerMockito.mockStatic(RpcContext.class);
        PowerMockito.when(RpcContext.getContext()).thenReturn(rpcContext);
        PowerMockito.doReturn(buildFuture(builderAppCatInfo())).when(rpcContext).getFuture();
    }


    @Test
    public void testAppRemote() throws Exception {
        AppCatInfo appCatInfo = Whitebox.invokeMethod(catManager, "appRemote", 1L);
        System.out.println(appCatInfo);
        Assert.assertEquals(1L, appCatInfo.getAppId());
    }


    private <T> Future<T> buildFuture(T value){
        return CompletableFuture.completedFuture(value);
    }


    private AppCatInfo builderAppCatInfo() {
        AppCatInfo appCatInfo = new AppCatInfo();
        appCatInfo.setAppId(1L);
        return appCatInfo;
    }
}
2. mock dubbo服务的同步调用

同步调用时跟一般mock方式是一样的

@Test
public void syncCreateMessageContentTest(){
    MessageContentDto messageContentDto = new MessageContentDto();
    messageContentDto.setBody("aaa");
    ResultDto resultDto = new ResultDto();
    resultDto.setT(messageContentDto);
    resultDto.setSuccess(true);
    PowerMockito.doReturn(resultDto).when(messageEngineFacade).createMessageContent(Mockito.any());
    MessageContentDto messageContent = messageEngineService.syncCreateMessageContent(new MessageContentDto());
    Assert.assertEquals(messageContent.getBody(),"aaa");
}

7. 匹配对象参数

应用场景

public void provideColumnAccountDtos(String userId, String sender) {
    // 请求参数由程序内部自己new出来的
    MessageHomeReq messageHomeReq = new MessageHomeReq();
    messageHomeReq.setSenders(Lists.newArrayList(sender));
    messageHomeReq.setUserId(userId);
    List<MessageHomeDto> messageHomeDtos = messageEngineService.messageHome(messageHomeReq);
}

方式一:参数匹配器(any)

PowerMockito.when(messageEngineService.messageHome(Mockito.any())).thenReturn(value);

// 被测试方法内多次调用mock方法,第一次返回value1,第二次返回value2
PowerMockito.when(messageEngineService.messageHome(Mockito.any())).thenReturn(value1, value2);

方式二:参数匹配器(eq)

PowerMockito.doReturn(Lists.newArrayList(messageHomeDto)).when(messageEngineService).messageHome(Mockito.eq(messageHomeReq));

注:如果是复杂对象,使用eq时需要参数对象实现equals、hashCode方法

8.mapStruct 与 mockito

通过mapStruct 编写的dto转换类实质上还是一种工具类,但为了使用方便,在项目中通常会通过spring的方式进行依赖注入

如果我们希望测试的时候不用mock这种dto转换类数据,直接走实际流程,怎么操作呢?

9.mock post请求

@RunWith(PowerMockRunner.class)
@PrepareForTest(ConfigService.class)
@PowerMockIgnore({"javax.management.*"})
public class RiskUtilTest {
    private RiskUtil riskUtil = new RiskUtil();


    @Before
    public void init() throws Exception {
        HttpClient httpClient = mock(HttpClient.class);
        when(httpClient.post(anyString(), anyMap(), anyString(), any())).thenReturn(getResult());
        ReflectionTestUtils.setField(riskUtil, "httpClient", httpClient, HttpClient.class);
    }


    private String getResult() {
//        return "{\"result\":{\"score\":90.0,\"riskLevel\":\"DANGER\",\"firedRules\":[\"SSOID_GLOBAL_LIST\"],\"explanations\":[\"全局风险名单\"],\"rulePoint\":\"SSOID\"},\"suc\":true,\"code\":1001,\"message\":\"\"}";
//        return "{\"result\":{\"score\":79.0,\"riskLevel\":\"HIGH\",\"firedRules\":[\"SSOID_GLOBAL_LIST\"],\"explanations\":[\"全局风险名单\"],\"rulePoint\":\"SSOID\"},\"suc\":true,\"code\":1001,\"message\":\"\"}";
        return "{\"result\":{\"score\":0.0,\"riskLevel\":\"LOW\",\"firedRules\":[],\"explanations\":[\"\"],\"rulePoint\":\"\"},\"suc\":true,\"code\":1001,\"message\":\"\"}";
    }


    @Test
    public void testRiskReport() {
        RiskConfig riskConfig = mockRiskConfig(RiskUtil.ADD_POINT);
        System.out.println("风控配置文件:" + JSON.toJSONString(riskConfig));


        //获取风控结果
        System.out.println("风控返回结果:" + riskUtil.getRiskReport(mockPageRequest(), riskConfig));
    }


    private RiskConfig mockRiskConfig(int type) {
        String riskConfigStr = Constants.getRiskConfig();
        List<RiskConfig> riskConfigList = JSONObject.parseArray(riskConfigStr, RiskConfig.class);
        Map<Integer,RiskConfig> tempMap = riskConfigList.stream().collect(Collectors.toMap(RiskConfig::getType, Function.identity(),(key1, key2)->key2));
        return tempMap.get(type);
    }


}

三、编写测试用例小知识点

1.参数化测试

(1)Junit4 参数化测试

官方文档:参数化测试

步骤:

  1. 在测试类上加上注解@RunWith(Parameterized.class)
  2. 构造参数
    @Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {     
                 { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }
           });
    }

     3.参数注入

        3.1 通过构造函数注入

@RunWith(Parameterized.class)
public class MockParams1 {
    @Parameterized.Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
                { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }
        });
    }
    private int value1;
    private int value2;
    public MockParams1(int value1, int value2) {
        this.value1 = value1;
        this.value2 = value2;
    }
    @Test
    public void test(){
        System.out.println(value1 + value2);
    }
}

        3.2 通过参数注入

@RunWith(Parameterized.class)
public class MockParams {
   // 需要是public static修饰
    @Parameterized.Parameters
    public static Collection<Object[]> data() {
        return Arrays.asList(new Object[][] {
                { 0, 0 }, { 1, 1 }, { 2, 1 }, { 3, 2 }, { 4, 3 }
        });
    }
    // public
    @Parameterized.Parameter
    public int value1;
    @Parameterized.Parameter(1)
    public int value2;
    @Test
    public void test(){
        System.out.println(value1 + value2);
    }
}

(2)Junit5参数化测试

文档:JUnit 5 User Guide

Junit5的参数化测试功能更加强大,可以支持枚举、方法、csv文件作为数据源

@ParameterizedTest
@ValueSource(ints = { 1, 2, 3 })
void testWithValueSource(int argument) {
    assertTrue(argument > 0 && argument < 4);
}
2.thenReturn与doReturn

在Mockito中打桩(即stub)有两种方法when(...).thenReturn(...)和doReturn(...).when(...)。这两个方法在大部分情况下都是可以相互替换的,但是在使用了Spies对象(@Spy注解),而不是mock对象(@Mock注解)的情况下他们调用的结果是不相同的

● when(...) thenReturn(...)会调用真实的方法,如果你不想调用真实的方法而是想要mock的话,就不要使用这个方法。

● doReturn(...) when(...) 不会调用真实方法

3.参数匹配器

使用参数匹配器时,所有参数都应使用匹配器。

verify(mock).someMethod(anyInt(), anyString(), eq("third argument"));
// 不正确的,第三个参数没有使用参数匹配器
verify(mock).someMethod(anyInt(), anyString(), "third argument");

如果参数是null值,需要用mockito.eq 进行参数匹配,不能使用类似mockito.any()这种

四、常见问题与解决方式

1.测试用例报错:org.mockito.exceptions.misusing.WrongTypeOfReturnValue:

将when(test.do()).thenReturn(mockData)改为

doReturn(mockData).when(test).do()形式

2.错误:ScriptEngineManager providers.next(): javax.script.ScriptEngineFactory: Provider jdk.nashorn.api.scripting.NashornScriptEngineFactory not a subtype ScriptEngineManager providers.next(): javax.script.ScriptEngineFactory: Provider jdk.nashorn.api.scripting.NashornScriptEngineFactory not a subtype 2017-06-09 13:37:24,660 main ERROR No ScriptEngine found for language javascript. Available languages are: 2017-06-09 13:37:24,776 main WARN No script named {} could be found

原因:主要由于PowerMock与一些组件,比如log4j有冲突,但不影响测试用例的执行,可以直接忽略

解决:在测试类上加上注解

@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*","javax.script.*"})
public class MockUtils {

}

3.Junit5 中mock 静态方法

junit5不支持PowerMockito,但可以使用MockedStatic.

注:Mockito3.4.0 版本之前是不支持MockedStatic的

@Test
public void staticTest(){
    MockedStatic<Static5Class> static5ClassMockedStatic = Mockito.mockStatic(Static5Class.class);
    static5ClassMockedStatic.when(Static5Class::str).thenReturn("bbb");
    Assert.assertEquals("bbb",Static5Class.str());
}

五、JUNIT5与Mocktio3

1.How to resolve Unneccessary Stubbing exception

在一段代码中,可能会存在多段代码逻辑分支。

我们将所有的外部依赖方法都mock之后,如果在Test流程中因为某个逻辑分支没有使用到,Mockito会报不必要的存根错误。

如下图所示,我写了6个方法的mock,但是其实在Test流程中,因为只跑了某个逻辑分支,该分支只用到了其中的4个mock。就会报错。

解决方法:

@MockitoSettings(strictness = Strictness.LENIENT)
class MyTest{
  ......
}
2.Mockito3 中必须手动关闭mockStatic

错误描述:图片:

解决方案:每个测试用例调用完成MockedStatic<RpcContext> rpcContextMockedStatic = Mockito.mockStatic(RpcContext.class);之后必须手动关闭:

rpcContextMockedStatic.close();

  • 16
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Java单元测试中的Mock是指在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来代替它,以便测试能够顺利进行。Mock可以模拟出一个对象的行为,使得测试人员可以在不依赖于真实对象的情况下进行测试。常用的Java Mock框架有Mockito和PowerMock等。下面是一个使用Mockito进行单元测试的例子: 假设我们有一个UserService类,其中有一个getUserById方法,该方法依赖于一个UserDao对象,我们可以使用Mockito来模拟UserDao对象的行为,从而测试getUserById方法的正确性。 ```java import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import static org.junit.Assert.assertEquals; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) public class UserServiceTest { @Mock private UserDao userDao; @InjectMocks private UserService userService; @Test public void testGetUserById() { User user = new User(); user.setId(1L); user.setName("Alice"); when(userDao.getUserById(1L)).thenReturn(user); assertEquals(user, userService.getUserById(1L)); } } ``` 在上面的例子中,我们使用了MockitoJUnitRunner来运行测试,使用@Mock注解来模拟UserDao对象,使用@InjectMocks注解来注入UserService对象。在testGetUserById方法中,我们使用when方法来指定当调用userDao.getUserById(1L)方法时,返回一个预设的User对象,然后使用assertEquals方法来判断userService.getUserById(1L)方法的返回值是否与预期相同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M.Rambo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值