相关文章推荐
逆袭的毛衣  ·  javascript ...·  1 年前    · 
玩命的炒粉  ·  python 畫圖問題 - iT ...·  1 年前    · 
阳刚的硬盘  ·  Viewing the SQL ...·  1 年前    · 

前面分析过了Mockito是如何通过注解创建对应的mock对象,并对其所有方法设置拦截器。这篇博客再分析下Mockito是如何实现自动装配和插桩的,只有更好的了解其原理,在写复杂单测的时候才能更加得心应手。

@InjectMocks的自动装配

InjectingAnnotationEngine.java

    public void process(Class<?> clazz, Object testInstance) {
        //创建mock对象或者spy对象
        processIndependentAnnotations(testInstance.getClass(), testInstance);
        //对添加了@InjectMocks注解的对象进行自动装配
        processInjectMocks(testInstance.getClass(), testInstance);

之前详细分析了processIndependentAnnotations(final Class<?> clazz, final Object testInstance源码,现在再看看processInjectMocks(final Class<?> clazz, final Object testInstance) 这部分源码

InjectingAnnotationEngine.java

     private void processInjectMocks(final Class<?> clazz, final Object testInstance) {
        Class<?> classContext = clazz;
        //循环,从当前测试类对象开始执行自动注入的方法,一直到所有的非Object的父类都注入完成
        while (classContext != Object.class) {
            injectMocks(testInstance);
            classContext = classContext.getSuperclass();
     public void injectMocks(final Object testClassInstance) {
        //获得当前测试类Class
        Class<?> clazz = testClassInstance.getClass();
        Set<Field> mockDependentFields = new HashSet<Field>();
        Set<Object> mocks = newMockSafeHashSet();
        while (clazz != Object.class) {
            //[1]从当前测试类开始遍历找到添加了@InjectMock属性加入到mockDependentFields集合 (一直向上遍历,直到Object)
            new InjectMocksScanner(clazz).addTo(mockDependentFields);
            //从当前测试类开始遍历找到添加了@Mock属性加入到mocks集合
            new MockScanner(testClassInstance, clazz).addPreparedMocks(mocks);
            //这里是个钩子方法,没有实现
            onInjection(testClassInstance, clazz, mockDependentFields, mocks);
            clazz = clazz.getSuperclass();
        //[2]真正对添加了@InjectMocks注解的对象进行自动注入
        new DefaultInjectionEngine().injectMocksOnFields(mockDependentFields, mocks, testClassInstance);
[1]InjectMocksScanner和MockScanner

InjectMocksScanner.java就是通过反射拿到clazz的所有属性,将添加了@InjectMocks注解但是没有加@Mock和@Captor的属性都添加到mockDependentFields集合中 ,从源码可以看出 : 不能同时对一个属性添加@Mock和@InjectMocks注解,否则会抛出异常

MockScanner同理,就是拿到clazz下所有添加了@Spy或者@Mock注解的对象(也可以是通过api生成的mock或者spy对对象)

    public InjectMocksScanner(Class<?> clazz) {
        this.clazz = clazz;
    public void addTo(Set<Field> mockDependentFields) {
        //将遍历到的满足条件的属性都加入到参数集合中
        mockDependentFields.addAll(scan());
    private Set<Field> scan() {
        Set<Field> mockDependentFields = new HashSet<Field>();
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            //将添加了@InjectMocks并且没有加上@Mock和@Captor的属性都加入到集合中
            if (null != field.getAnnotation(InjectMocks.class)) {
                assertNoAnnotations(field, Mock.class, Captor.class);
                mockDependentFields.add(field);
        return mockDependentFields;
[2]injectMocksOnFields 自动注入

创建MockInjection对象,顺便指定注入策略(构造器注入和属性注入),以及注入后置处理器,完成对@InjectMocks属性的处理

DefaultInjectionEngine.java

     private final MockInjectionStrategy injectionStrategies = MockInjectionStrategy.nop();
     public void injectMocksOnFields(Set<Field> needingInjection, Set<Object> mocks, Object testClassInstance) {
        //分别将needingInjection和testClassInstance赋值给OngoingMockInjection的fields和fieldOwner字段
        MockInjection.onFields(needingInjection, testClassInstance)
                //赋值给给OngoingMockInjection的mocks字段
                .withMocks(mocks)
                //向injectionStrategies注入策略添加构造器注入ConstructorInjection
                .tryConstructorInjection()
                //向injectionStrategies注入策略添加属性注入PropertyAndSetterInjection(这是一个责任链模式)
                .tryPropertyOrFieldInjection()
                //给postInjectionStrategies添加SpyOnInjectedFieldsHandler(将InjectsMock的属性采用spy方法初始化??)
                .handleSpyAnnotation()
                //执行属性注入和注入后的后置处理
                .apply();
    //MockInjection.java
    public void apply() {
       for (Field field : fields) {
           injectionStrategies.process(field, fieldOwner, mocks);
           postInjectionStrategies.process(field, fieldOwner, mocks);
    //MockInjectionStrategy.java
    public boolean process(Field onField, Object fieldOwnedBy, Set<Object> mockCandidates) {
        //先执行构造器注入,如果失败则获取下一个策略--属性注入
        if(processInjection(onField, fieldOwnedBy, mockCandidates)) {
            return true;
        return relayProcessToNextStrategy(onField, fieldOwnedBy, mockCandidates);

分别看下两个注入策略
ConstructorInjection.java

 public boolean processInjection(Field field, Object fieldOwner, Set<Object> mockCandidates) {
        try {
            SimpleArgumentResolver simpleArgumentResolver = new SimpleArgumentResolver(mockCandidates);
            //构建其FieldInitialization进行初始化(由于在测试类里没有带参数的构造函数,所以这里会走到异常)
            FieldInitializationReport report = new FieldInitializer(fieldOwner, field, simpleArgumentResolver).initialize();
            return report.fieldWasInitializedUsingContructorArgs();
        } catch (MockitoException e) {
            if(e.getCause() instanceof InvocationTargetException) {
                Throwable realCause = e.getCause().getCause();
                throw fieldInitialisationThrewException(field, realCause);
            // other causes should be fine
            return false;

PropertyAndSetterInjection.java

     private final MockCandidateFilter mockCandidateFilter =
            new TypeBasedCandidateFilter(
                    new NameBasedCandidateFilter(
                            new TerminalMockCandidateFilter()));
    public boolean processInjection(Field injectMocksField, Object injectMocksFieldOwner, Set<Object> mockCandidates) {
        //创建属性构造器
        FieldInitializationReport report = initializeInjectMocksField(injectMocksField, injectMocksFieldOwner);
        // for each field in the class hierarchy
        boolean injectionOccurred = false;
        //这里的fieldClass就是加了@InjectMocks注解的字段类型
        Class<?> fieldClass = report.fieldClass();
        //添加了@InjectMocks注解的字段的实例
        Object fieldInstanceNeedingInjection = report.fieldInstance();
        while (fieldClass != Object.class) {
            injectionOccurred |= injectMockCandidates(fieldClass, fieldInstanceNeedingInjection, newMockSafeHashSet(mockCandidates));
            fieldClass = fieldClass.getSuperclass();
        return injectionOccurred;
    private boolean injectMockCandidates(Class<?> awaitingInjectionClazz, Object injectee, Set<Object> mocks) {
        boolean injectionOccurred;
        //获取@InjectMocks对象的字段信息
        List<Field> orderedCandidateInjecteeFields = orderedInstanceFieldsFrom(awaitingInjectionClazz);
        //[1] 执行结果为true
        injectionOccurred = injectMockCandidatesOnFields(mocks, injectee, false, orderedCandidateInjecteeFields);
        //[2] 由于orderedCandidateInjecteeFields只有一个待被mock对象注入的属性,所以这一步没有执行什么逻辑,返回默认true,按位相或 也是true.
        injectionOccurred |= injectMockCandidatesOnFields(mocks, injectee, injectionOccurred, orderedCandidateInjecteeFields);
        return injectionOccurred;
    //[1],[2]
    private boolean injectMockCandidatesOnFields(Set<Object> mocks,
                                                 Object injectee,
                                                 boolean injectionOccurred,
                                                 List<Field> orderedCandidateInjecteeFields) {
        //遍历被注入对象的属性                                         
        for (Iterator<Field> it = orderedCandidateInjecteeFields.iterator(); it.hasNext(); ) {
            Field candidateField = it.next();
            //匹配mock对象和待注入对象的属性
            //TypeBasedCandidateFilter Class类型匹配,满足则进行下一步
            //NameBasedCandidateFilter 判断mockName是否相同,是否没有同类型的其他字段需要注入 这里第一次[1]返回false
            //TerminalMockCandidateFilter 对mock集合只有一个mock对象的情形进行注入
            Object injected = mockCandidateFilter.filterCandidate(mocks, candidateField, orderedCandidateInjecteeFields, injectee)
                                                 .thenInject();
            //mock对象被注入成功之后从mock对象集合中移除                                     
            if (injected != null) {
                injectionOccurred |= true;
                mocks.remove(injected);
                it.remove();
        return injectionOccurred;

TerminalMockCandidateFilter.java

  public OngoingInjector filterCandidate(final Collection<Object> mocks,
                                           final Field candidateFieldToBeInjected,
                                           final List<Field> allRemainingCandidateFields,
                                           final Object injectee) {
        //集合里的mock对象只有1个                                    
        if(mocks.size() == 1) {
            final Object matchingMock = mocks.iterator().next();
            //返回一个匿名内部类的对象
            return new OngoingInjector() {
                public Object thenInject() {
                    try {
                        //这就是真正进行注入的方法,先通过属性的setter方法进行反射赋值;
                        //如果失败则通过反射直接对属性赋值
                        if (!new BeanPropertySetter(injectee, candidateFieldToBeInjected).set(matchingMock)) {
                            setField(injectee, candidateFieldToBeInjected,matchingMock);
                    } catch (RuntimeException e) {
                        throw cannotInjectDependency(candidateFieldToBeInjected, matchingMock, e);
                    return matchingMock;
        return OngoingInjector.nop;

插桩Stubbing

之前简单分析过Mockito是怎么让mock对象调用方法都进入自己的拦截器,主要是在通过ByteBuddy生成mock代理类的时候定义了相关的拦截器

接下来再看看Mockito中最核心的Stubbing的实现

   Mockito.when(userDao.queryUserByName(eq("chenpp"))).thenReturn(list);
Stub之When方法

先来看下When的作用,这里也是进入了MOCKITO_CORE的对应方法,其代码都是围绕mockingProgress这个对象展开的,可以看到这是从ThreadLocal里获取到的一个对象, 调用其pullOngoingStubbing方法后获得一个OngoingStubbing的返回对象,也就是这个方法的返回值。可以看到when方法的入参是mock对象的某个调用方法的返回值,那么来看看这个入参到底是什么呢?
之前一篇博客里提到过mock对象的所有方法调用都会被拦截,进入到MockHandlerImpl的handle方法,再来仔细看看这个类的源码

    @CheckReturnValue
    public static <T> OngoingStubbing<T> when(T methodCall) {
        return MOCKITO_CORE.when(methodCall);
   //MockitoCore 
   public <T> OngoingStubbing<T> when(T methodCall) {
        MockingProgress mockingProgress = mockingProgress();
        mockingProgress.stubbingStarted();
        @SuppressWarnings("unchecked")
        OngoingStubbing<T> stubbing = (OngoingStubbing<T>) mockingProgress.pullOngoingStubbing();
        if (stubbing == null) {
            mockingProgress.reset();
            throw missingMethodInvocation();
        return stubbing;

ThreadSafeMockingProgress.java

  private static final ThreadLocal<MockingProgress> MOCKING_PROGRESS_PROVIDER = new          ThreadLocal<MockingProgress>() {
        @Override
        protected MockingProgress initialValue() {
            return new MockingProgressImpl();
public final static MockingProgress mockingProgress() {
        return MOCKING_PROGRESS_PROVIDER.get();

这里的mockingProgress实际是一个MockingProgressImpl的实例对象,具体的属性有什么等到后面使用到的时候再看

MockingProgressImpl.java

    private final ArgumentMatcherStorage argumentMatcherStorage = new ArgumentMatcherStorageImpl();
    private OngoingStubbing<?> ongoingStubbing;
    private Localized<VerificationMode> verificationMode;
    private Location stubbingInProgress = null;
    private VerificationStrategy verificationStrategy;
    private final Set<MockitoListener> listeners = new LinkedHashSet<MockitoListener>();
    public MockingProgressImpl() {
        this.verificationStrategy = getDefaultVerificationStrategy();
    public static VerificationStrategy getDefaultVerificationStrategy() {
        return new VerificationStrategy() {
            public VerificationMode maybeVerifyLazily(VerificationMode mode) {
                return mode;
MockHandlerImpl 拦截器进入的类

这里的Invocation即InterceptedInvocation,把代理类拦截时的方法调用和参数封装在一起,它包含以下几个对象:真正的方法realMethod,Mockito的方法MockitoMethod,参数arguments,以及mock对象mockRef等

  public Object handle(Invocation invocation) throws Throwable {
        //判断当前mock对象是否已插桩(在执行when的时候,还没有被插桩所有不会执行该分支)
        if (invocationContainer.hasAnswersForStubbing()) {
            // stubbing voids with doThrow() or doAnswer() style
            InvocationMatcher invocationMatcher = matchersBinder.bindMatchers(
                    mockingProgress().getArgumentMatcherStorage(),
                    invocation
            invocationContainer.setMethodForStubbing(invocationMatcher);
            return null;
        //获取verify信息
        VerificationMode verificationMode = mockingProgress().pullVerificationMode();
        //invocationMatcher里包含invocation信息以及参数匹配信息(bindMatchers执行之后会将之前的信息返回并且清空ArgumentMatcherStorageImpl存储匹配信息的matcherStack)
        InvocationMatcher invocationMatcher = matchersBinder.bindMatchers(
                //ArgumentMatcherStorageImpl的实例对象,保存参数匹配的相关信息
                mockingProgress().getArgumentMatcherStorage(),
                invocation
        //验证当前MockingProgressImpl状态是否正常,比方说matcherStack是否已经被重置清理等
        mockingProgress().validateState();
        // verificationMode 只有当有人在操作verify的是才会有值,插桩和mock的是都是null
        if (verificationMode != null) {
            // 判断verification的mock对象和在使用的mock对象是否是一个
            if (((MockAwareVerificationMode) verificationMode).getMock() == invocation.getMock()) {
                VerificationDataImpl data = new VerificationDataImpl(invocationContainer, invocationMatcher);
                verificationMode.verify(data);
                return null;
            } else {
                // 当前的invocation是另一个mock对象的,将verify信息重新加回去
                //this means there is an invocation on a different mock. Re-adding verification mode
                mockingProgress().verificationStarted(verificationMode);
        // prepare invocation for stubbing
        invocationContainer.setInvocationForPotentialStubbing(invocationMatcher);
        OngoingStubbingImpl<T> ongoingStubbing = new OngoingStubbingImpl<T>(invocationContainer);
        //将ongoingStubbing赋值给MockingProgressImpl对象的ongoingStubbing属性,每次调用都会获取到最新的ongoingStubbing
        mockingProgress().reportOngoingStubbing(ongoingStubbing);
        //根据invocation查找对应的answer(在插桩的时候stubbed是空的)
        // look for existing answer for this invocation
        StubbedInvocationMatcher stubbing = invocationContainer.findAnswerFor(invocation);
        // 通知回调,通知listener when completed, we should be able to get rid of the casting below
        notifyStubbedAnswerLookup(invocation, stubbing, invocationContainer.getStubbingsAscending(),
                                  (CreationSettings) mockSettings);
        //插桩时stubbing为null
        if (stubbing != null) {
            stubbing.captureArgumentsFrom(invocation);
            try {
                return stubbing.answer(invocation);
            } finally {
                //Needed so that we correctly isolate stubbings in some scenarios
                //see MockitoStubbedCallInAnswerTest or issue #1279
                mockingProgress().reportOngoingStubbing(ongoingStubbing);
        } else {
            //获取默认的answer信息
            Object ret = mockSettings.getDefaultAnswer().answer(invocation);
            //验证answer类型和对应的方法返回值是否匹配
            DefaultAnswerValidator.validateReturnValueFor(invocation, ret);
            //Mockito uses it to redo setting invocation for potential stubbing in case of partial mocks / spies.
            //为下一次可能的stub重置invocation (将invocationMatcher赋值给InvocationContainerImpl的invocationForStubbing属性)
            invocationContainer.resetInvocationForPotentialStubbing(invocationMatcher);
            return ret;

再看下之前when里的源码就很清楚了,通过when(mockObj.methdo())方法调用,我们可以拿到在MockHandlerImpl创建的ongoingStubbing对象,里面包含invocation和我们调用方法的参数匹配信息

   //MockitoCore 
   public <T> OngoingStubbing<T> when(T methodCall) {
        //获取MockingProgressImpl实例对象
        MockingProgress mockingProgress = mockingProgress();
        //验证mockingProgress状态并且给stubbingInProgress属性赋值(即标记开始插桩)
        mockingProgress.stubbingStarted();
        @SuppressWarnings("unchecked")
        //获取ongoingStubbing对象,这是在调用mock对象的方法时赋值的,包含invocation信息以及参数匹配信息
        OngoingStubbing<T> stubbing = (OngoingStubbing<T>) mockingProgress.pullOngoingStubbing();
        if (stubbing == null) {
            mockingProgress.reset();
            throw missingMethodInvocation();
        return stubbing;
Stub之ThenReturn

将返回结果包装成answer,通过InvocationContainerImpl的addAnswer方法将stub和返回绑定

    public OngoingStubbing<T> thenReturn(T value) {
        return thenAnswer(new Returns(value));
    private final InvocationContainerImpl invocationContainer;
    @Override
    public OngoingStubbing<T> thenAnswer(Answer<?> answer) {
        if(!invocationContainer.hasInvocationForPotentialStubbing()) {
            throw incorrectUseOfApi();
        //向invocationContainer添加answer(这里的answer就是Return对象,包含我们创建的返回值对象)
        invocationContainer.addAnswer(answer, strictness);
        return new ConsecutiveStubbing<T>(invocationContainer);

InvocationContainerImpl.java

    public void addAnswer(Answer answer, Strictness stubbingStrictness) {
        registeredInvocations.removeLast();
        addAnswer(answer, false, stubbingStrictness);
     * Adds new stubbed answer and returns the invocation matcher the answer was added to.
     * @param isConsecutive连续不断的
    public StubbedInvocationMatcher addAnswer(Answer answer, boolean isConsecutive, Strictness stubbingStrictness) {
        Invocation invocation = invocationForStubbing.getInvocation();
        //标记完成插桩
        mockingProgress().stubbingCompleted();
        if (answer instanceof ValidableAnswer) {
             //验证设定的返回值是否正确
            ((ValidableAnswer) answer).validateFor(invocation);
        synchronized (stubbed) {
            //这里就是把调用和返回绑定,如果下次调用匹配到了,就返回对应的answer
            if (isConsecutive) {
                stubbed.getFirst().addAnswer(answer);
            } else {
                //这里设置的answer并不是连续的,所以直接加到stubbed(LinkedList)的头部就行
                Strictness effectiveStrictness = stubbingStrictness != null ? stubbingStrictness : this.mockStrictness;
                //这里的invocationForStubbing
                stubbed.addFirst(new StubbedInvocationMatcher(answer, invocationForStubbing, effectiveStrictness));
            return stubbed.getFirst();

这样在单元测试调用Mock对象的方法时,会再次进入MockHandlerImpl的handle方法

    public StubbedInvocationMatcher findAnswerFor(Invocation invocation) {
        synchronized (stubbed) {
            for (StubbedInvocationMatcher s : stubbed) {
                //匹配mock对象,方法信息,参数信息,如果都匹配上则说明是这个answer
                if (s.matches(invocation)) {
                    s.markStubUsed(invocation);
                    //标记对应的stub信息 we should mark stubbed at the point of stubbing, not at the point where the stub is being used
                    invocation.markStubbed(new StubInfoImpl(s));
                    return s;
        return null;
    @Override
    public boolean matches(Invocation candidate) {
        return invocation.getMock().equals(candidate.getMock()) && hasSameMethod(candidate) && argumentsMatch(candidate);

最后执行StubbedInvocationMatcher的answer方法获取最终的返回结果,如下:

  public Object answer(InvocationOnMock invocation) throws Throwable {
        //see ThreadsShareGenerouslyStubbedMockTest
        Answer a;
        synchronized(answers) {
            //这里是从answers中获取其中一个,如果只有一个则使用peek(),否则使用poll
            //peek表示获取队列的队头元素但是并不移除,poll表示获取并移除队头元素(这是为了可以进行多次mock以及设置相同参数的每次不同的返回值--不了解的可以看下我之前关于Mockito使用的博客)
            a = answers.size() == 1 ? answers.peek() : answers.poll();
        //就是返回Returns封装的value值
        return a.answer(invocation);

Verify

最后再简单讲下verify的原理,Mockito.verify(userDao, Mockito.times(1))返回的还是一开始的mock对象,所以后续方法调用依旧会走到MockHandlerImpl的handle方法,此时VerificationMode就不为null了

    public <T> T verify(T mock, VerificationMode mode) {
        if (mock == null) {
            throw nullPassedToVerify();
        //获取mock详细信息
        MockingDetails mockingDetails = mockingDetails(mock);
        if (!mockingDetails.isMock()) {
            throw notAMockPassedToVerify(mock.getClass());
        assertNotStubOnlyMock(mock);
        //拿到mockHandler
        MockHandler handler = mockingDetails.getMockHandler();
        //调用VerificationStarted的通知回调
        mock = (T) VerificationStartedNotifier.notifyVerificationStarted(
            handler.getMockSettings().getVerificationStartedListeners(), mockingDetails);
        MockingProgress mockingProgress = mockingProgress();
        VerificationMode actualMode = mockingProgress.maybeVerifyLazily(mode);
        //封装MockAwareVerificationMode对象并赋值给MockingProgressImpl的verificationMode字段属性
        mockingProgress.verificationStarted(new MockAwareVerificationMode(mock, actualMode, mockingProgress.verificationListeners()));
        return mock;

当VerificationMode不为空时,先验证mode的状态是否正确,验证通过后获取到对应的container和invocationMatcher信息,最后调用verificationMode的verify方法,完成验证。

        if (verificationMode != null) {
            // We need to check if verification was started on the correct mock
            // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
            if (((MockAwareVerificationMode) verificationMode).getMock() == invocation.getMock()) {
                //获取校验信息,然后验证
                VerificationDataImpl data = new VerificationDataImpl(invocationContainer, invocationMatcher);
                verificationMode.verify(data);
                return null;
            } else {
                // this means there is an invocation on a different mock. Re-adding verification mode
                // - see VerifyingWithAnExtraCallToADifferentMockTest (bug 138)
                mockingProgress().verificationStarted(verificationMode);

至此,Mockito主要的原理分析都完成了

前面分析过了Mockito是如何通过注解创建对应的mock对象,并对其所有方法设置拦截器。这篇博客再分析下Mockito是如何实现自动装配和插桩的,只有更好的了解其原理,在写复杂单测的时候才能更加得心应手。@InjectMocks的自动装配InjectingAnnotationEngine.java public void process(Class&lt;?&gt; clazz, Object testInstance) { //创建mock对象或者spy对象
org.mockito.exceptions.misusing.PotentialStubbingProblem:Strict stubbing argument mismatch
org.mockito.exceptions.misusing.PotentialStubbingProblem: Strict stubbing argument mismatch. Please check: Typically, stubbing argument mismatch indicates user mistake when writing tests. Mockito fails early so that you can debug potential problem easily
文章目录Unit test 示例1. 工具类测试用例2. 依赖`spring`容器注入的bean3. 依赖Spring容器bean同时依赖静态类4. Mock各种实际情况1)忽略静态变量初始化2)初始化静态变量3)依赖的`bean` 为` list` 或者`map`a) `bean` 为 `list`b)`bean` 为`map`4) 链式调用`mock` 深度插桩建议 Unit test 示例 ​ 单元测试选用的测试框架junit,如果依赖Spring 框架需要Mock 数据,选用mockito和powe
文章目录Mock的基本原理Mock和Spy区别调用方法的行为使用场景实例Stubbing插桩顺序的问题插桩先行插桩后行总结Reference Mock的基本原理 通过生成子类的方式,拦截具体的方法调用,将方法调用转发至Mock代码,从而实现方法返回值或者方法执行体的自定义。 Mock和Spy Mock和Spy的基本区别——在没有进行任何方法调用Mock的情况下,Mock根据全局配置根据方法调用返回值类型返回一个默认值,不会进行具体的方法调用;而Spy对象会默认调用真实的方法。 调用方法的行为 初识 Mockito 这个测试框架后,我们要使用 Mock 的属性创建一个被测试类实例时,大概会下面这么纯手工来打造。 假定类 UserService 有一个属性 UserDao userDao, 需要构造 UserService 实例时 Mock 内部状态 UserDao...
mockito 依赖注入是诸如Spring和EJB之类的Control容器反转的非常强大的功能。 将注入的值封装到私有字段中总是一个好主意。 但是,自动连线字段的封装会降低可测试性。 我喜欢Mockito解决此问题以模拟自动装配字段的方式。 将在示例中进行解释。 (此博客文章希望您对Mockito语法有点熟悉,但是它具有足够的自我描述性。) 这是测试模块的第一个依赖项。 是Spring单例豆...
混合使用字段注入和构造器注入后,当采用mockitio来进行mock字段注入的bean时该字段一直为null private final ServiceA serviceA @Autowired private ServiceB serviceB public CurrentService(ServiceA serviceA){ this.serviceA = serviceA 上述示例的中的ServiceB在使用mockitio 测试想要stub掉serviceB时一直会空
在使用Mockito和PowerMock写单测的时候发现,如果使用了PowerMock的@PrepareForTest注解,JaCoCo在统计代码覆盖率的时候就会忽略注解@PrepareForTest({})里面的类,导致覆盖率统计不准确 对于一些工具类,我们可以先使用@PrepareForTest完成单测的开发,再单独对工具类写单测就行了,只要@PrepareForTest里面的类不是自己的项目代码就可以了(比方说第三方jar包提供的), 但是在遇到系统类的时候就不行了。因为PowerMockmock
前面介绍了不少写单元测试的内容,比方说Mockito和PowerMockito, JUnit 5,经常写单元测试的想必对这些框架都比较熟悉。 这篇博客主要介绍下数据库驱动测试框架–DbUnit(http://dbunit.sourceforge.net/), 主要从DbUnit的设计原理和实际使用来展开,这里的使用我又分为三个部分: 基于spring-test-dbunit的使用 基于dbunit本身api的使用 在dbunit的基础上整合了公司自己的jdbc框架完成的工具类 DBUnit 设计原理 在平时开发过程中,我们往往会遇到以下问题 1.由于依赖调用的接口没有开发完成,需要等待(客户端和服务端,服务端和其他服务之间) 2.自测时由于服务器故障等无法正常调用接口,或者一些边界条件无法在测试环境模拟数据 3.同样的单元测试,当依赖的数据发生变化时,无法反复执行,不能在上线前对之前的功能进行自动回归 mock就帮我们解决了以上问题 mock的定义(what): mock是在测试...
Mockito 是一个流行的 Java 测试框架,它支持使用 mock 对象进行单元测试Mockito 的核心思想是模拟对象,以便在测试过程中隔离代码的依赖关系。通过模拟对象,开发人员可以更容易地编写单元测试,并且不需要在测试中使用实际的依赖项。 Mockito源码分析可以帮助我们更好地了解 Mockito 的工作原理和实现机制,以及如何使用 Mockito 进行单元测试Mockito 的核心是 mock 对象。Mock 对象是 Java 对象的模拟实现,它可以在测试中代替真正的对象。Mockito 提供了一组 API 来创建和配置 mock 对象,包括 mock()、when()、thenReturn() 等方法。下面是一个简单的示例: List<String> mockedList = mock(List.class); when(mockedList.get(0)).thenReturn("first"); 在这个示例中,我们使用 Mockito 创建了一个 List 类的 mock 对象。然后,我们使用 when() 方法来指定当调用 mockedList.get(0) 方法时应该返回什么值。 Mockito 的另一个重要的组件是 ArgumentMatcher。ArgumentMatcher 是用于验证方法参数的接口。Mockito 提供了一组内置的 ArgumentMatcher,例如 anyInt()、eq()、anyString() 等。 Mockito 还提供了一个注解 @Mock,用于自动创建 mock 对象。例如: public class MyTest { @Mock private List<String> mockedList; @Before public void setUp() { MockitoAnnotations.initMocks(this); @Test public void test() { when(mockedList.get(0)).thenReturn("first"); String value = mockedList.get(0); assertEquals("first", value); 在这个示例中,我们使用 @Mock 注解自动创建了一个 List 类的 mock 对象。在 setUp() 方法中,我们调用了 MockitoAnnotations.initMocks(this) 来初始化 mock 对象。然后,在 test() 方法中,我们使用 when() 方法来指定当调用 mockedList.get(0) 方法时应该返回什么值。 Mockito 的核心实现机制是使用 Java 动态代理来创建 mock 对象。Mockito 使用 Java 动态代理来创建一个实现了 mock 对象接口的代理类。当测试代码调用代理对象的方法时,Mockito 捕获对该方法的调用,并根据预定义的行为返回结果。 总之,Mockito 是一个流行的 Java 测试框架,它通过模拟对象来隔离代码的依赖关系,从而使单元测试更容易编写。Mockito源码分析可以帮助我们更好地了解 Mockito 的工作原理和实现机制,并且可以帮助我们更好地使用 Mockito 进行单元测试