2011-12-02 42 views
7

Tôi đang cố gắng xây dựng một số thử nghiệm xung quanh một số thực thể được kiểm toán. Vấn đề của tôi là envers chỉ kiểm toán trên một giao dịch cam kết.Thử nghiệm tích hợp với Hibernate Envers

Tôi cần tạo/chỉnh sửa một số đối tượng thử nghiệm, cam kết giao dịch và sau đó kiểm tra các sửa đổi.

Cách tiếp cận tốt nhất để thử nghiệm tích hợp với envers là gì?

Cập nhật: Đây là một lớp học thử nghiệm thực sự không tốt, không xác định về những gì tôi muốn đạt được. Tôi muốn thực hiện việc này mà không cần dựa vào thứ tự của các phương pháp thử nghiệm

Trước tiên hãy tạo tài khoản và account_transaction trong một giao dịch duy nhất. Cả hai mục được kiểm tra đều dành cho sửa đổi 1.

Lần thứ hai cập nhật account_transaction trong giao dịch mới. Mục được kiểm tra đang được sửa đổi 2.

Thứ ba, tải tài khoản đã được kiểm tra tại bản sửa đổi 1 và làm điều gì đó với nó.

@Transactional 
@RunWith(SpringJUnit4ClassRunner.class) 
@ContextConfiguration(locations = {"/testApplicationContext.xml"}) 
public class TestAuditing { 

    @Autowired 
    private AccountDao accountDao; 

    @PersistenceContext 
    private EntityManager entityManager; 

    @Test 
    @Rollback(false) 
    public void first() { 
     Account account = account("Test Account", "xxxxxxxx", "xxxxxx"); 

     AccountTransaction transaction = transaction(new Date(), Deposit, 100, "Deposit"); 
     account.setTransactions(newArrayList(transaction)); 

     accountDao.create(account); 
    } 

    @Test 
    @Rollback(false) 
    public void second() { 
     Account account = accountDao.getById(1L); 
     AccountTransaction transaction = account.getTransactions().get(0); 
     transaction.setDescription("Updated Transaction"); 
     accountDao.update(account); 
    } 

