gpt4 book ai didi

java - JMockit - 期望返回旧值而不是与私有(private)字段关联的值

转载 作者:行者123 更新时间:2023-11-30 10:56:01 24 4
gpt4 key购买 nike

当记录返回字段值的期望时,我希望返回值是调用实际方法时字段的值(引用的值),而不是期望时字段的值被记录下来。

这是被测类(实际上是其中的 2 个):

public class ListObservingCache<T> extends ObservingCache {
public ListObservingCache(Supplier<List<T>> syncFunc, int intervalMillis) {
super(syncFunc, intervalMillis);
}

@SuppressWarnings("unchecked")
@Override
public List<T> getItems() {
return items != null ? Collections.unmodifiableList((List<T>) items) : null;
}
}



public abstract class ObservingCache {
private static final int DEFAULT_CACHE_REFRESH_INTERVAL = 10 * 60 * 1000; // 10 minutes
private static int DEFAULT_CACHE_INITIAL_DELAY = 10 * 60 * 1000; // 10 minutes
private static final int DEFAULT_THREAD_POOL_SIZE = 5;

private static ScheduledExecutorService executor;

protected Object items;

protected ObservingCache(Supplier<? extends Object> syncFunc) {
this(syncFunc, DEFAULT_CACHE_REFRESH_INTERVAL);
}

protected ObservingCache(Supplier<? extends Object> syncFunc, int intervalMillis) {
if (executor == null || executor.isShutdown()) {
executor = Executors.newScheduledThreadPool(DEFAULT_THREAD_POOL_SIZE);
}
Runnable task = () -> {
Object result = syncFunc.get();
if (result != null) {
items = result;
}
};
task.run(); // First run is blocking (saves a lot of trouble later).
executor.scheduleAtFixedRate(task, DEFAULT_CACHE_INITIAL_DELAY, intervalMillis, TimeUnit.MILLISECONDS);
}

public abstract Object getItems();

}

这是我的测试类:

