mockito 很好mock框架

本文介绍Mockito框架在Java单元测试中的应用,包括创建mock对象、验证方法调用、模拟真实对象等技巧,并展示了如何结合行为驱动开发(BDD)风格进行测试编写。

 

package com.zero.orzprofiler.mockito;

import org.junit.Test;
import java.util.*;

import static org.mockito.Mockito.*;
/**
 * User: luochao
 * Date: 14-1-3
 * Time: 上午9:54
 */
public class MockitoTest {
    @Test
    public  void testVerify() throws RuntimeException{
        //mock create
        List mockedList = mock(List.class);
        mockedList.add("one");
        mockedList.clear();

        verify(mockedList).add("one");
        verify(mockedList).clear();
        when(mockedList.get(0)).thenReturn("l234");
//        when(mockedList.get(1)).thenThrow(new RuntimeException("runtime exception..."));

        System.out.println(mockedList.get(0));
        System.out.println(mockedList.get(2));
        System.out.println(mockedList.get(1));
        List spyList = new LinkedList();
        List spy = spy(spyList);
        spy.add("one");
        when(spy.size()).thenReturn(100);
        verify(spy).add("one");
        System.out.println(spy.size());
        doReturn("lccx").when(spy).get(2);

        //verify(mockedList).add("two");
        System.out.println(spy.get(2));

    }
}

 timetunel初始化参数

package com.taobao.timetunnel.tunnel;

import static java.lang.Thread.currentThread;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.doReturn;

import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;

import com.taobao.timetunnel.InjectMocksSupport;
import com.taobao.timetunnel.message.Message;
import com.taobao.timetunnel.session.Attribute;
import com.taobao.timetunnel.session.Session;
import com.taobao.timetunnel.tunnel.ByteBufferMessageWatchers;
import com.taobao.timetunnel.tunnel.Cursor;
import com.taobao.timetunnel.tunnel.Group;
import com.taobao.timetunnel.tunnel.Watchable;
import com.taobao.timetunnel.tunnel.Watcher;
import com.taobao.util.Bytes;

/**
 * @{link WatcherTest}
 * @author <a href=mailto:jushi@taobao.com>jushi</a>
 * @created 2010-11-29
 * 
 */
public class WatcherTest extends InjectMocksSupport {
    @Mock
   private Session session;
   @Mock
   public Message<ByteBuffer> message;

   private Watcher<ByteBuffer> watcher;

   private static final int TIMES = 10;

   private final FakeWatchable watchable = new FakeWatchable();
  @Before
  public void setUp() throws Exception {
    doReturn(3).when(session).intValueOf(Attribute.receiveWindowSize);
    doReturn("dw").when(session).stringValueOf(Attribute.subscriber);
    doReturn(Bytes.toBuffer(0)).when(message).content();
    watcher = new ByteBufferMessageWatchers(watchable).watcher(session);
  }
@Test
  public void shouldAckedGetAndRefluxEqualsPushed() throws Exception {
    int ackedGet = 0;
    int size = 0;
    while (ackedGet + size < TIMES) {
      ackedGet += size;
      size = watcher.ackAndGet().size();
    }
    watcher.dispose();
    final int pushed = watchable.count.get();
    assertThat(watchable.reflux + ackedGet, is(pushed));
  }



  /**
   * @{link FakeWatchable}
   */
  private final class FakeWatchable implements Watchable<ByteBuffer> {


    @Override
    public void onAvaliable(final Watcher<ByteBuffer> watcher) {
      new Thread(new Runnable() {

        @Override
        public void run() {
          fireOnMessageReceived(watcher);
        }
      }, "FireOnMessageReceivedTask").start();
    }

    protected void fireOnMessageReceived(final Watcher<ByteBuffer> watcher) {
      System.out.println(currentThread().getName() + currentThread().getId());
      for (;;) {
        final Iterator<Message<ByteBuffer>> itr = Collections.singleton(message).iterator();
        final Cursor<Message<ByteBuffer>> cursor = new Cursor<Message<ByteBuffer>>() {

          @Override
          public Message<ByteBuffer> next() {
            if (itr.hasNext()) return itr.next();
            return null;
          }
        };
        watcher.onMessageReceived(cursor);
        if (itr.hasNext()) break;
        count.incrementAndGet();
      }
    }

    private int reflux;
    private final AtomicInteger count = new AtomicInteger();

    @Override
    public Group<ByteBuffer> groupOf(Watcher<ByteBuffer> watcher) {
      return new Group<ByteBuffer>() {

        @Override
        public void reclaim(Session session, List<Message<ByteBuffer>> reflux) {
          FakeWatchable.this.reflux += reflux.size();
        }

        @Override
        public void dispose() {}

      };
    }

    @Override
    public void onHasUselessMessages(Watcher<ByteBuffer> watcher) {}
}

 

1. Shorthand for mocks creation - @Mock annotation

