zoukankan      html  css  js  c++  java
  • 单元测试之Mockito学习笔记

    之前用的单元测试用的都是Junit,看有的单元测试会用到Mockito,学习记录下笔记。

    主要看的是b站视频:https://www.bilibili.com/video/BV1jJ411A7Sv

    好的类似笔记有:https://blog.csdn.net/yangshengwei230612/category_9737103.html

    Mockito中文文档:https://gitee.com/libearys/mockito-doc-zh   (很有用)

    1.单元测试简介

    和单元测试相关的:

    junit
    
    testNg
    
    powermock
    
    easymock
    
    mockito
    
    jmock

    作者认为上面最好的是powermock和mockito

    powermock是对mockito的补充,可以mock静态方法、final类型的方法、final类型的class和局部变量。mockito不能做这几个mock。

    绝佳:用mockito+powermock+junit的组合。

    从功能测试角度看的工具:

         concordion

        cucumber:DDD主流,功能强大

    比如关注数据的input和output的报表,就会用到concordion,关注到某一个功能相关的,就会用到cucumber。

    集成工具:

        jekins

        git/github/git workflow

    好的单元测试的特征:

    自动化
    
    执行要够快
    
    每个test不能依赖其他test(独立、任意顺序执行)
    
    test不能依赖外部资源(数据库、文件、网络资源、第三方API接口等)
    
    任何时间和任何环境执行结果一样
    
    test必须有意义(没必要测试get、set、toString和代码自动生成的这些方法)
    
    test要短
    
    test和业务代码一样对待
    
    配置、源代码、测试代码,都要有版本控制。【上线后,哪怕改动配置文件,也要做充分的测试、公告、版本管理】

    实际上,测试时是需要依赖很多外部资源的,比如数据库、调用第三方接口的入参、文件等。并且对这些资源修改后,还有对齐还原,就会又很耗费资源---》解决方法:mock数据。

    mock就是替身。

    2.快速入门

    1mockito干什么的?

    获取数据库连接并且读写数据

    连接网络和下载文件

    发送邮件

    调用某个web的服务

    调用打印机、出报表等IO操作

    上面这些场景就不需要真正连接外部资源了,可以直接用mockito去模拟。

    2)快速开始

    步骤:

    -1)引入依赖

    引入mockito和junit

    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>
    
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-all</artifactId>
        <version>1.10.19</version>
        <scope>test</scope>
    </dependency>

    引入servlet-api:假设用户在页面的登陆行为

    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>

    servlet要有tomcat容器,用户登陆的username、password放到数据库中,就用mockito。

    -2)写Accout类

    public class Account {
    }

    -3)写AccountLoginController.java

    public class AccountLoginController {
    
        private final AccountDao accountDao;
    
        /**
         * 通过构造器的方式注入
         * @param accountDao
         */
        public AccountLoginController(AccountDao accountDao) {
            this.accountDao = accountDao;
        }
    
    
        public String login(HttpServletRequest request) {
            final String username = request.getParameter("username");
            final String password = request.getParameter("password");
            //数据库也有不可用的情况,加上try
            try {
                Account account = accountDao.findAccount(username, password);
                if(account == null) {
                    return "/login";
                }else {
                    return "/index";
                }
            }catch (Exception e) {
                return "/505";
            }
        }
    }

    -4)写AccountDao的findAccount方法

    public class AccountDao {
    
        public Account findAccount(String username, String password) {
            //模拟不可调,一调就抛异常
            throw new UnsupportedOperationException("db可用");
        }
    }

    -5)写单元测试;AccountLoginControllerTest

    @RunWith(MockitoJUnitRunner.class)
    public class AccountLoginControllerTest {
    
        private AccountDao accountDao;
    
        private HttpServletRequest request;
    
        private AccountLoginController accountLoginController;
    
        @Before
        public void setUp(){
            this.accountDao = Mockito.mock(AccountDao.class);
            this.request = Mockito.mock(HttpServletRequest.class);
            this.accountLoginController = new AccountLoginController(accountDao);
        }
    
        @Test
        public void testLoginSuccess() {
            Account account = new Account();
            //when是静态导入,实际是Mockito.when()
            when(request.getParameter("username")).thenReturn("alex");
            when(request.getParameter("password")).thenReturn("123456");
            when(accountDao.findAccount(anyString(), anyString())).thenReturn(account);
            assertThat(accountLoginController.login(request), equalTo("/index"));
        }
    
        @Test
        public void testLoginFailure() {
            when(request.getParameter("username")).thenReturn("alex");
            when(request.getParameter("password")).thenReturn("123451");
            when(accountDao.findAccount(anyString(), anyString())).thenReturn(null);
            assertThat(accountLoginController.login(request), equalTo("/login"));
        }
    
        @Test
        public void testLogin505() {
            when(request.getParameter("username")).thenReturn("alex");
            when(request.getParameter("password")).thenReturn("123451");
            when(accountDao.findAccount(anyString(), anyString())).thenThrow(UnsupportedOperationException.class);
            assertThat(accountLoginController.login(request), equalTo("/505"));
        }
    
    }

    解释:

    -1)初始化

    controller中的HttpServletRequest(前端请求)和AccountDao(数据库查询的数据)是外部资源,因此将这两个变量作为类的属性,并在setUp中通过Mock方式赋值。

    AccountController是要测试的类,也作为类的属性,并在setUp中直接new来赋值。

    -2)测试:

    login方法有三种不同的结果:登陆成功、登陆失败、数据库异常返回505,针对每种不同的结果mock然后断言测试。

    when(...).thenReturn(...):当调用...,就返回... (就模拟调用外部资源[mock对象]的返回值),这个模拟过程会替换调实际执行结果中 的调用并且作为返回值给实际执行中。

    when(...).thenThrow(...):当调用...,就抛出异常....

    assertThat(...., equalTo(....)):断言(....等于....)。调用测试的目标方法,得到实际执行结果,断言实际执行结果等于期望结果。

    anyString():Macthers.anyString() 返回任意的字符串,可以作为参数模拟。相应的还有anyInt、anyList等

     // 静态导入会使代码更简洁

     import static org.mockito.Mockito.*;

    3.几种不同的mock方式以及深度mock

    1mock方式

    mock一个对象,就是用mock的东西去替代真实依赖的外部资源(比如db、文件等)

    mock方式:

        -1@RunWith(MockitoJUnitRunner.class):在类上

        -2@Mock

           在类的属性上(mock的对象)标注@Mock注解

           写init()方法,   ---->不写这个就会报NPE

           方法上标注@Before

           在方法中要初始化mockMockitoAnnotations.initMock(this);

          (这个初始化mock什么意思?)

        -3@Rule

                        public MockitoRule mockitoRule = MockitoJUnit.rule();

    方式1:@RunWith

    @RunWith(MockitoJUnitRunner.class)
    public class MockByRunnerTest {
    
        @Test
        public void testMock() {
            AccountDao accountDao = mock(AccountDao.class);
            //AccountDao accountDao 
            = mock(AccountDao.class, Answers.RETURNS_SMART_NULLS)
    
            //调用这个方法不会抛异常,得到null(没有写when、return这些stubbing)。
            Account account = accountDao.findAccount(“x”, “x”);
    
        }
    }

    mock(classToMock):传入要mock的类

    mock(classToMock, defaultAnswer):传入要mock的类和默认的answer。当没有给mock的对象进行stubbing(when...thenReturn...),就会返回默认的answer。如果没有指定answer,也有全局的answer(根据类型来定)。

    方式2:@Mock + MockitoAnnotations.initMocks(this);

    public class MockByAnnotationTest {
    
        @Before
        public void init() {
            MockitoAnnotations.initMocks(this);
        }
    
    
         @Mock
        //@Mock(answer=Answers.RETURNS.SMART_NULLS)
        AccountDao accountDao;
    
        @Test
        public void testMock() {
            Account account = accountDao.findAccount("x", "x");
        }
    }

    给mock对象设置Answer

    @Mock

    @Mock(answer=Answer.RETURNS_SMART_NULLLS)

    方式3:@Rule + MockitoRule

    public class MockitoByRuleTest {
    
        //必须是public的属性
        @Rule
        public MockitoRule mockitoRule = MockitoJUnit.rule();
    
        @Test
        public void testMock() {
            AccountDao accountDao = mock(AccountDao.class);
            //也可以将accountDao作为属性,用@Mock标注,在test方法中直接用,代替上面这行
    
            Account account = accountDao.findAccount("x", "x");
            System.out.println(account);
        }
    
    }

    2)深度mock

    -1)写个Lesson03Service类和Lesson03

    -2)测试

    1】空指针异常

    public class DeepMockTest {
    
        @Mock
        private Lesson03Service lesson03Service;
        
        @Before
        public void init() {
            MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void testNPEMock() {
            Lesson03 lesson03 = lesson03Service.get();
            lesson03.foo();
        }
    
    }

    上面会报空指针异常。因为mock的lesson03Service调用的get方法,返回的Lesson03对象是null,再调用foo()就会报NPE。

    2stub

    public class DeepMockTest {
    
        @Mock
        private Lesson03Service lesson03Service;
    
        @Mock
        private Lesson03 lesson03;
    
        @Before
        public void init() {
            MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void testStubMock() {
            //调用lesson03Service.get()就返回mock的lesson03对象(sutb)
            when(lesson03Service.get()).thenReturn(lesson03);
            //这个res03就是mock的lesson03
            Lesson03 res03 = lesson03Service.get();
            //此时就可以调foo()方法了,因为此时的res03不是null了
            res03.foo();
        }
    
    
    }

    3】深度mock

    public class DeepMockTest {
        
        //深度mock,mock了lesson03Service,也自动mock了调用它的方法的返回值
        @Mock(answer = Answers.RETURNS_DEEP_STUBS)
        private Lesson03Service lesson03Service;
    
        @Before
        public void init() {
            MockitoAnnotations.initMocks(this);
        }
        
        @Test
        public void testDeepMock() {
            lesson03Service.get().foo();
        }
        
    }

    深度mock中,自动帮助mock的返回值不一定是我们想要的返回值。

    4.Stub语法详解

    stub有人也称为测试桩。可以不用翻译。

    stubmock的理解:

           stub是对方法的stub,调用mock对象的某个方法,进而返回某个值

           mock是模拟的意思,mock一个对象

    easyMock中:

              when().thenReturn() ---->称为录制,约定当调用什么,其中传什么参数,就返回什么内容

       后面的实际调用,就按照约定的形式返回-----播放

    上面的when().thenReturn() 类似这种操作就是stub

    使用Stub,我们只关注方法的调用和返回的结果。一个方法被stub后,该方法就只返回该结果。

    注意:

          staticfinal方法,无法进行stub操作

      当连续为一个方法stub操作,只会调用最近的一次。

    测试:

    @RunWith(MockitoJUnitRunner.class)
    public class StubblingTest {
    
        private List<String> list;
    
        @Before
        public void inti() {
            this.list = mock(ArrayList.class);
        }
    
        @Test
        public void howToStubblingReturn() {
            //实例list中没有放数据,这边的stubbling只是mock一种行为:
            // 当从list取出第一个元素list.get(0),就返回"first"
            when(list.get(0)).thenReturn("first");
            assertThat(list.get(0), equalTo("first"));
        }
    
        @Test
        public void howToStubblingException() {
    
            //入参为任何数字,抛出异常
            when(list.get(anyInt())).thenThrow(new RuntimeException());
            try {
                list.get(0);
                fail();
            }catch (Exception e) {
                assertThat(e, instanceOf(RuntimeException.class));
            }
    
    
        }
    
        /**
         * 没有返回值的方法的校验执行次数的stubbling
         */
        @Test
        public void howToStubblingVoidVerifyMethod() {
            doNothing().when(list).clear();
            list.clear();
            //verify检验list是否执行了1次的clear方法
            verify(list, times(1)).clear();
        }
    
        /**
         * 没有返回值的方法的抛出异常的stubbling
         */
        @Test
        public void howToStubblingVoidException() {
            doThrow(RuntimeException.class).when(list).clear();
            try{
                list.clear();
                //这个有什么用
                fail();
            }catch (Exception e) {
                assertThat(e, instanceOf(RuntimeException.class));
            }
    
        }
    
    
        @Test
        public void stubDoReturn() {
            //这两个方法等价
            when(list.get(0)).thenReturn("first");
            doReturn("second").when(list).get(1);
    
            assertThat(list.get(0), equalTo("first"));
            assertThat(list.get(1), equalTo("second"));
        }
    
        /**
         * 相同调用,返回不同的值,最后一次调用生效
         */
        @Test
        public void stubOverride() {
            when(list.size()).thenReturn(1);
            when(list.size()).thenReturn(2);
            when(list.size()).thenReturn(3);
            when(list.size()).thenReturn(4);
    
            //前三个断言都失败
            assertThat(list.size(), equalTo(1));
            assertThat(list.size(), equalTo(2));
            assertThat(list.size(), equalTo(3));
            //断言成功
            assertThat(list.size(), equalTo(4));
    
        }
    
        @Test
        public void iterateStub() {
            when(list.size()).thenReturn(1, 2, 3, 4);
            /**和上面等价
            when(list.size()).thenReturn(1).thenReturn(2).thenReturn(3).thenReturn(4);*/
    
            //4个断言都成功,第几次调用返回第几个值,
            // 当前面的值都用完了,以后的调用都是返回的最后一个值
            assertThat(list.size(), equalTo(1));
            assertThat(list.size(), equalTo(2));
            assertThat(list.size(), equalTo(3));
            assertThat(list.size(), equalTo(4));
            //后面调用都返回最后一个返回的值
            assertThat(list.size(), equalTo(4));
            assertThat(list.size(), equalTo(4));
            assertThat(list.size(), equalTo(4));
        }
    
        /**
         * 需求:给定一个数字,返回该数字*10的字符串
         * 用thenAnswer,可以灵活写返回值
         */
        @Test
        public void stubAnswer() {
            when(list.get(anyInt())).thenAnswer(invocationOnMock -> {
                Integer index = invocationOnMock.getArgumentAt(0, Integer.class);
                return String.valueOf(index*10);
            });
            assertThat(list.get(0), equalTo("0"));
            assertThat(list.get(999), equalTo("9990"));
        }
    
    
        @Test
        public void stubThenCallRealMethod() {
            StubblingService service = mock(StubblingService.class);
            //调用mock对象的方法,不会执行原来对象的方法
    
            //调用mock对象的方法,实际调用的是代理对象的方法,返回值是默认值或者指定的值(stub)
            service.getS();
            System.out.println(service.getClass());
            //class com.yang.mockito.lessson04.StubblingService
            //$$EnhancerByMockitoWithCGLIB$$3264aa13
    
            //现在希望调用getI()可以执行原来对象的方法 ( getI()不需要外部资源 )
            //thenCallRealMethod
    
            when(service.getS()).thenReturn("alex");
            assertThat(service.getS(), equalTo("alex"));
            when(service.getI()).thenCallRealMethod();
            assertThat(service.getI(), equalTo(10));
    
        }
    
    
    
    
        @After
        public void destroy() {
            //销毁/重置stubbling的动作
            reset(this.list);
        }
    
    }

    StubblingService.java

    public class StubblingService {
        public int getI() {
            System.out.println("getI()执行....");
            return 10;
        }
    
        public String getS() {
            throw new RuntimeException();
        }
    }

    5.Spy

    mock出来的对象在调用方法时,都不会执行原来对象的方法(除非when(....).thenCallRealMethod())

        Spy可以spy一个对象,和mock出来一个对象相同。但作用不同。

        spymock都是代理对象。

    Spying作用:

        和mock对象相反,当Spy一个对象后,调用它的方法,如果该方法没有被stub,就会真正执行原来对象的真正方法。如果spy对象的方法被stub,就会返回stub的值。

       【mock对象,调用它的方法,不管有没有stub,执行它的方法,都不会执行原来对象的方法】

    spy+stub,起到部分方法mock的作用

    @RunWith(MockitoJUnitRunner.class)
    public class SpyingTest {
    
        public void testSpy() {
            List<String> realList = new ArrayList<>();
            List<String> list = spy(realList);
    
            list.add("Mockito");
            list.add("PowerMockito");
    
            //会执行realList(原对象)的方法,而不是spy的list的方法
            //如果是mock出来的对象,只会执行mock对象的方法
            assertThat(list.get(0), equalTo("Mockito"));
            assertThat(list.get(1), equalTo("PowerMockito"));
            assertThat(list.isEmpty(), equalTo(false));
    
        }
    
        /**
         * spy+stub,起到部分方法的mock
         * spy的对象,对它的某些方法stub,调用这些方法就会返回stub的值;
         * 其他没有stub的方法就会调用原来对象的方法,放回真正的值
         */
        @Test
        public void testSpyStub() {
            List<String> realList = new ArrayList<>();
            List<String> list = spy(realList);
    
            list.add("Mockito");
            list.add("PowerMockito");
    
            when(list.isEmpty()).thenReturn(true);
            when(list.size()).thenReturn(0);
    
            //该方法没有stub,就会调用原来对象的方法返回值
            assertThat(list.get(0), equalTo("Mockito"));
            //返回stub方法的mock出来的值
            assertThat(list.isEmpty(), equalTo(true));
            assertThat(list.size(), equalTo(0));
        }
        
    
    }

    采用@Spy注解的方式spy

    public class SpyingAnnotationTest {
    
        @Spy
        private List<String> list = new ArrayList<>();
    
        @Before
        public void init() {
            MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void testSpy() {
            list.add("Mockito");
            list.add("PowerMockito");
    
            when(list.size()).thenReturn(0);
    
            assertThat(list.get(0), equalTo("Mockito"));
            assertThat(list.size(), equalTo(0));
        }
    }

    6.Argument Matcher

    很关键。

    argument matcher是参数匹配器。

    比如stubmock对象的方法会传一些参数,然后返回给定的值

    当后面执行该方法,传入该参数时,matcher就会进行匹配方法和参数,相同就返回相应的值;如果一个都没匹配到,就返回返回类型的默认值。

    when().thenReturn()  都是由matcher保证不同的参数返回不同的返回值。

    isA(class<T> clazz)

    any(class<T> clazz)

    eq(primitive value)

    public class ArgumentMatcherTest {
    
        @Test
        public void basicTest() {
            List list = mock(List.class);
    
    
            //stub动作
            when(list.get(0)).thenReturn(100);
    
            //stub后,当执行mock的方法时(即list.get(0)),Matcher就起作用,判断这个方法的参数是否和stub中写的参数
            // 是否一样,判断的方法就是java中的"equals";如果参数相同,会返回stub的返回值100
            list.get(0);
            assertThat(list.get(0), equalTo(100));
    
            //参数1,就没有匹配到stub中的参数,就返回null
            list.get(1);
            assertThat(list.get(1), equalTo(null));
            assertThat(list.get(1), nullValue());
    
            //注意:写单元测试时,入参很重要,要设计好Matcher
        }
    
        /**
         * 匹配该类或者子类型
         */
        @Test
        public void testIsA() {
            Foo foo = mock(Foo.class);
            when(foo.function(isA(Parent.class))).thenReturn(100);
            int result1 = foo.function(new Child1());
            int result2 = foo.function(new Child2());
            //isA()匹配器可以匹配到Parent.class和它的子类/实现类
            assertThat(result1, equalTo(100));
            assertThat(result2, equalTo(100));
    
            //注意要重置mock(不要之前的stub),
            // 否则会对下面的有影响(一般不用,因为下面的要新建一个test)
            reset(foo);
    
            when(foo.function(isA(Child1.class))).thenReturn(100);
            int res1 = foo.function(new Child1());
            int res2 = foo.function(new Child2());
            assertThat(res1, equalTo(100));
            //断言失败,child2不是child1或它的子类。没有指定就默认返回0
            assertThat(res2, equalTo(100));
            //断言成功
            assertThat(res2, equalTo(0));
    
        }
    
    
        /**
         * 匹配任何,只要满足类型检查即可
         */
        @Test
        public void testAny() {
            Foo foo = mock(Foo.class);
            when(foo.function(any(Child1.class))).thenReturn(100);
            int result = foo.function(new Child2());
            assertThat(result, equalTo(100));
            
        }
    
    
        static class Foo {
            int function(Parent parent) {
                return parent.work();
            }
        }
    
        interface Parent {
            int work();
        }
    
        class Child1 implements Parent {
    
            @Override
            public int work() {
                throw new RuntimeException();
            }
        }
    
        class Child2 implements Parent {
    
            @Override
            public int work() {
                throw new RuntimeException();
            }
        }
    
    }

    7.WildCard Argument Matcher

    通配的参数匹配器。

    stub中方法的参数可以是任意数字(anyInt())/任意字符串(anyString())...,或者是任意类型的子类(isA()),或者直接是任意(any())....,就是WildCard Argument Matcher处理

    anyXXX()

    any()

    isA()

    eq()

    代码:

    好的习惯,在单元测试最后一个destory方法(@Before注解)reset下这个mock(mock中的这些stub行为都消除。)

    可以reset(),也可以reset某个具体的对象reset(mock对象)

    @RunWith(MockitoJUnitRunner.class)
    public class WildcardArgumentMatcherTest {
    
        @Mock
        private SimpleService simpleService;
    
        @Before
        public void init() {
            MockitoAnnotations.initMocks(this);
        }
    
        @Test
        public void wildcardMethod1() {
            when(simpleService.method1(anyInt(),
                    anyString(),
                    anyCollection(),
                    isA(Serializable.class))
            ).thenReturn(100);
            int result = simpleService
                    .method1(1, "Alex", Collections.EMPTY_LIST, "Mockito");
            assertThat(result, equalTo(100));
        }
    
        /**
         * 用法报错,如果参数中有matcher,其他参数都要是matcher
         */
        @Test
        public void testWildcardMethod1WithSpecFalse() {
            //如果参数中有一个是matcher,其他参数都要是matcher,而不是具体的值
            when(simpleService.method1(anyInt(),
                    "Alex",
                    anyCollection(),
                    isA(Serializable.class))
            ).thenReturn(100);
            when(simpleService.method1(anyInt(),
                    "Mockito",
                    anyCollection(),
                    isA(Serializable.class))
            ).thenReturn(200);
            int result = simpleService
                    .method1(1, "Alex", Collections.EMPTY_LIST, "Mockito");
            assertThat(result, equalTo(100));
            int result2 = simpleService
                    .method1(1, "Mockito", Collections.EMPTY_LIST, "Mockito");
            assertThat(result2, equalTo(200));
    
        }
    
        /**
         * 将具体的值变为eq(具体的值)---》matcher,就和其他matcher一起作为参数
         */
        @Test
        public void testWildcardMethod1WithSpecSuccess() {
            when(simpleService.method1(anyInt(),
                    eq("Alex"),
                    anyCollection(),
                    isA(Serializable.class))
            ).thenReturn(100);
            when(simpleService.method1(anyInt(),
                    eq("Mockito"),
                    anyCollection(),
                    isA(Serializable.class))
            ).thenReturn(200);
            int result = simpleService
                    .method1(1, "Alex", Collections.EMPTY_LIST, "Mockito");
            assertThat(result, equalTo(100));
            int result2 = simpleService
                    .method1(1, "Mockito", Collections.EMPTY_LIST, "Mockito");
            assertThat(result2, equalTo(200));
            int result3 = simpleService
                    .method1(1, "Other", Collections.EMPTY_LIST, "Mockito");
            assertThat(result3, equalTo(0));
    
        }
    
        @Test
        public void testMethod2() {
            List list = Collections.EMPTY_LIST;
            doNothing()
                    .when(simpleService)
                    .method2(anyInt(), anyString(), anyCollection(), isA(Serializable.class));
            simpleService
                    .method2(1, "Alex", list, "Mockito");
            //verify验证后面的给定参数的方法 在前面执行的次数
            verify(simpleService, times(1))
                    .method2(1, "Alex", list, "Mockito");
        }
    
        /**
         * 注意matcher匹配范围和顺序的关系:越在后面,优先级越高
         * 成功
         */
        @Test
        public void testOrderSuccess() {
            when(
                    simpleService.method1(
    anyInt(), anyString(), anyCollection(), isA(Serializable.class))
            ).thenReturn(-1);
            when(
                    simpleService.method1(
    anyInt(), eq("Alex"), anyCollection(), isA(Serializable.class))
            ).thenReturn(100);
            when(
                    simpleService.method1(
    anyInt(), eq("Mockito"), anyCollection(), isA(Serializable.class))
            ).thenReturn(100);
    
            //anyString()在最前面,除了"Alex","Mockito"返回100,其他string都返回-1
    
            int result1 = simpleService
                    .method1(1, "Alex", Collections.EMPTY_LIST, "Mockito");
            assertThat(result1, equalTo(100));
    
            int result2 = simpleService
                    .method1(1, "Mockito", Collections.EMPTY_LIST, "Mockito");
            assertThat(result2, equalTo(100));
    
            int result3 = simpleService
                    .method1(1, "Other", Collections.EMPTY_LIST, "Mockito");
            assertThat(result3, equalTo(-1));
    
        }
    
        /**
         * 注意matcher匹配范围和顺序的关系:越在后面,优先级越高
         * 失败
         */
        @Test
        public void testOrderFail() {
    
            when(
                    simpleService.method1(
    anyInt(), eq("Alex"), anyCollection(), isA(Serializable.class))
            ).thenReturn(100);
            when(
                    simpleService.method1(
    anyInt(), eq("Mockito"), anyCollection(), isA(Serializable.class))
            ).thenReturn(100);
            when(
                    simpleService.method1(
    anyInt(), anyString(), anyCollection(), isA(Serializable.class))
            ).thenReturn(-1);
    
            //anyString() 在最后,所以string都是返回-1,包括前面的
    
    
            int result1 = simpleService
                    .method1(1, "Alex", Collections.EMPTY_LIST, "Mockito");
            assertThat(result1, equalTo(100));
    
            int result2 = simpleService
                    .method1(1, "Mockito", Collections.EMPTY_LIST, "Mockito");
            assertThat(result2, equalTo(100));
    
            int result3 = simpleService
                    .method1(1, "Other", Collections.EMPTY_LIST, "Mockito");
            assertThat(result3, equalTo(-1));
    
        }
    
    
    
    
    
        /**
         * 好的习惯,写个after,reset下mock
         */
        @After
        public void destroy() {
            reset(simpleService);
        }
    
    
    }

    8.Hamcrest Matcher

    assertThat(String reason, T actual, Matcher<? super T> matcher)

    断言都可以调用上面的方法,不同的匹配模式就实现不同的matcher就行,这个接口不用变。

    如果是AssertEquals(Double actual, Double expected),那么要增加类型时,就需要再修改这个类,增加相应的方法。

    public class AssertMatcherTest {
    
    
        @Test
        public void test() {
            int i = 10;
            //hamcrest的matcher方式
            assertThat(i, equalTo(10));
            //not(matcher)
            assertThat(i, not(equalTo(20)));
            assertThat(i, is(10));
            assertThat(i, not(is(20)));
            //两个中一个通过就行:either(matcher).or(matcher)
            assertThat(i, either(equalTo(20)).or(equalTo(10)));
            //两个都要满足:both(matcher).and(matcher)
            assertThat(i, both(equalTo(10)).and(not(equalTo(20))));
            //任何一个满足:anyOf(matcher1, matcher2, matcher3)
            assertThat(i, anyOf(equalTo(10), is(20), not(equalTo(30))));
            //都要满足:allOf(matcher1, matcher2, matcher3)
            assertThat(i, allOf(equalTo(10), is(10), not(equalTo(20)), not(is(20))));
    
            //传统的junit方式
            Assert.assertEquals(i, 10);
        }
    
        @Test
        public void testDesc() {
            int i = 10;
            //assertThat(String reason, Object actual, Matcher matcher)
            //失败时,会给出reason的原因
            assertThat("i 要等于10", i, equalTo(20));
        }
    
    
    }

    9.自定义Matcher

    一般不用自定义扩展,因为本身已经提供了很丰富的内容。

    步骤:

    -1GreaterThan类继承BaseMatcher

    public class GreaterThan<T extends Number> extends BaseMatcher<T> {
    
        private final T value;
        private GreaterThan(T value) {
            this.value = value;
        }
    
        /**
         *
         * @param actual 就是assertThat(10, gt(5))中传来的10
         * @return
         */
        @Override
        public boolean matches(Object actual) {
            Class<?> clazz = actual.getClass();
            if(clazz == Integer.class) {
                //value是构造器gt(5)中传来的值5
                return (Integer) actual > (Integer) value;
            }else if(clazz == Short.class){
                return (Short) actual > (Short) value;
            }else if(clazz == Long.class) {
                return (Long) actual > (Long) value;
            }else if(clazz == Byte.class) {
                return (Byte) actual > (Byte) value;
            }else if(clazz == Float.class) {
                return (Float) actual > (Float) value;
            }
            throw new RuntimeException("不支持" + clazz.getName() + "类型");
        }
    
        /**
         * 调用gt(value),实际就返回GreaterThan对象
         * @Factory标明这个是个工厂返回,没什么用
         */
        @Factory
        public static <T extends Number> GreaterThan<T> gt(T value) {
            return new GreaterThan<>(value);
        }
    
    
        @Override
        public void describeTo(Description description) {
            description.appendText("比较数字失败");
        }
    }

    -2)实际调用

    public class SimpleTest {
    
        @Test
        public void test() {
            //自定义lt、gt、
            assertThat(10, GreaterThan.gt(20));
        }
    }

    上面的GreaterThan类有个很大的问题,当有其他类型要判断时(比如以后增加新的类型,或者现在有些类型没有写),就需要修改GreaterThan类。

    就是GraterThan类做了不止一件事,又要比较,又要判断类型。

    解决方法:把判断类型的逻辑抽离出来。

    扩展版:

    -1)写Compare接口

    public interface Compare {
    
        boolean compare(Object expected);
    }

    -2)写默认的Compare实现:DefaultCompare

    public class DefaultCompare<T extends Number>  implements Compare{
    
        private T value;
    
        public DefaultCompare(T value) {
            this.value = value;
        }
    
    
        @Override
        public boolean compare(Object actual) {
            Class<?> clazz = actual.getClass();
            if(clazz == Integer.class) {
                //value是构造器gt(5)中传来的值5
                return (Integer) actual > (Integer) value;
            }else if(clazz == Short.class){
                return (Short) actual > (Short) value;
            }else if(clazz == Long.class) {
                return (Long) actual > (Long) value;
            }else if(clazz == Byte.class) {
                return (Byte) actual > (Byte) value;
            }else if(clazz == Float.class) {
                return (Float) actual > (Float) value;
            }
            throw new RuntimeException("不支持" + clazz.getName() + "类型");
        }
    
    
    }

    -3)写GreaterThanNew

    public class GreaterThanNew<T extends Number> extends BaseMatcher<T> {
    
        private T value;
        private Compare compare;
    
    
        private GreaterThanNew(T value, Compare compare) {
            this.value = value;
            this.compare = compare;
        }
    
    
    
        @Override
        public boolean matches(Object o) {
            return compare.compare(o);
        }
    
        @Override
        public void describeTo(Description description) {
    
        }
    
        public static GreaterThanNew gt(Number o) {
            return new GreaterThanNew(o, new DefaultCompare<>(o));
        }
    
        /**
         *如果有新的类型,就实现新的compare
         */
        public static GreaterThanNew gt(Number o, Compare compare) {
            return new GreaterThanNew(o, compare);
        }
    
    }

    -4)测试

    public class GreaterThanNewTest {
    
        @Test
        public void test1() {
            assertThat(10, GreaterThanNew.gt(1));
        }
    
    }
  • 相关阅读:
    Linux内核RPC请求过程
    二分图
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 合并石子
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 摩尔斯电码
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 文本加密
    Java蓝桥杯 算法提高 九宫格
    Java蓝桥杯 算法提高 九宫格
  • 原文地址:https://www.cnblogs.com/yq055783/p/14496804.html
Copyright © 2011-2022 走看看