2015-09-06 34 views
0

我有一個使用JPA與流行的Sql數據庫交互的Spring MVC應用程序(Spring Boot v。1.2.5)。
因此,我有幾個實體映射數據庫中的所有表。顯然,這些類只有getter/setter和實體之間關係的註釋。JPA實體必須經過單元測試並且如何執行?

例如爲:

@Entity 
@Table 
public class Article { 

    @Id 
    @GeneratedValue(strategy = GenerationType.IDENTITY) 
    @JsonView(View.Private.class) 
    private Long id; 

    @Column(nullable = false) 
    @JsonView(View.Public.class) 
    private String name; 

    @ManyToOne(optional = false, fetch = FetchType.LAZY) 
    @JoinColumn(name = "categoryId", nullable = false, updatable = false) 
    @JsonIgnore 
    private Category category; 

    //Constructors Getters and Setters 
    ... 
} 

我的問題是:我應該單元測試這些類?我應該測試什麼?如何

+0

我建議使用Groovy的兩種或龍目島爲您生成所有這些方法。 – chrylis

+0

我已經使用Lombok –

+1

然後不,相信註釋庫(JPA和Bean驗證)完成他們的工作。 – chrylis

回答

1

我建議你測試你寫(或者你選擇寫的)一切......所以在這種情況下,我看到以下內容:

@GeneratedValue(strategy = GenerationType.IDENTITY) 
@Column(nullable = false) 
@ManyToOne(optional = false, fetch = FetchType.LAZY) 
@JoinColumn(name = "categoryId", nullable = false, updatable = false) 

您定義一個與此註釋一些行爲(我只挑那些來自JPA的,同樣的事情應該用JSONView註解完成),如果一切工作正常(像你定義的那樣),你想通過單元測試來跟蹤。

@Test(expect = SQLException.class) 
public void should_not_allow_null_name() { 
    /* Given */ Article article = new Article(null, new Category()); 
    /* When */ articleRepository.save(article); 
} 

有了這樣的(簡單)的測試,你可以,如果行爲尊重你已經實現了何種後續。如果某人(也許是你自己)刪除了這個註釋,你將會有一個提醒。

