package pl.mielecmichal.news.services.news;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import pl.mielecmichal.news.entities.news.News;
import pl.mielecmichal.news.entities.newssources.NewsSource;
import pl.mielecmichal.news.repositories.news.NewsRepository;
import static java.util.Arrays.asList;
import static org.mockito.Mockito.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class NewsServiceTest {
NewsService newsService;
NewsRepository newsRepository;
private static final String FIRST_AUTHOR = "first@mail.com";
private static final String FIRST_TITLE = "First Title";
private static final String FIRST_CONTENT = "First Content Content Content";
private static final String FIRST_URL = "http://localhost/first";
private static final String SECOND_AUTHOR = "second@mail.com";
private static final String SECOND_TITLE = "Second";
private static final String SECOND_CONTENT = "Second Content";
private static final String THIRD_AUTHOR = "third@mail.com";
private static final String THIRD_TITLE = "Third Title";
private static final String THIRD_CONTENT = "Third Content";
private final News firstNews = firstCorrectNews();
private final News secondNews = secondCorrectNews();
private final News thirdNews = thirdCorrectNews();
private final NewsSource source = correctSource();
public NewsServiceTest() throws MalformedURLException {
}
@Before
public void setUp() throws MalformedURLException {
newsRepository = mock(NewsRepository.class);
newsService = new NewsService(newsRepository);
}
@Test
public void saveNewNewses_savedNewsesGivenAgain_shouldSaveOnlyNew() {
// given
List<News> newses = new ArrayList<>();
newses.add(firstNews);
newses.add(secondNews);
when(newsRepository.countByNewsSourceAndAuthorAndTitle(source, FIRST_AUTHOR, FIRST_TITLE)).thenReturn(0L);
when(newsRepository.countByNewsSourceAndAuthorAndTitle(source, SECOND_AUTHOR, SECOND_TITLE)).thenReturn(1L);
// when
newsService.saveNewNewses(newses);
// then
verify(newsRepository, times(1)).save(asList(firstNews));
verify(newsRepository, never()).save(newses);
}
private News firstCorrectNews() {
News news = new News();
news.setAuthor(FIRST_AUTHOR);
news.setTitle(FIRST_TITLE);
news.setContent(FIRST_CONTENT);
news.setNewsSource(source);
return news;
}
private News secondCorrectNews() {
News news = new News();
news.setAuthor(SECOND_AUTHOR);
news.setTitle(SECOND_TITLE);
news.setContent(SECOND_CONTENT);
news.setNewsSource(source);
return news;
}
private News thirdCorrectNews() {
News news = new News();
news.setAuthor(THIRD_AUTHOR);
news.setTitle(THIRD_TITLE);
news.setContent(THIRD_CONTENT);
news.setNewsSource(source);
return news;
}
private NewsSource correctSource() throws MalformedURLException {
NewsSource source = new NewsSource();
source.setUrl(new URL(FIRST_URL));
source.setUpdateTime(LocalDateTime.now());
return source;
}
}
我在调试器下进行了检查,并且 countBy 方法总是返回 O,但参数在我的 SUT 中是不同且正确的。看起来莫基托没有区分方法参数。干杯!
我添加了完整的源代码以表明常量是正确的。
虽然问题主要在于字段的顺序,但您可以采取一些措施来降低再次发生此错误的可能性,并清理测试。
首先,您的三种方法 - firstCorrectNews
、secondCorrectNews
和 thirdCorrectNews
- 都以略有不同的参数执行完全相同的操作。 统一他们的目的更有意义。
private News correctNews(final String author, final String title, final String content, final NewsSource source) {
final News news = new News();
news.setAuthor(author);
news.setTitle(title);
news.setContent(content);
news.setNewsSource(source);
return news;
}
如果使用此方法引导测试对象news
,则每次都强制传入源,这样您就不会陷入依赖于测试对象整体状态的任何内容中。
当我们在这里时,也值得修改correctSource
方法,以便我们传入 URL 而不是再次假设状态。
private NewsSource correctSource(final String url) throws MalformedURLException {
final NewsSource source = new NewsSource();
source.setUrl(new URL(url));
source.setUpdateTime(LocalDateTime.now());
return source;
}
接下来,我们可以利用 Mockito 的 runner 类,这样我们就不必在 @Before
子句中更新模拟。 这使代码小得多,并使模拟类和测试类的期望值更高。
@RunWith(MockitoJUnitRunner.class)
public class NewsServiceTest {
@Mock
NewsRepository newsRepository;
@InjectMocks
NewsService newsService;
// other code to follow
}
现在,让我们把它们放在一起。 这应该与您正在测试的内容完全相同,主要区别在于:
- 代码中的重复要少得多,特别是在引导预期数据时
- 您的模拟定义明确
- 您真正关心的所有测试数据都在您的特定测试中,当您想要编写更多测试时,这会有所帮助 测试
- 数据在范围内也是隔离的,这允许您并行运行此测试
@RunWith(MockitoJUnitRunner.class)
public class NewsServiceTest {
@Mock
NewsRepository newsRepository;
@InjectMocks
NewsService newsService;
@Test
public void saveNewNewses_savedNewsesGivenAgain_shouldSaveOnlyNew() {
// given
final String FIRST_AUTHOR = "first@mail.com";
final String FIRST_TITLE = "First Title";
final String FIRST_CONTENT = "First Content Content Content";
final String URL = "http://localhost/first";
final String SECOND_AUTHOR = "second@mail.com";
final String SECOND_TITLE = "Second";
final String SECOND_CONTENT = "Second Content";
final List<News> newses = new ArrayList<>();
final NewsSource newsSource = correctSource(URL);
final News firstNews = correctNews(FIRST_AUTHOR, FIRST_TITLE, FIRST_CONTENT, newsSource);
final News secondNews = correctNews(SECOND_AUTHOR, SECOND_TITLE, SECOND_CONTENT, newsSource);
newses.add(firstNews);
newses.add(secondNews);
// when
when(newsRepository.countByNewsSourceAndAuthorAndTitle(newsSource, FIRST_AUTHOR, FIRST_TITLE)).thenReturn(0L);
when(newsRepository.countByNewsSourceAndAuthorAndTitle(newsSource, SECOND_AUTHOR, SECOND_TITLE)).thenReturn(1L);
newsService.saveNewNewses(newses);
// then
verify(newsRepository).save(asList(firstNews));
verify(newsRepository, never()).save(newses);
}
private News correctNews(final String author, final String title, final String content, final NewsSource source) {
final News news = new News();
news.setAuthor(author);
news.setTitle(title);
news.setContent(content);
news.setNewsSource(source);
return news;
}
private NewsSource correctSource(final String url) throws MalformedURLException {
NewsSource source = new NewsSource();
source.setUrl(new URL(url));
source.setUpdateTime(LocalDateTime.now());
return source;
}
}
好的,问题是初始化新闻和来源的顺序。
当我创建第一新闻,第二新闻,第三新闻时,源对象还为空。但是在我的测试中,它是完全初始化的。
private final NewsSource source = correctSource(); //should be here
private final News firstNews = firstCorrectNews();
private final News secondNews = secondCorrectNews();
private final News thirdNews = thirdCorrectNews();
private final NewsSource source = correctSource(); //not here