【问题标题】:Mocking a method inside another method在另一个方法中模拟一个方法
【发布时间】:2020-08-29 18:22:00
【问题描述】:

这是我正在测试的方法,我正在使用 mockito 进行模拟:

 /**
     * Sync get all children under single zk node.
     *
     * @param zk
     *          zookeeper client
     * @param node
     *          node path
     * @return direct children
     * @throws InterruptedException
     * @throws IOException
     */
    public static List<String> getChildrenInSingleNode(final ZooKeeper zk, final String node, long zkOpTimeoutMs)
            throws InterruptedException, IOException, KeeperException.NoNodeException {
        final GetChildrenCtx ctx = new GetChildrenCtx();
        getChildrenInSingleNode(zk, node, new GenericCallback<List<String>>() {
            @Override
            public void operationComplete(int rc, List<String> ledgers) {
                synchronized (ctx) {
                    if (Code.OK.intValue() == rc) {
                        ctx.children = ledgers;
                    }
                    ctx.rc = rc;
                    ctx.done = true;
                    ctx.notifyAll();
                }
            }
        });

        synchronized (ctx) {
            long startTime = System.currentTimeMillis();
            while (!ctx.done) {
                try {
                    ctx.wait(zkOpTimeoutMs > 0 ? zkOpTimeoutMs : 0);
                } catch (InterruptedException e) {
                    ctx.rc = Code.OPERATIONTIMEOUT.intValue();
                    ctx.done = true;
                }
                // timeout the process if get-children response not received
                // zkOpTimeoutMs.
                if (zkOpTimeoutMs > 0 && (System.currentTimeMillis() - startTime) >= zkOpTimeoutMs) {
                    ctx.rc = Code.OPERATIONTIMEOUT.intValue();
                    ctx.done = true;
                }
            }
        }
        if (Code.NONODE.intValue() == ctx.rc) {
            throw new KeeperException.NoNodeException("Got NoNode on call to getChildren on path " + node);
        } else if (Code.OK.intValue() != ctx.rc) {
            throw new IOException("Error on getting children from node " + node);
        }
        return ctx.children;
    }

这是我的测试课:

@RunWith(value= Parameterized.class)

public class ZkUtilsGetChildrenTest  {


    private boolean expectedResult;
    private ZooKeeper zkc ;
    private String path;
    private long timeout;
    private static List<String> paths = Arrays.asList("/ledgers/000/000/000/001", "/ledgers/000/000/000/002",
            "/ledgers/000/000/000/003");
    private static List<String> childPaths = Arrays.asList("001", "002", "003");
    // ZooKeeper related variables
    private static ZooKeeperUtil zkUtil = new ZooKeeperUtil();

    @Mock
    ZkUtils.GetChildrenCtx mocked = mock(ZkUtils.GetChildrenCtx.class) ;


    @BeforeClass
    public static  void setUp() throws Exception {

        zkUtil.startCluster();
        ZooKeeper initializerZkc = new ZooKeeper(zkUtil.getZooKeeperConnectString(), 10000, null);

        for (String path : paths ){

            ZkUtils.createFullPathOptimistic(initializerZkc, path, "data".getBytes() , ZooDefs.Ids.OPEN_ACL_UNSAFE ,
                    CreateMode.CONTAINER);

        }
    }

    @AfterClass
    public static void tearDown() throws Exception {

        zkUtil.killCluster();

    }


    @Parameterized.Parameters
    public static Collection<Object[]> getTestParameters() throws IOException {
        return Arrays.asList(new Object[][]{


                {false , "null" , "/ledgers/000/000/000/004" , 0},

                {true , "new" , "/ledgers/000/000/000" , 1000 },

                {false , "wrong" , "/ledgers/000/000/000/00b" , -1},

                {false , "new" , "/ledgers/000/000/00b" , 0 },//aggiunto per migliorare statement e branch coverage

                {false , "new" , "/ledgers/000/000/003" , 1 },//aggiunto per migliorare statement e branch coverage

                {false , "mock" , "/ledgers/000/000" , 1000 },//aggiunto per migliorare statement coverage

        });

    }


    public ZkUtilsGetChildrenTest(boolean expectedResult ,String zkc , String path , long timeout) throws IOException {

        if(zkc == "null"){

            this.zkc = null;

        }else if( zkc == "wrong"){

            this.zkc = new ZooKeeper("wrongString", 10000, null);

        }else if(zkc == "new"){

            this.zkc = new ZooKeeper(zkUtil.getZooKeeperConnectString(), 10000, null);

        }else if(zkc == "mock"){

            //TODO MOCK THE INNER METHOD
            this.zkc = new ZooKeeper(zkUtil.getZooKeeperConnectString(), 10000, null);
            when(mocked).thenThrow(new InterruptedException());
        }

        this.expectedResult = expectedResult;
        this.path = path;
        this.timeout = timeout;


    }

    @Test
    public void testGetChildrenInSingleNode() {

        boolean realResult;

        try {


            List<String> children = ZkUtils.getChildrenInSingleNode(zkc, path, timeout);

            assertThat(children, is(childPaths));



        } catch (Exception e) {

            realResult = false;
            e.printStackTrace();

            assertEquals(expectedResult, realResult);
        }



    }

 }

我想问你如何模拟方法 ctx.wait(zkOpTimeoutMs > 0 ? zkOpTimeoutMs : 0);这样我就可以触发该语句后面的 catch 块:如果我调用此方法,我将模拟此类的一个 istance 并在该模拟上调用该方法,但因为不是我调用此方法,而是由下面的方法调用测试,我怎样才能正确地模拟它?

祝大家今天好!

【问题讨论】:

    标签: java unit-testing intellij-idea mocking mockito


    【解决方案1】:

    如果您可以使用 Powermock 到 mock the creation of new objects,并且您不能或不想重构现有代码,则可以使用它。使用 Powermock 并非没有缺点,但它是一种强大的方法,可以更精细地控制这种情况和其他情况(静态类/方法和私有/受保护类/方法/字段)。

    【讨论】:

    • 我尽快尝试发布更新!但如果可能的话,我更喜欢 mockito 实现,因为我读到 mockito 和 powermock 库可能是冲突的。
    • 我将它们一起使用没有问题,但过去由于 Powermock 的存在而出现了代码覆盖问题。但是 Powermock 和 Mockito 应该齐头并进。他们可以使用一些相同的方法名称,因此请确保您使用了正确的包名称。如果您确实遇到任何冲突,我肯定很想知道它,也许我们也可以解决这些问题!
    • 更新:我尝试按照您链接的指南进行操作,但我的问题有点不同...通常您可以模拟一个类的对象实例并使用该对象:我的问题是不是它不是由我创建的对象,而是由我正在测试的方法创建的,所以我需要模拟方法调用 ctx.wait(zkOpTimeoutMs > 0 ? zkOpTimeoutMs : 0);在任何 ctx 对象上,因为我无法创建和传递我自己的 ctx 实例。
    • 不,这个想法是你在模拟 GetChildrenCtx 对象,对吗?更具体地说,您正在模拟构造函数以拦截对 new GetChildrenCtx 的任何调用。您想要做的是每当调用 ctx.wait 时(并且您可以指定是否希望它在某个参数或任何参数上触发),它应该抛出一个错误。通常是通过doThrow。但是 Powermock 很棘手,需要一些工作才能正确。它还取决于它是否是静态方法等。
    • 所以我把最后一条评论搞砸了,但你可以让它从构造函数拦截器返回一个模拟 - PowerMockito.whenNew(Bar.class).withNoArguments().thenReturn(mockBar);
    猜你喜欢
    • 2019-06-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多