通过比较器模拟排序列表(EasyMock)

Mocking a sorted list by a Comparator (EasyMock)

当排序对象是模拟对象时,如何为排序函数(使用比较器)编写单元测试?

举个小例子:我有一个有电影的图书馆。三个 classes:Library、Movie、myCmpMovies(实现比较器)。 我正在测试 class 库,将其隔离。对于这个目标,我正在模拟 class 电影,以测试库中的所有方法。

使用 EasyMock。

我正在以这种方式对电影列表进行排序:

class Library{
    ArrayList<Movie> movies;
    public List<Movie> getSortedMoviesByDatesAndNames() {
        List<Movie> movies = this.getMovies();
        Comparator myCmpMovies = new myCmpMovies();
        Collections.sort(movies, myCmpMovies);
        return movies;
    }
}

现在,我正在用单元测试来测试这个方法。模拟对象是电影。我只是在尝试类似的东西:

    @Test
    public List<Movie> TestGetSortedMoviesByDatesAndNames() {
        movie1= createMock(Movie.class);
        movie2= createMock(Movie.class);
        movie3= createMock(Movie.class);

        ....
    }

我知道有几个关于这个话题的问题,但我认为这些不是同一个问题。

提前致谢。

对你的评论进行一些编辑:如果你真的必须坚持看那个 "clumsy" 电影 class,并且你想去嘲笑,我会这样做:

Movie mockMovieWith(String title, Date releaseDate, ...) {
  Movie mockedMovie = createMock(Movie.class);
  expect(mockedMovie.getTitle()).andStubReturn(title);
  expect(mockedMovie.getReleaseDate())...
  replay(mockedMovie);
  return mockedMovie;
}

换句话说:使用辅助方法创建一个模拟电影 object,它具有您认为 should/would 稍后将被您的 "real" 代码使用的那些属性使用模拟的 objects.

但是如下所写;你真的应该在这里添加一些 "anti corruption layer" 。意思是:当 "external" 库 Movie class 太难用时,请绕过一些东西 - 这样您的代码就不会被 Movie class![= 中的糟糕设计破坏14=]

...作为记录,这里是初始答案。

你在很多层面上都犯了这个错误。首先,您正在 shadowing 您的字段 movies 在您的方法中使用局部变量。这很少是个好主意,因为它会导致各种细微的错误。

那么:首先要测试的不是那个方法;它是 比较器 本身。意思是:你依赖于 Collections.sort();无需测试 that 部分。你想确保你的比较器做它应该做的。

从这个意义上讲,您的第一个测试用例应该简单地定义两个 Movie object;然后调用比较器的 compareTo 方法并检查预期结果。

引出您问题的真正主题:您应该不需要 模拟电影object。这些东西代表某种数据。从这个意义上说:您不需要实例化模拟数据,您只需创建具有已知内容的真实电影 object。

例如,假设电影的核心 属性 是它的标题,您可以这样:

Movie movieA = new Movie("Title A");
Movie movieB = new Movie("Title B");

这就是您要输入比较器的内容。或者可能进入你的电影列表(你仍然想至少做一个 "integration" 测试来确保 getSortedMovies...() 做了它应该做的事情)。

如果您认为需要 mock 电影 objects,那么您的设计很糟糕。就这么简单。如果像上面显示的那样 "that" 不容易实例化电影 object,那么您应该启用它。

否则,您将不得不执行以下操作:

Movie mockedMovieA = createMock(Movie.class);

然后提供 mock 期望的所有属性,例如:

expect(mockedMovieA.getTitle()).andReturn("Title A");

那会非常麻烦而且容易出错。

因此,如前所述,您应该研究 核心 事情:创建一个允许您使用 "real" 电影 object 的设计你的单元测试。如果你的 Movie class 包含太多东西,这是不可能的......那是一个明确的迹象,表明你的 Movie class 太大了,并且正在做不属于它的事情。

解决此类"too big"问题的第一种方法:您可以分离一个"Movie"接口,其中包含您当前电影class的核心方法,例如getTitle()、getReleaseDate (), 任何。然后 all 你的代码只适用于那个接口......这很容易被嘲笑;或由 more "stupid" test-only class 实现。

How to write unit tests for a sorting function (that use Comparator), when the sorted object is a mock object?

使用 UnitTest,您可以测试 可观察到的行为

这意味着您要检查返回集合中的对象是否具有正确的顺序,而不管该方法是如何实现的。

    @Test
    public void TestGetSortedMoviesByDates() {
        movie1= createMock(Movie.class);
        movie2= createMock(Movie.class);
        movie3= createMock(Movie.class);

        expect(movie1.getName()).andReturn("any name");
        expect(movie2.getName()).andReturn("any name");
        expect(movie3.getName()).andReturn("any name");

        Date date = new Date(); // curent Date
        expect(movie2.getDate()).andReturn(date.clone())
        date.setYear(date.getYear()+1);
        expect(movie3.getDate()).andReturn(date.clone())
        date.setYear(date.getYear()+1);
        expect(movie1.getDate()).andReturn(date.clone())

        List<Movie> movies = Arrays.asList(movie1,movie2,movie3);
        Library library = new Library();
        // put the list into the library
        List<Movie> sortedMovies = library.getSortedMoviesByDatesAndNames();

        assertArrayEquals(sortedMovies, Arrays.asList(movie2,movie3,movie1));
    }

    @Test
    public void TestGetSortedMoviesByNamesForEqualDates() {
        movie1= createMock(Movie.class);
        movie2= createMock(Movie.class);
        movie3= createMock(Movie.class);

        expect(movie1.getName()).andReturn("ZZ Last after sort");
        expect(movie2.getName()).andReturn("AA first after sort");
        expect(movie3.getName()).andReturn("MM middle after sort");

        Date date = new Date(); // curent Date
        expect(movie2.getDate()).andReturn(date)
        expect(movie3.getDate()).andReturn(date)
        expect(movie1.getDate()).andReturn(date)

        List<Movie> movies = Arrays.asList(movie1,movie2,movie3);
        Library library = new Library();
        // put the list into the library
        List<Movie> sortedMovies = library.getSortedMoviesByDatesAndNames();

        assertArrayEquals(sortedMovies, Arrays.asList(movie2,movie3,movie1));
   }