    @Test 
    public void third() { 
     AuditReader reader = AuditReaderFactory.get(entityManager); 

     List<Number> accountRevisions = reader.getRevisions(Account.class, 1L); 
     //One revision [1] 

     List<Number> transactionRevisions = reader.getRevisions(AccountTransaction.class, 1L); 
     //Two revisions [1, 2] 

     Account currentAccount = accountDao.getById(1L); 
     Account revisionAccount = (Account) reader.createQuery().forEntitiesAtRevision(Account.class, 1).getSingleResult(); 

     System.out.println(revisionAccount); 
    } 
+1

Kiểm tra [this] (http://nurkiewicz.blogspot.com/2011/11/spring-pitfalls-transactional-tests.html) quảng cáo tự quảng cáo không biết xấu hổ. –

+0

Cảm ơn bạn đã trả lời Tomasz, tôi vẫn không chắc chắn cách giải quyết vấn đề của tôi từ bài đăng trên blog của bạn. Tôi không thực sự có một vấn đề với các mặt tích cực sai từ tải chậm vv, nhiều hơn với thực sự cam kết một số giao dịch để thiết lập một số dữ liệu kiểm tra kiểm toán. Có lẽ tôi bị mất một cái gì đó rõ ràng trong bài viết của bạn mặc dù? –

+1

Vâng, hãy cuộn bài viết của tôi xuống 'DbResetRule' - ý tưởng của tôi là tránh sử dụng các bài kiểm tra' @ Transactional' JUnit và chỉ cho phép mã của bạn cam kết và các giao dịch rollback. Rõ ràng điều này làm cho các bài kiểm tra không lặp lại và mong manh. Nhưng thay vì quay trở lại những thay đổi, tôi đề nghị bán phá giá cơ sở dữ liệu và khôi phục lại nó trước/sau mỗi lần kiểm tra. Mã là ở Scala, nhưng đây chỉ là một ý tưởng chung. Hãy cho tôi biết nếu đây là những gì bạn đang tìm kiếm vì vậy tôi sẽ xây dựng thêm một chút trong câu trả lời riêng biệt. –

Trả lời

3

Theo đề xuất của Tomasz, tôi đã sử dụng TransactionTemplate để đạt được các cam kết sau mỗi thao tác dao. Không có chú thích @Transactional cấp lớp.

Mục nhập kiểm tra của trình kiểm tra được chèn trước khi phương thức kết thúc, đó chỉ là những gì tôi cần.

@ContextConfiguration("testApplicationContext.xml") 
public class TestAuditing extends AbstractJUnit4SpringContextTests { 

    @Autowired 
    private PlatformTransactionManager platformTransactionManager; 

    @Autowired 
    private PersonDao personDao; 

    private TransactionTemplate template; 

    @Before 
    public void transactionTemplate() { 
     template = new TransactionTemplate(platformTransactionManager); 
    } 

    @Test 
    public void test() { 
     Person person = createInTransaction(person("Karl", "Walsh", address("Middle of nowhere")), personDao); 
     System.out.println(person); 
    } 

    private <T> T createInTransaction(final T object, final Dao<?, T> dao) { 
     return template.execute(new TransactionCallback<T>() { 
      public T doInTransaction(TransactionStatus transactionStatus) { 
       dao.create(object); 
       return object; 
      } 
     }); 
    } 
} 
4

Tôi là một người sử dụng hỗ trợ kiểm tra giao dịch của Spring mà cuộn lại kiểm tra khi thực hiện của họ, và do thiết kế của envers, sửa đổi không được tạo ra. Tôi tạo ra một hack xuất hiện để cho phép một để "nói" envers để làm công việc của mình, bằng tay, trước khi giao dịch cam kết, nhưng cho phép mùa xuân để tiếp tục rollback.

Các đoạn mã này sẽ hữu ích. 1. Tạo danh sách kiểm tra của riêng bạn để ghi đè người nghe kiểm toán hiện tại của máy kiểm duyệt. Điều này cho phép truy cập vào một thành viên tĩnh hiển thị cho các bài kiểm tra đơn vị. Có lẽ là một cách tốt hơn, nhưng nó hoạt động.

public class AuditEventListenerForUnitTesting extends AuditEventListener { 

    public static AuditConfiguration auditConfig; 

    @Override 
    public void initialize(Configuration cfg) { 
     super.initialize(cfg); 
     auditConfig = super.getVerCfg(); 
    } 
} 

sửa đổi persistence.xml bạn để bao gồm lớp này nghe mới thay vì một cung cấp bởi envers

(lặp lại cho thính giả khác nếu cần thiết)

Bây giờ trong "đơn vị" kiểm tra:

{ 
    saveNewList(owner); //code that does usual entity creation 
    em.flush(); 
    EventSource hibSession = (EventSource) em.getDelegate(); 
    AuditEventListenerForUnitTesting.auditConfig.getSyncManager().get(hibSession).doBeforeTransactionCompletion(hibSession);  
    //look for envers revisions now, and they should be there 
} 

Tôi cần điều này vì tôi có một số truy vấn JDBC chống lại các thực thể ngủ đông tham gia vào các bảng phiên bản.

0

Hai giải pháp khác không hiệu quả đối với tôi, vì vậy tôi đã sử dụng một cách khác, tôi chỉ tạo giao dịch mới và bắt buộc thực hiện. Mỗi lần tôi cần một bản sửa đổi mới, tôi làm lại.

@Autowired 
@Qualifier("transactionManager") 
private PlatformTransactionManager platformTransactionManager; 

@Test 
public void enversTest() throws Exception{ 
    Entity myEntity = new Entity(); 

    TransactionStatus status = platformTransactionManager.getTransaction(new DefaultTransactionDefinition()); 
    myEntity.setName("oldName"); 
    myEntity = entityDao.merge(myEntity); 
    platformTransactionManager.commit(status); // creates a revision with oldname 

    TransactionStatus newStatus = platformTransactionManager.getTransaction(new DefaultTransactionDefinition()); 
    myEntity.setName("newName"); 
    myEntity = entityDao.merge(myEntity); 
    platformTransactionManager.commit(newStatus); // creates a new revision with newName 
} 

Nếu bạn sử dụng @Transactional(transactionManager="transactionManager") tuy nhiên nó có thể bỏ qua các cam kết và xem xét mỗi bài kiểm tra là một giao dịch (do đó không versionning nhiều thời gian trong cùng một thử nghiệm ...)

2

này được lấy cảm hứng mạnh mẽ bởi this previous answer thích nghi với làm việc với Envers 4.2.19.Final (JPA 2.0).Giải pháp này không cần giao dịch để cam kết một trong hai, đó là một yêu cầu trong trường hợp của tôi.

Đầu tiên tạo việc thực hiện sau đây của org.hibernate.integrator.spi.Integrator và thêm nó vào classpath:

public class MyIntegrator implements Integrator { 

    public static AuditConfiguration auditConfig; 

    @Override 
    public void integrate(Configuration configuration, SessionFactoryImplementor sessionFactory, 
    SessionFactoryServiceRegistry serviceRegistry) { 
    auditConfig = AuditConfiguration.getFor(configuration); 
    } 

    @Override 
    public void integrate(MetadataImplementor metadata, SessionFactoryImplementor sessionFactory, 
    SessionFactoryServiceRegistry serviceRegistry) { 
    // NOP 
    } 

    @Override 
    public void disintegrate(SessionFactoryImplementor sessionFactory, SessionFactoryServiceRegistry serviceRegistry) { 
    // NOP 
    } 

} 

sau đó tạo ra một tập tin META-INF/services/org.hibernate.integrator.spi.Integrator dưới src/test/resources và dán tên đầy đủ qulified của lớp tích hợp vào nó.

Trong thử nghiệm của mình, gọi phương thức DAO của bạn, hãy xả phiên hibernate và sau khi hoàn thành công việc nói Envers để tiến hành:

EventSource es = (EventSource) entityManager.getDelegate(); 
SessionImplementor si = entityManager.unwrap(SessionImplementor.class); 
MyIntegrator.auditConfig.getSyncManager().get(es).doBeforeTransactionCompletion(si); 

sau đó bạn có thể kiểm tra nội dung của DB của bạn và cuối cùng rollback giao dịch.

Các vấn đề liên quan