Издевательство над классом и интерфейсом

#java #unit-testing #interface #mockito

#ява #модульное тестирование #интерфейс #mockito #java

Вопрос:

Привет всем, я до сих пор пытался использовать mockito в своем тесте. Вот мой вопрос: могу ли я издеваться над интерфейсом, чтобы каждый класс, реализующий этот интерфейс, вел себя так, как описано в mock?

Это метод, который я тестирую:

 public static boolean initNewCluster(ServerConfiguration conf) throws Exception {
    return runFunctionWithRegistrationManager(conf, rm -> {
        try {
            return rm.initNewCluster();
        } catch (Exception e) {
            System.out.println("MOCK WORKS!!! n");
            throw new UncheckedExecutionException(e.getMessage(), e);
        }
    });
}
  

И мне нужно было бы издеваться над вызовом rm.initNewCluster().
Теперь это интерфейс, который имеет initNewCluster() в качестве метода:

 @LimitedPrivate
@Evolving
public interface RegistrationManager extends AutoCloseable {

    

    /**
     * Initializes new cluster by creating required znodes for the cluster. If
     * ledgersrootpath is already existing then it will error out.
     *
     * @return returns true if new cluster is successfully created or false if it failed to initialize.
     * @throws Exception
     */

    boolean initNewCluster() throws Exception;
  

И вот класс, который реализует этот интерфейс:

     /**
     * ZooKeeper Based {@link RegistrationManager}.
     */
    @Slf4j
    public class ZKRegistrationManager implements RegistrationManager {
    
        private static final Function<Throwable, BKException> EXCEPTION_FUNC = cause -> {
            if (cause instanceof BKException) {
                log.error("Failed to get bookie list : ", cause);
                return (BKException) cause;
            } else if (cause instanceof InterruptedException) {
                log.error("Interrupted reading bookie list : ", cause);
                return new BKInterruptedException();
            } else {
                return new MetaStoreException();
            }
        };
    
        private final ServerConfiguration conf;
        private final ZooKeeper zk;
        private final List<ACL> zkAcls;
        private final LayoutManager layoutManager;
    
        private volatile boolean zkRegManagerInitialized = false;
    
        // ledgers root path
        private final String ledgersRootPath;
        // cookie path
        private final String cookiePath;
        // registration paths
        protected final String bookieRegistrationPath;
        protected final String bookieReadonlyRegistrationPath;
        // session timeout in milliseconds
        private final int zkTimeoutMs;
    
        public ZKRegistrationManager(ServerConfiguration conf,
                                     ZooKeeper zk,
                                     RegistrationListener listener) {
            this(conf, zk, ZKMetadataDriverBase.resolveZkLedgersRootPath(conf), listener);
        }
    
        public ZKRegistrationManager(ServerConfiguration conf,
                                     ZooKeeper zk,
                                     String ledgersRootPath,
                                     RegistrationListener listener) {
            this.conf = conf;
            this.zk = zk;
            this.zkAcls = ZkUtils.getACLs(conf);
            this.ledgersRootPath = ledgersRootPath;
            this.cookiePath = ledgersRootPath   "/"   COOKIE_NODE;
            this.bookieRegistrationPath = ledgersRootPath   "/"   AVAILABLE_NODE;
            this.bookieReadonlyRegistrationPath = this.bookieRegistrationPath   "/"   READONLY;
            this.zkTimeoutMs = conf.getZkTimeout();
    
            this.layoutManager = new ZkLayoutManager(
                zk,
                ledgersRootPath,
                zkAcls);
    
            this.zk.register(event -> {
                if (!zkRegManagerInitialized) {
                    // do nothing until first registration
                    return;
                }
                // Check for expired connection.
                if (event.getType().equals(EventType.None)
                    amp;amp; event.getState().equals(KeeperState.Expired)) {
                    listener.onRegistrationExpired();
                }
            });
        }
    
.
.
.
.
.
.
    
    
       
    
        @Override
        public boolean initNewCluster() throws Exception {
            System.out.println("I have been called ! n");
            String zkServers = ZKMetadataDriverBase.resolveZkServers(conf);
            String instanceIdPath = ledgersRootPath   "/"   INSTANCEID;
            log.info("Initializing ZooKeeper metadata for new cluster, ZKServers: {} ledger root path: {}", zkServers,
                    ledgersRootPath);
    
            boolean ledgerRootExists = null != zk.exists(ledgersRootPath, false);
    
            if (ledgerRootExists) {
                log.error("Ledger root path: {} already exists", ledgersRootPath);
                return false;
            }
    
            List<Op> multiOps = Lists.newArrayListWithExpectedSize(4);
    
            // Create ledgers root node
            multiOps.add(Op.create(ledgersRootPath, EMPTY_BYTE_ARRAY, zkAcls, CreateMode.PERSISTENT));
    
            // create available bookies node
            multiOps.add(Op.create(bookieRegistrationPath, EMPTY_BYTE_ARRAY, zkAcls, CreateMode.PERSISTENT));
    
            // create readonly bookies node
            multiOps.add(Op.create(
                bookieReadonlyRegistrationPath,
                EMPTY_BYTE_ARRAY,
                zkAcls,
                CreateMode.PERSISTENT));
    
            // create INSTANCEID
            String instanceId = UUID.randomUUID().toString();
            multiOps.add(Op.create(instanceIdPath, instanceId.getBytes(UTF_8),
                    zkAcls, CreateMode.PERSISTENT));
    
            // execute the multi ops
            zk.multi(multiOps);
    
            // creates the new layout and stores in zookeeper
            AbstractZkLedgerManagerFactory.newLedgerManagerFactory(conf, layoutManager);
    
            log.info("Successfully initiated cluster. ZKServers: {} ledger root path: {} instanceId: {}", zkServers,
                    ledgersRootPath, instanceId);
            return true;
        }
    
        
    }
  

Вот мой тестовый класс :