但是不要測試默認的行爲(例如,列名,您選擇讓JPA(和ORM)爲您選擇...所以不要測試框架,這是限制。

關於如何測試它,我喜歡使用(自Spring Boot以來)名爲DBSetup的項目,它允許我在我的測試中將我的數據集硬編碼而不是冗長的XML,這是一個非常有趣的項目,來自ninja- squade

測試的一個例子:

數據庫測試配置:

@Configuration 
@EnableJpaRepositories(basePackages = "lan.dk.podcastserver.repository") 
@EntityScan(basePackages = "lan.dk.podcastserver.entity") 
public class DatabaseConfiguraitonTest { 

    @Bean 
    public DataSource dataSource() { 
     return new EmbeddedDatabaseBuilder() 
       .setType(EmbeddedDatabaseType.H2) 
       .build(); 
    } 

    @Bean 
    @Autowired 
    public FullTextEntityManager fullTextEntityManager(EntityManager entityManager) { 
     return getFullTextEntityManager(entityManager); 
    } 

    public static final DateTimeFormatter formatter = new DateTimeFormatterBuilder().append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral(" ").append(DateTimeFormatter.ISO_LOCAL_TIME).toFormatter(); 
    public static final Operation DELETE_ALL_PODCASTS = deleteAllFrom("PODCAST"); 
    public static final Operation DELETE_ALL_ITEMS = deleteAllFrom("ITEM"); 
    public static final Operation DELETE_ALL_TAGS = sequenceOf(deleteAllFrom("PODCAST_TAG"), deleteAllFrom("TAG")); 
    public static final Operation DELETE_ALL = sequenceOf(DELETE_ALL_ITEMS, DELETE_ALL_TAGS, DELETE_ALL_PODCASTS, DELETE_ALL_TAGS); 
} 

==>https://gist.github.com/davinkevin/bb4f62aaec031b68b8f3

和測試:

@Transactional 
@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(classes = {DatabaseConfiguraitonTest.class, HibernateJpaAutoConfiguration.class}) 
public class ItemRepositoryTest { 

    @Autowired DataSource dataSource; 
    @Autowired ItemRepository itemRepository; 

    private final static DbSetupTracker dbSetupTracker = new DbSetupTracker(); 
    public static final Operation INSERT_REFERENCE_DATA = sequenceOf(
      insertInto("PODCAST") 
        .columns("ID", "TITLE", "URL", "TYPE", "HAS_TO_BE_DELETED") 
        .values(1, "AppLoad", null, "RSS", false) 
        .values(2, "Geek Inc HD", "http://fake.url.com/rss", "YOUTUBE", true) 
        .build(), 
      insertInto("ITEM") 
        .columns("ID", "TITLE", "URL", "PODCAST_ID", "STATUS", "PUBDATE", "DOWNLOADDDATE") 
        .values(1L, "Appload 1", "http://fakeurl.com/appload.1.mp3", 1, Status.FINISH, now().minusDays(15).format(formatter), now().minusDays(15).format(formatter)) 
        .values(2L, "Appload 2", "http://fakeurl.com/appload.2.mp3", 1, null, now().minusDays(30).format(formatter), null) 
        .values(3L, "Appload 3", "http://fakeurl.com/appload.3.mp3", 1, Status.NOT_DOWNLOADED, now().format(formatter), null) 
        .values(4L, "Geek INC 123", "http://fakeurl.com/geekinc.123.mp3", 2, Status.DELETED, now().minusYears(1).format(formatter), now().format(formatter)) 
        .values(5L, "Geek INC 124", "http://fakeurl.com/geekinc.124.mp3", 2, Status.FINISH, now().minusDays(15).format(formatter), now().minusDays(15).format(formatter)) 
        .build(), 
      insertInto("TAG") 
        .columns("ID", "NAME") 
        .values(1L, "French Spin") 
        .values(2L, "Studio Knowhere") 
        .build(), 
      insertInto("PODCAST_TAG") 
        .columns("PODCAST_ID", "TAG_ID") 
        .values(1, 1) 
        .values(2, 2) 
        .build() 
    ); 

    @Before 
    public void prepare() throws Exception { 
     Operation operation = sequenceOf(DELETE_ALL, INSERT_REFERENCE_DATA); 
     DbSetup dbSetup = new DbSetup(new DataSourceDestination(dataSource), operation); 

     dbSetupTracker.launchIfNecessary(dbSetup); 
    } 

    @Test 
    public void should_find_by_podcast_and_page() { 
     /* Given */ 
     dbSetupTracker.skipNextLaunch(); 
     Integer podcastId = 1; 
     PageRequest pageRequest = new PageRequest(1, 1, Sort.Direction.ASC, "id"); 

     /* When */ 
     Page<Item> itemByPodcast = itemRepository.findByPodcast(podcastId, pageRequest); 

     /* Then */ 
     PageAssert 
       .assertThat(itemByPodcast) 
       .hasSize(1) 
       .hasTotalElements(3) 
       .hasTotalPages(3) 
       .hasNumberOfElements(1); 

     ItemAssert 
       .assertThat(itemByPodcast.getContent().get(0)) 
       .hasTitle("Appload 2"); 
    } 

==>https://gist.github.com/davinkevin/df041729608dc21bf7f3

+0

您的推理看起來很有說服力(雖然看起來使用註釋節省的時間將花費在單元測試上):+1。我會等幾個小時,然後我會接受你的答案,如果沒有更令人信服的提供 –

0

你應該測試的功能不是類。如果您不確定映射是否正常工作,那麼測試此類的對象的保存/加載是否適合您測試。但是,單元測試還應該隔離持久層,因此您可以測試業務邏輯而不是持久層。