#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 /…