通过比较器模拟排序列表(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));
}
当排序对象是模拟对象时,如何为排序函数(使用比较器)编写单元测试?
举个小例子:我有一个有电影的图书馆。三个 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));
}