 package org.apache.bookkeeper.client;


import org.apache.bookkeeper.conf.ServerConfiguration;
import org.apache.bookkeeper.discover.RegistrationManager;
import org.apache.bookkeeper.discover.ZKRegistrationManager;
import org.apache.bookkeeper.meta.LayoutManager;
import org.apache.bookkeeper.test.BookKeeperClusterTestCase;
import org.apache.bookkeeper.test.ZooKeeperCluster;
import org.apache.bookkeeper.test.ZooKeeperUtil;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.ACL;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.mockito.InjectMocks;
import org.mockito.Mock;



import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;


@RunWith(value= Parameterized.class)
public class BookKeeperAdminInitNewClusterTest extends BookKeeperClusterTestCase {

    private boolean resu<
    private ServerConfiguration conf;
    private String confType ;

    private static final int numOfBookies = 2;
    private final int lostBookieRecoveryDelayInitValue = 1800;


    @Mock
    RegistrationManager mockedRM = mock(RegistrationManager.class) ;
  


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

                //last parameter states if the method rm.initNewCluster() called inside
                // BookKeeperAdmin.initNewCluster(conf) must be mocked or not

                //{true ,  "new" },
                //{false , "null" },
                //{false , "wrong"},
                {false , "mock"},  //caso di test introdotto per portare la branch coverage al 100%
                                    // entrando nella clausola catch del metodo initNewCluste()

        });

    }



    public BookKeeperAdminInitNewClusterTest(boolean result , String conf) throws Exception {

        super(numOfBookies, 480);
        baseConf.setLostBookieRecoveryDelay(lostBookieRecoveryDelayInitValue);
        baseConf.setOpenLedgerRereplicationGracePeriod(String.valueOf(30000));
        setAutoRecoveryEnabled(true);

        this.result = resu<
        this.confType = conf;


    }

    @Test
    public void testInitNewCluster() throws Exception {

        boolean realResult ;


        if(confType == "null"){

            this.conf = null;

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

            this.conf = new ServerConfiguration().setMetadataServiceUri("zk hierarchical://127.0.0.1/ledgers");

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

            this.conf = new ServerConfiguration(baseConf);
            String ledgersRootPath = "/testledgers";
            this.conf.setMetadataServiceUri(newMetadataServiceUri(ledgersRootPath));

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



            this.conf = new ServerConfiguration(baseConf);
            String ledgersRootPath = "/testledgers";
            this.conf.setMetadataServiceUri(newMetadataServiceUri(ledgersRootPath));
   

            when(mockedRM.initNewCluster()).thenThrow(new Exception());



        }


        try {

            realResult = BookKeeperAdmin.initNewCluster(conf);

        } catch (Exception e) {
            realResult = false ;
            e.printStackTrace();

        }
        assertEquals(result,realResult);
    }

}`
  

Проблема в том, что когда вызывается BookKeeperAdmin.initNewCluster(conf), это приводит к вызову метода реализации initNewCluster(), а не к тому, который я высмеял!
Надеюсь, вы сможете помочь, всем доброго дня!

Комментарии:

1. В вашем примере кода есть два initNewCluster() метода; другой static BookkeeperAdmin ) означает, что вы не можете издеваться над ним с помощью «традиционного» издевательства (без манипуляций с байт-кодом). Не могли бы вы уточнить, что именно вы пытаетесь протестировать?

2. Тот, кто написал этот код, сделал неудачный выбор имен, имо, поэтому я резюмирую ситуацию, чтобы немного прояснить: у меня есть 3 метода, называемых initNewCluster : первый из класса BookkeeperAdmin, и это метод, который я тестирую. Давайте назовем это methodA. Другие 2 метода initNewCluster взяты из интерфейса RegistrationManager и его реализации ZKmanager или чего-то в этом роде. Давайте назовем эти 2 метода соответственно methodB и methodBImplementation. Проблема в том, что methodaв его исполнении вызывает methodB. Я издевался над methodB, но вместо того, чтобы создавать исключение, как я ожидал, выполняется methodBImplementation.

3. Я не знаю, был ли я более ясен … дело в том, что я не пытаюсь издеваться над BookkeeperAdmin.initNewCluster(), а над методом rm.initNewCluster() (у них одинаковое имя, но это разные методы), который вызывается внутри первого, так что этот второй метод может вызвать исключение и доставить меня в блок catch первого фрагмента кода, который я опубликовал

4. Можете ли вы также добавить исходный код BookKeeperAdmin ?

5. Это длинный код, поэтому я добавляю сюда ссылку на код внутри моего публичного репозитория для этого класса: github.com/CecBazinga/bookkeeper/blob/master/bookkeeper-server /…