编程填空:学生信息处理程序
这是Project Student的一部分。 其他职位包括带有Jersey的Webservice Client,带有Jersey的 Webservice Server和带有Spring Data的Persistence 。
RESTful Webapp洋葱的第三层是业务层。 这就是应用程序的精髓所在–编写良好的持久性和Web服务层受到约束,但是任何事情都在业务层中进行。
此时我们仅实现CRUD方法,因此这些方法非常简单。
设计决策
Spring Data –我们将使用Spring Data作为持久层,因此我们需要牢记指定持久层接口。 正如我们将在下一个博客中看到的那样,这大大简化了我们的生活。
局限性
DataAccessException –不会尝试根据我们收到的DataAccessException的类型抛出不同的异常。 稍后这将很重要-我们应该以不同于丢失数据库连接的方式对待约束违反。
服务介面
首先提醒我们在上一篇文章中确定的服务接口。
public interface CourseService {
List<Course> findAllCourses();
Course findCourseById(Integer id);
Course findCourseByUuid(String uuid);
Course createCourse(String name);
Course updateCourse(Course course, String name);
void deleteCourse(String uuid);
}
服务实施
由于服务是基本的CRUD操作,因此该服务的实施速度很快。 我们唯一关心的是数据库是否存在问题(Spring中的DataAccessException)或找不到期望值。 我们需要在前者的API中添加新的例外,而后者的API中已经有例外。
public class CourseServiceImpl implements CourseService {
private static final Logger log = LoggerFactory
.getLogger(CourseServiceImpl.class);
@Resource
private CourseRepository courseRepository;
/**
* Default constructor
*/
public CourseServiceImpl() {
}
/**
* Constructor used in unit tests
*/
CourseServiceImpl(CourseRepository courseRepository) {
this.courseRepository = courseRepository;
}
/**
* @see com.invariantproperties.sandbox.student.business.CourseService#
* findAllCourses()
*/
@Transactional(readOnly = true)
@Override
public List<Course> findAllCourses() {
List<Course> courses = null;
try {
courses = courseRepository.findAll();
} catch (DataAccessException e) {
if (!(e instanceof UnitTestException)) {
log.info("error loading list of courses: " + e.getMessage(), e);
}
throw new PersistenceException("unable to get list of courses.", e);
}
return courses;
}
/**
* @see com.invariantproperties.sandbox.student.business.CourseService#
* findCourseById(java.lang.Integer)
*/
@Transactional(readOnly = true)
@Override
public Course findCourseById(Integer id) {
Course course = null;
try {
course = courseRepository.findOne(id);
} catch (DataAccessException e) {
if (!(e instanceof UnitTestException)) {
log.info("internal error retrieving course: " + id, e);
}
throw new PersistenceException("unable to find course by id", e, id);
}
if (course == null) {
throw new ObjectNotFoundException(id);
}
return course;
}
/**
* @see com.invariantproperties.sandbox.student.business.CourseService#
* findCourseByUuid(java.lang.String)
*/
@Transactional(readOnly = true)
@Override
public Course findCourseByUuid(String uuid) {
Course course = null;
try {
course = courseRepository.findCourseByUuid(uuid);
} catch (DataAccessException e) {
if (!(e instanceof UnitTestException)) {
log.info("internal error retrieving course: " + uuid, e);
}
throw new PersistenceException("unable to find course by uuid", e,
uuid);
}
if (course == null) {
throw new ObjectNotFoundException(uuid);
}
return course;
}
/**
* @see com.invariantproperties.sandbox.student.business.CourseService#
* createCourse(java.lang.String)
*/
@Transactional
@Override
public Course createCourse(String name) {
final Course course = new Course();
course.setName(name);
Course actual = null;
try {
actual = courseRepository.saveAndFlush(course);
} catch (DataAccessException e) {
if (!(e instanceof UnitTestException)) {
log.info("internal error retrieving course: " + name, e);
}
throw new PersistenceException("unable to create course", e);
}
return actual;
}
/**
* @see com.invariantproperties.sandbox.course.persistence.CourseService#
* updateCourse(com.invariantproperties.sandbox.course.domain.Course,
* java.lang.String)
*/
@Transactional
@Override
public Course updateCourse(Course course, String name) {
Course updated = null;
try {
final Course actual = courseRepository.findCourseByUuid(course
.getUuid());
if (actual == null) {
log.debug("did not find course: " + course.getUuid());
throw new ObjectNotFoundException(course.getUuid());
}
actual.setName(name);
updated = courseRepository.saveAndFlush(actual);
course.setName(name);
} catch (DataAccessException e) {
if (!(e instanceof UnitTestException)) {
log.info("internal error deleting course: " + course.getUuid(),
e);
}
throw new PersistenceException("unable to delete course", e,
course.getUuid());
}
return updated;
}
/**
* @see com.invariantproperties.sandbox.student.business.CourseService#
* deleteCourse(java.lang.String)
*/
@Transactional
@Override
public void deleteCourse(String uuid) {
Course course = null;
try {
course = courseRepository.findCourseByUuid(uuid);
if (course == null) {
log.debug("did not find course: " + uuid);
throw new ObjectNotFoundException(uuid);
}
courseRepository.delete(course);
} catch (DataAccessException e) {
if (!(e instanceof UnitTestException)) {
log.info("internal error deleting course: " + uuid, e);
}
throw new PersistenceException("unable to delete course", e, uuid);
}
}
}
该实现告诉我们持久层所需的接口。
持久层接口
我们将在持久层中使用Spring Data,并且DAO接口与Spring Data存储库相同。 我们只需要一种非标准方法。
public class CourseServiceImplTest {
@Test
public void testFindAllCourses() {
final List<Course> expected = Collections.emptyList();
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findAll()).thenReturn(expected);
final CourseService service = new CourseServiceImpl(repository);
final List<Course> actual = service.findAllCourses();
assertEquals(expected, actual);
}
@Test(expected = PersistenceException.class)
public void testFindAllCoursesError() {
final List<Course> expected = Collections.emptyList();
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findAll()).thenThrow(new UnitTestException());
final CourseService service = new CourseServiceImpl(repository);
final List<Course> actual = service.findAllCourses();
assertEquals(expected, actual);
}
@Test
public void testFindCourseById() {
final Course expected = new Course();
expected.setId(1);
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findOne(any(Integer.class))).thenReturn(expected);
final CourseService service = new CourseServiceImpl(repository);
final Course actual = service.findCourseById(expected.getId());
assertEquals(expected, actual);
}
@Test(expected = ObjectNotFoundException.class)
public void testFindCourseByIdMissing() {
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findOne(any(Integer.class))).thenReturn(null);
final CourseService service = new CourseServiceImpl(repository);
service.findCourseById(1);
}
@Test(expected = PersistenceException.class)
public void testFindCourseByIdError() {
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findOne(any(Integer.class))).thenThrow(
new UnitTestException());
final CourseService service = new CourseServiceImpl(repository);
service.findCourseById(1);
}
@Test
public void testFindCourseByUuid() {
final Course expected = new Course();
expected.setUuid("[uuid]");
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findCourseByUuid(any(String.class))).thenReturn(
expected);
final CourseService service = new CourseServiceImpl(repository);
final Course actual = service.findCourseByUuid(expected.getUuid());
assertEquals(expected, actual);
}
@Test(expected = ObjectNotFoundException.class)
public void testFindCourseByUuidMissing() {
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findCourseByUuid(any(String.class))).thenReturn(null);
final CourseService service = new CourseServiceImpl(repository);
service.findCourseByUuid("[uuid]");
}
@Test(expected = PersistenceException.class)
public void testFindCourseByUuidError() {
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findCourseByUuid(any(String.class))).thenThrow(
new UnitTestException());
final CourseService service = new CourseServiceImpl(repository);
service.findCourseByUuid("[uuid]");
}
@Test
public void testCreateCourse() {
final Course expected = new Course();
expected.setName("name");
expected.setUuid("[uuid]");
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.saveAndFlush(any(Course.class))).thenReturn(expected);
final CourseService service = new CourseServiceImpl(repository);
final Course actual = service.createCourse(expected.getName());
assertEquals(expected, actual);
}
@Test(expected = PersistenceException.class)
public void testCreateCourseError() {
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.saveAndFlush(any(Course.class))).thenThrow(
new UnitTestException());
final CourseService service = new CourseServiceImpl(repository);
service.createCourse("name");
}
@Test
public void testUpdateCourse() {
final Course expected = new Course();
expected.setName("Alice");
expected.setUuid("[uuid]");
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findCourseByUuid(any(String.class))).thenReturn(
expected);
when(repository.saveAndFlush(any(Course.class))).thenReturn(expected);
final CourseService service = new CourseServiceImpl(repository);
final Course actual = service.updateCourse(expected, "Bob");
assertEquals("Bob", actual.getName());
}
@Test(expected = ObjectNotFoundException.class)
public void testUpdateCourseMissing() {
final Course expected = new Course();
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findCourseByUuid(any(String.class))).thenReturn(null);
final CourseService service = new CourseServiceImpl(repository);
service.updateCourse(expected, "Bob");
}
@Test(expected = PersistenceException.class)
public void testUpdateCourseError() {
final Course expected = new Course();
expected.setUuid("[uuid]");
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findCourseByUuid(any(String.class))).thenReturn(
expected);
doThrow(new UnitTestException()).when(repository).saveAndFlush(
any(Course.class));
final CourseService service = new CourseServiceImpl(repository);
service.updateCourse(expected, "Bob");
}
@Test
public void testDeleteCourse() {
final Course expected = new Course();
expected.setUuid("[uuid]");
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findCourseByUuid(any(String.class))).thenReturn(
expected);
doNothing().when(repository).delete(any(Course.class));
final CourseService service = new CourseServiceImpl(repository);
service.deleteCourse(expected.getUuid());
}
@Test(expected = ObjectNotFoundException.class)
public void testDeleteCourseMissing() {
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findCourseByUuid(any(String.class))).thenReturn(null);
final CourseService service = new CourseServiceImpl(repository);
service.deleteCourse("[uuid]");
}
@Test(expected = PersistenceException.class)
public void testDeleteCourseError() {
final Course expected = new Course();
expected.setUuid("[uuid]");
final CourseRepository repository = Mockito
.mock(CourseRepository.class);
when(repository.findCourseByUuid(any(String.class))).thenReturn(
expected);
doThrow(new UnitTestException()).when(repository).delete(
any(Course.class));
final CourseService service = new CourseServiceImpl(repository);
service.deleteCourse(expected.getUuid());
}
}
整合测试
集成测试已推迟到实现持久层。
源代码
翻译自: https://www.javacodegeeks.com/2014/01/project-student-business-layer.html
编程填空:学生信息处理程序