如何测试List <! - ? extends Object - >是一个UnmodifableList吗?

时间:2011-12-03 01:47:40

标签: java unit-testing collections tdd

我正在寻找一种方法来测试一些给定的List是否是不可修改的。

我有一个List<NoMatter>的对象,为了提供addNoMatter(NoMatter nm)之类的方法,而不是让API客户端只执行.getNoMatters().add(nm);我总是返回一个不可修改的版本列表,所以客户端仍然可以拥有该列表。我这样做:

public List<NoMatter> getNoMatters() {
    return Collections.unmodifiableList(this.noMatters);
}

问题在于,当我进行测试时,我无法检查此对象是否为UnmodifiableList类型。我的第一次尝试是:

@Test
public void checkIfListIsImmutable(){
    assertTrue("List is not immutable", this.myObj.getNoMatters() instanceof UnmodifiableList);
}

发生我似乎无法导入UnmodifiableList类型java.util.Collections$UnmodifiableRandomAccessList,这是我在控制台尝试System.out.println(myObj.getNoMatters().getClass().getName());时获得的内容。

那我怎么能实现呢???

PS:我知道我可以通过以下方式通过测试:

@Test(expected = UnsupportedOperationException.class)
public void checkIfListIsImmutable(){
     this.myObj.getNoMatters().add(null);
}

编辑:以上测试并没有授予我处理的列表不是不可变的,因为我需要对可能修改原始列表的每个方法进行测试,包括.remove (),. clear(),. shuffle()等!这就是为什么我认为这不是一个很好的方法。

&GT;&GT;&GT;但我仍然相信它甚至不是一个优雅的解决方案! &LT;&LT;&LT;

9 个答案:

答案 0 :(得分:15)

我认为您的解决方案不仅合理,而且优雅。您想测试您无法修改列表,并且您的测试证明它简洁。测试类的名称测试名称,而不是行为。在这种情况下,谁在乎这个名字?

同样,如果我想测试我不能将null传递给某个方法,我会在测试中传递null并期望IllegalArgumentException。

答案 1 :(得分:7)

我认为这是你最好的选择。您的替代方法(不太优雅)是检查班级的名称。

this.myObj.getNoMatters().getClass().getSimpleName().equals("UnmodifiableCollection")

问题在于包裹的UnmodifiableCollection是包私有。

我没有看到在那里期待一个例外有什么不妥,但那只是我。

答案 2 :(得分:5)

您可以使用Class#isInstance进行检查。

Collections.unmodifiableList(someList).getClass().isInstance(listToCheck);

/ E1
以下内容返回false

Collections.unmodifiableList(new ArrayList<Object>()).getClass().isInstance(new ArrayList<Object>())

以下内容返回true

Collections.unmodifiableList(new ArrayList<Object>()).getClass().isInstance(Collections.unmodifiableList(new ArrayList<Object>()))

/ E2
您的问题可能是因为Collections#unmodifiableList在某些时候会返回UnmodifiableList,其余时间会返回UnmodifiableRandomAccessList(请参阅下面的代码)。但是,由于UnmodifiableRandomAccessList扩展了UnmodifiableList,如果您获得UnmodifiableList的实例来检查,那么您将是金色的。

要获取UnmodifiableList的实例,您可以使用Collections.unmodifiableList(new LinkedList<Object>())LinkedList未实现RandomAccess,因此不会返回UnmodifiableRandomAccessList的实例。

Collections#unmodifiableList代码:

public static <T> List<T> unmodifiableList(List<? extends T> list) {
        return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));
}

UnmodifiableRandomAccessList的类标题:

static class UnmodifiableRandomAccessList<E> extends UnmodifiableList<E> implements RandomAccess

答案 3 :(得分:4)

为什么要测试您的列表是不可变的?您的“替代”解决方案是可行的方法:您应该专注于测试类的行为,而不是

我的意思是你并不关心你的方法是否返回UnmodifiableList或其他任何实例。谁在乎?你的测试当然不应该。如果您稍后更改实施以实现完全相同的行为,您的测试不应失败

那么你想测试什么?用户无法在列表中添加任何内容?然后编写一个测试(如你所建议的那样),它会在列表中添加一些内容并期望出现异常。他们无法从列表中删除任何内容?对另一个测试做同样的事情。等等。

测试像这些负面情况确实很优雅,我发现它经常被遗忘在课程的功能性覆盖范围内。适当的报道应明确说明您的课程不允许,并说明在这些情况下的预期。

答案 4 :(得分:0)

是一种黑客攻击,但请尝试:

import java.util.*;
public class Main {
    public static void main(String[] args) {
        List<Integer> list=new LinkedList<Integer>();
        list.add(1);
        List<Integer> unmodifiableList=Collections.unmodifiableList(list);
        System.out.println(unmodifiableList.getClass());
        if(unmodifiableList.getClass().getName().contains("UnmodifiableList"))
            System.out.println(true);
    }
}

答案 5 :(得分:0)

我希望像Collections.examine(T element)之类的东西可以在不实际修改集合的情况下完成工作。

除了投票最多的答案之外,请注意,Collections.EmptyList(至少)是另一个不可修改的列表。

所以,如果我真的必须创建一个实用程序作为一个脏解决方案,我将添加我的'易于识别'元素,看看它是否抛出UnsupportedOperationException,然后它意味着它是不可修改的。否则,我必须立即删除输入的内容。

答案 6 :(得分:0)

除了user949300回答之外,其中指出一个人应该测试行为,并包括Steve Zobell的评论,他建议添加一个空列表,以防列表不可修改它没有被修改,我在这里添加了一个如何检查列表的不可修改性的示例。

代码尝试添加一个空列表,如果可能则抛出IllegalArgumentException,否则为方便起见,返回原始列表,现在可以确定它是不可修改的。

/**
 * Ensures that a list is unmodifiable (i.e. that nothing can be added).
 *
 * Unmodifiable lists can e.g. be created by Collections.UnmodifiableList().
 *
 * @param list a list
 * @param <T> element type of the list
 * @return the list that is verified to be unmodifiable
 */
public static <T> List<T> verifyUnmodifiable(List<T> list) {
    try {
        list.addAll(Collections.emptyList());
    } catch (Exception e) {
        return list;
    }
    throw new IllegalArgumentException("List is modifiable.");
}

不确定是否还需要测试remove,clear,...无法添加通常是不可修改的良好迹象。

答案 7 :(得分:0)

如果API难以测试,那通常是API需要改进的气味。在这种情况下,问题是API应该返回只读列表但是(由于JDK中缺少这种类型,我猜)它返回更通用的类型。

我会考虑更改函数签名,以便返回实际上是只读列表的类型,例如guava的ImmutableList

根据定义,无法修改,因此您根本不需要测试。这也减少了班上客户的混淆。

答案 8 :(得分:0)

SetListMapJava 10 开始支持方法 copyOf(...)
它确保您获得一个不可变的集合/列表/映射。 如果参数已经是 不可变的 set/list/map,它只是返回它。 如果它是可修改/可变的,copyOf(...) 将元素/条目复制到一个不可变的 set/list/map 中并返回它。
然而,如果一个可修改的集合/列表/地图被 Collections.unmodifiableSet(...)/unmodifiableList(...)/unmodifiableMap(...),
copyOf(...) 将所有元素/条目复制到一个不可变的 set/list/map 中,以确保它不能被仍然持有可修改 set/list/map 实例的人修改。