  • Minimizes repetitive mock creation code.
  • Makes the test class more readable.
  • Makes the verification error easier to read because the field name is used to identify the mock.
1 public class ArticleManagerTest {
2    
3     @Mock private ArticleCalculator calculator;
4     @Mock private ArticleDatabase database;
5     @Mock private UserProvider userProvider;
6    
7     private ArticleManager manager;
Important!  This needs to be somewhere in the base class or a test runner:
1MockitoAnnotations.initMocks(testClass);
when(mock.someMethod("some arg"))
02   .thenThrow(new RuntimeException())
03   .thenReturn("foo");
04 
05//First call: throws runtime exception:
06 mock.someMethod("some arg");
07 
08//Second call: prints "foo"
09 System.out.println(mock.someMethod("some arg"));
10 
11//Any consecutive call: prints "foo" as well (last stubbing wins).
12 System.out.println(mock.someMethod("some arg"));

 doReturn()|doThrow()doAnswer()|doNothing()|doCallRealMethod() family of methods

 

2. Spying on real objects

You can create spies of real objects. When you use the spy then the  real  methods are called (unless a method was stubbed).

Real spies should be used carefully and occasionally, for example when dealing with legacy code.

Spying on real objects can be associated with "partial mocking" concept. Before the release 1.8, Mockito spies were not real partial mocks. The reason was we thought partial mock is a code smell. At some point we found legitimate use cases for partial mocks (3rd party interfaces, interim refactoring of legacy code, the full article is here)

01 List list = new LinkedList();
02List spy = spy(list);
03 
04//optionally, you can stub out some methods:
05 when(spy.size()).thenReturn(100);
06 
07//using the spy calls *real* methods
08 spy.add("one");
09 spy.add("two");
10 
11//prints "one" - the first element of a list
12 System.out.println(spy.get(0));
13 
14//size() method was stubbed - 100 is printed
15System.out.println(spy.size());
16 
17//optionally, you can verify
18 verify(spy).add("one");
19 verify(spy).add("two");
//you can create partial mock with spy() method:   
2 List list = spy(new LinkedList());
3 
4//you can enable partial mock capabilities selectively on mocks:
5 Foo mock = mock(Foo.class);
6//Be sure the real implementation is 'safe'.
7//If real implementation throws exceptions or depends on specific state of the object then you're in trouble.
8 when(mock.someMethod()).thenCallRealMethod();


reset(mock);



行为驱动开发

19. Aliases for behavior driven development (Since 1.8.0)

Behavior Driven Development style of writing tests uses //given //when //then comments as fundamental parts of your test methods. This is exactly how we write our tests and we warmly encourage you to do so!

Start learning about BDD here: http://en.wikipedia.org/wiki/Behavior_Driven_Development

The problem is that current stubbing api with canonical role of when word does not integrate nicely with //given //when //then comments. It's because stubbing belongs to given component of the test and not to the when component of the test. Hence BDDMockito class introduces an alias so that you stub method calls with BDDMockito.given(Object) method. Now it really nicely integrates with the given component of a BDD style test!

Here is how the test might look like:

01 import static org.mockito.BDDMockito.*;
02 
03 Seller seller = mock(Seller.class);
04 Shop shop = new Shop(seller);
05 
06 public void shouldBuyBread() throws Exception {
07   //given 
08   given(seller.askForBread()).willReturn(new Bread());
09    
10   //when
11   Goods goods = shop.buyBread();
12    
13   //then
14   assertThat(goods, containBread());
15

 

### 为什么在 Java 单元测试中使用 Mockito 创建 Mock 服务对象而不是直接硬编码固定值 在 Java 单元测试中,使用 Mockito 创建 Mock 服务对象的主要优势在于提升测试的灵活性、可维护性和准确性。相比直接在方法中硬编码固定值,Mockito 提供了对依赖行为的精确控制,使测试更加贴近真实场景并减少副作用。 #### 1. 提升测试的隔离性 Mockito 允许开发者模拟特定方法的返回值,而无需依赖真实的服务实现或外部资源。这种方式可以有效隔离被测代码与外部依赖之间的耦合,确保测试仅关注当前逻辑的正确性。例如,当测试一个调用远程 API 的服务时,Mockito 可以模拟该 API 的响应,而无需真正发起网络请求。 ```java MyService mockService = mock(MyService.class); when(mockService.fetchData()).thenReturn("Mocked Data"); ``` 这种隔离性可以避免因外部服务不可用或响应不稳定而导致的测试失败[^1]。 #### 2. 支持验证方法调用次数与顺序 Mockito 提供了丰富的验证机制,可以验证方法是否被调用、调用次数以及调用顺序。例如,`verify(mockService, times(3)).fetchData()` 可以确保 `fetchData()` 方法被调用三次,而 `verify(mockService, never()).fetchData()` 则用于验证方法从未被调用。这种能力是硬编码无法实现的,它帮助开发者确保代码逻辑的正确执行路径。 #### 3. 降低测试维护成本 在实际项目中,一个 Service 类可能依赖多个其他类,如果每次测试都手动构造这些依赖,不仅繁琐而且容易出错。使用 Mockito 创建 Mock 对象,可以避免手动管理依赖的复杂性,从而显著降低测试维护成本[^2]。 #### 4. 支持多种调用行为的模拟 Mockito 支持模拟异常抛出、延迟响应、多参数匹配等复杂行为,这在硬编码中难以实现。例如: ```java when(mockService.process(anyString())).thenThrow(new RuntimeException("Error")); ``` 这种灵活性使得测试可以覆盖更多边界条件和异常场景,提升测试覆盖率和代码健壮性。 #### 5. 与测试框架集成良好 Mockito 可以很好地与 JUnit、TestNG 等主流测试框架集成,尤其在 Spring Boot 项目中,`@MockBean` 和 `@DataJpaTest` 等注解进一步简化了测试配置,使得模拟对象的使用更加自然和高效[^3]。 --- ### 总结 使用 Mockito 创建 Mock 服务对象,相比直接硬编码固定值,能够提供更强大的控制能力、更高的可维护性以及更强的测试可靠性。Mockito 的设计目标是帮助开发者专注于被测逻辑本身,而不是其依赖的实现细节。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值