我的任务是为大量度量数据结构(即quadtree和k-d tree变体)创建实现。我已经有大约四个这样的实现了,但我目前正在测试的方式不是,因为我没有更好的词,所以很好。
我需要一种干净的方法来测试从这些树/特里结构中插入和删除数据的方式,我可以测试节点的内部结构(检查父节点,子节点,排序等)。这些实现遵循单独的正确性证明和运行时分析,因此我需要确保不仅正确插入节点(意味着,稍后可从树中检索),而且还要确保树中的非常“正确”位置。
然而,“单元测试”似乎是错误的方法,因为它的目的是,如果我没有弄错的话,是测试结构或系统的外部API。我看过很多与单元测试相关的问题,询问“如何在单元测试中访问私有字段”或“我如何测试非公开方法的返回值”,答案通常是“不要” t“ - 我同意这个答案。所以我不会让任何愿意帮助的人只是模糊的ramblings,我的树实现的界面如下(基于java集合的Map界面):
public interface SpatialMap<K, V> extends Iterable<SpatialMap.Entry<K, V>>
{
// Query Operations
/**
* Returns the number of key-value mappings in this map. If the map contains more than
* <tt>Integer.MAX_VALUE</tt> elements, returns <tt>Integer.MAX_VALUE</tt>.
*
* @return The number of key-value mappings in this map.
*/
int size();
/**
* Returns <tt>true</tt> if this map contains no key-value mappings.
*
* @return <tt>true</tt> if this map contains no key-value mappings.
*/
boolean isEmpty();
/**
* Returns <tt>true</tt> if this map contains a mapping for the specified key.
*
* @param key
* The key whose presence in this map is to be tested.
* @return <tt>true</tt> if this map contains a mapping for the specified key.
*/
boolean containsKey(K key);
/**
* Returns the value to which the specified key is mapped, or {@code null} if this map contains
* no mapping for the key.
*
* <p>A return value of {@code null} does not <i>necessarily</i> indicate that the map contains
* no mapping for the key; it's also possible that the map explicitly maps the key to
* {@code null}. The {@link #containsKey containsKey} operation may be used to distinguish these
* two cases.
*
* @see #put(Comparable, Comparable, Object)
*
* @param key
* The key whose associated value is to be returned.
* @return The value to which the specified key is mapped, or {@code null} if this map contains
* no mapping for the key.
*/
V get(K key);
// Modification Operations
/**
* Associates the specified value with the specified key in this map. If the map previously
* contained a mapping for the key, the old value is replaced.
*
* @param key
* The key with which the specified value is to be associated.
* @param data
* The value to be associated with the specified key.
* @return The previous value associated with the key, or <tt>null</tt> if there was no mapping
* for the key. (A <tt>null</tt> return can also indicate that the map previously
* associated <tt>null</tt> with <tt>key</tt>.)
*/
V put(K key, V data);
/**
* Removes the mapping for the specified key from this map if present.
*
* @param key
* The key whose mapping is to be removed from the map.
* @return The previous value associated with the key, or <tt>null</tt> if there was no mapping
* for the key. (A <tt>null</tt> return can also indicate that the map previously
* associated <tt>null</tt> with <tt>key</tt>.)
*/
V remove(K key);
// Bulk Operations
/**
* Removes all of the mappings from this map. The map will be empty after this call returns.
*/
void clear();
}
这使得很难仅使用 公共方法进行测试,因为我需要公共接口无法获得的某些数据(子/父指针)。此外,在trie结构中(PR四叉树,PRKDTree,MX变体等)具有与数据分离的节点,因此创建返回“节点”的公共方法也将被抽象得太远而无法获得正确的数据。 / p>
我正在寻找什么类型的测试方法(或者我可以使用JUnit的技术,而不是感觉我正在破坏美丽的认知界限)?
答案 0 :(得分:5)
有些情况下,有时您确实需要测试结构的内部状态。在这种情况下,我将使用反射访问内部变量。有一些JUnit插件(PrivateAccessor http://junit-addons.sourceforge.net/junitx/util/PrivateAccessor.html)使这更容易。
权衡是你的测试会更脆弱,因为如果内部状态发生变化,那么你的测试可能会中断。但是如果你想要确信你的内部状态是正确的,有时你需要这样做。
答案 1 :(得分:1)
我在这种情况下使用的一种方法是使这些内部字段受到保护,并创建一个用于测试的子类。通过该子类,您可以暴露白盒测试所需的任何状态。
答案 2 :(得分:0)
如果您将接口及其实现单独放在专用包中,并使该实现的内部状态方法受包保护,那么您的测试可以访问它们,并可能进行测试他们,而你的系统的其余部分不能。
单位测试“纯粹主义者”并不好,但这通常是我的方式,当我不想将我的班级的胆量暴露给系统的其他部分但我仍然想要做出断言它的内在行为。