当排序对象是模拟对象时,如何为排序函数(使用Comparator)编写单元测试?
只是一个小例子:我有一个带电影的图书馆。三个类:Library,Movie,myCmpMovies(实现Comparator)。 我正在测试它隔离的类库。对于这个目标,我正在模拟类Movie,以测试库中的所有方法。
使用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);
....
}
我知道有关于这个主题的一些问题,但我认为这些问题不同。
提前致谢。
答案 0 :(得分:3)
对你的评论进行了一些编辑:如果你真的必须坚持这个&#34;笨拙&#34;电影课,你想去嘲笑,我会这样做:
Movie mockMovieWith(String title, Date releaseDate, ...) {
Movie mockedMovie = createMock(Movie.class);
expect(mockedMovie.getTitle()).andStubReturn(title);
expect(mockedMovie.getReleaseDate())...
replay(mockedMovie);
return mockedMovie;
}
换句话说:使用辅助方法创建一个模拟的Movie对象,该对象具有您认为应该/将要使用的&#34; real&#34;稍后将使用模拟对象的代码。
但如下所示;你真的应该添加一些&#34;反腐败层&#34;这里。含义:当&#34;外部&#34;库电影类很难使用,然后包装一些东西 - 这样你的代码就不会被那个Movie类中糟糕的设计所破坏!
...而且对于记录,这是最初的答案。
你在很多层面都犯了这个错误。首先,您在方法中使用局部变量遮蔽您的字段电影。这很少是一个好主意,因为它可能导致各种微妙的错误。
然后:要测试的第一件事不是那种方法;它是比较器本身。含义:您依赖于Collections.sort();没有必要测试那个部分。你想确保你的比较器做它应该做的事情。
从这个意义上说,你的第一个测试用例应该只定义两个Movie对象;然后调用比较器的compareTo方法并检查预期结果。
引出您问题的真实主题:不需要模拟Movie对象。这些东西代表某种数据。从这个意义上讲:你不要实例化模拟数据,只需创建具有已知内容的真实电影对象。
例如,假设电影的核心属性是其标题,您可以使用:
Movie movieA = new Movie("Title A");
Movie movieB = new Movie("Title B");
这就是你要比较的东西。或者也许进入你的电影列表(你仍然希望至少进行一次&#34;集成&#34;测试,以确保getSortedMovies ......()完成它应该做的事情。)
如果你认为你需要模拟电影对象,那么你的设计就不好了。就这么简单。如果它不是&#34;那&#34;如上所示简单来实例化一个Movie对象,而不是你应该启用它。
因为否则,您必须执行以下操作:
Movie mockedMovieA = createMock(Movie.class);
然后提供mock预期的所有属性,如:
expect(mockedMovieA.getTitle()).andReturn("Title A");
那将是非常麻烦和错误的。
所以,正如所说的那样,你应该研究核心的事情:创建一个允许你使用&#34;真实&#34;单元测试中的电影对象。如果你的Movie类包含很多东西,那么这是不可能的......那是一个明确的指示,那就是你的Movie类太大了,做了不属于它的东西。
解决此类问题的第一种方式&#34;太大&#34;问题:你可以分开一部电影&#34;包含当前Movie类的核心方法的接口,例如getTitle(),getReleaseDate()等等。然后所有您的代码只适用于该界面...这很容易被嘲笑;或由更多&#34;愚蠢的&#34;仅测试课程。
答案 1 :(得分:0)
当排序对象是模拟对象时,如何为排序函数(使用Comparator)编写单元测试?
使用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));
}