public class ListObservingCacheTest {
List<Integer> provList; // <-- The field I wish to use instead of the "willReturnList()" method

@Mocked
DummyTask mockTask;

@BeforeClass
public static void setupClass() {
ObservingCache.DEFAULT_CACHE_INITIAL_DELAY = 100;
}

@AfterClass
public static void tearDownClass() {
ExecutorService toShutDown = (ExecutorService) getField(ObservingCache.class, "executor");
toShutDown.shutdown();
}

@Before
public void setUp() {
mockTask = new DummyTask(); // Empty list
}

@Test
public void testBasic() throws Exception {
willReturnList(Arrays.asList(1, 2));
ListObservingCache<Integer> obsCache = new ListObservingCache<Integer>(() -> mockTask.acquireList(), 300);
assertEquals(Arrays.asList(1, 2), obsCache.getItems());
willReturnList(Arrays.asList(3, 4, 5));
assertEquals(Arrays.asList(1, 2), obsCache.getItems()); // ObservingCache should still returns the former list because its interval hasn't passed yet
Thread.sleep(300);
assertEquals(Arrays.asList(3, 4, 5), obsCache.getItems()); // ObservingCache should now return the "new" list, as its interval has passed and the task has been executed again
}

/**
* Instructs the mock task to return the specified list when its
* acquireList() method is called
*/
private void willReturnList(List<Integer> list) {
new Expectations() {{ mockTask.acquireList(); result = list; }};
}

/**
* Simulates an ObservingCache "real-life" task. Should never really be
* called (because it's mocked).
*/
class DummyTask {
private List<Integer> list;

public List<Integer> acquireList() {
return list;
}
}

这个测试通过了,但我想要一种更优雅的方式来设置对 acquireList() 返回值的期望值。方法,因为一旦我在同一个类中有多个这样的方法,这种“willReturn”方法将成为维护的噩梦。

我正在寻找类似于 mockito-syntax 命令的东西:

when(mockTask.acquireList()).thenReturn(provList);

这应该始终返回 provList 字段的当前值(与记录期望值时的值相反)。

编辑:阅读文档后,我想出了一个解决方案,使用委托(delegate):

new Expectations() {{
mockTask.acquireList();
result = new Delegate<List<Integer>>() {
List<Integer> delegate() {
return provList; // The private field
}
};
}};

这种方法有两个问题:
1. 不够优雅
2. List<Integer> delegate()方法导致编译时警告:

The method delegate() from the type new Delegate>(){} is never used locally

因此,还在寻找另一种解决方案

最佳答案

OP 试图“解决”的问题是:如何在单个测试方法中简化多个测试的编写,当被测代码(这里是 obsCache.getItems() 方法)和要执行的验证相同,但输入值不同。

所以,这实际上是一个关于如何正确编写测试的问题。 "Arrange-Act-Assert" (AAA) 描述了编写良好的测试的基本形式图案:

@Test
public void exampleOfAAATest() {
// Arrange: set local variables/fields with input values,
// create objects and/or mocks, record expectations.

// Act: call the code to be tested; normally, this is *one* method
// call only.

// Assert: perform a number of assertions on the output, and/or
// verify expectations on mocks.
}

@Test
public void exampleOfWhatisNotAnAAATest() {
// First "test":
// Arrange 1
// Act
// Assert 1

// Second "test":
// Arrange 2 (with different inputs)
// Act again
// Assert 2

// ...
}

显然,像上面第二个这样的测试被认为是不好的做法,不应该被鼓励。

编辑:为真正的 CUT 添加了完整的测试类(如下)。

public final class ListObservingCacheTest
{
@Mocked DummyTask mockTask;
final int refreshIntervalMillis = 30;
final List<Integer> initialItems = asList(1, 2);
final List<Integer> newItemsAfterRefreshInterval = asList(3, 4, 5);

@Before
public void arrangeTaskOutputForMultipleCalls() {
new Expectations() {{
mockTask.acquireList();
result = initialItems;
result = newItemsAfterRefreshInterval;
}};

// A trick to avoid a long initial delay before the scheduled task is first
// executed (a better solution might be to change the SUT to read the
// initial delay from a system property).
new MockUp<ScheduledThreadPoolExecutor>() {
@Mock
ScheduledFuture<?> scheduleAtFixedRate(
Invocation inv,
Runnable command, long initialDelay, long period, TimeUnit unit
) {
return inv.proceed(command, 0, period, unit);
}
};
}

@After
public void shutdownTheExecutorService() {
ScheduledExecutorService executorService =
Deencapsulation.getField(ObservingCache.class, ScheduledExecutorService.class);
executorService.shutdown();
}

@Test
public void getTheInitialItemsImmediatellyAfterCreatingTheCache() throws Exception {
// Arrange: empty, as there is nothing left to do beyond what the setup method
// already does.

// Act:
ListObservingCache<Integer> obsCache =
new ListObservingCache<>(() -> mockTask.acquireList(), refreshIntervalMillis);
List<Integer> items = obsCache.getItems();

// Assert:
assertEquals(initialItems, items);
}

@Test
public void getTheSameItemsMultipleTimesBeforeTheCacheRefreshIntervalExpires() throws Exception {
// Act:
ListObservingCache<Integer> obsCache =
new ListObservingCache<>(() -> mockTask.acquireList(), refreshIntervalMillis);
List<Integer> items1 = obsCache.getItems();
List<Integer> items2 = obsCache.getItems();
List<Integer> itemsIfTaskGotToBeCalledAgain = mockTask.acquireList();
List<Integer> items3 = obsCache.getItems();

// Assert:
assertEquals(initialItems, items1);
assertEquals(initialItems, items2);
assertEquals(initialItems, items3);
assertEquals(newItemsAfterRefreshInterval, itemsIfTaskGotToBeCalledAgain);
}

@Test
public void getNewItemsAfterTheCacheRefreshIntervalExpires() throws Exception {
// Act:
ListObservingCache<Integer> obsCache =
new ListObservingCache<>(() -> mockTask.acquireList(), refreshIntervalMillis);
List<Integer> items1 = obsCache.getItems();
Thread.sleep(refreshIntervalMillis);
List<Integer> items2 = obsCache.getItems();

// Assert:
assertEquals(initialItems, items1);
assertEquals(newItemsAfterRefreshInterval, items2);
}
}

关于java - JMockit - 期望返回旧值而不是与私有(private)字段关联的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33149078/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com