1 Reply Latest reply on Mar 18, 2009 4:19 PM by Jens Weintraut

    TestNG: Entity is detached?

    Jens Weintraut Apprentice

      Hi,


      at the moment I'm writing some TestNG tests for my application. Those I wrote to test the login functionality work.
      Then I tried to write one to test the creation of new entities. As soon as the Invoke application pahse runs and the persist method on EntityManager is invoked, the test fails since the EntityManager claims that the entity is detached. I had a look at the blog example. For me it seems to work very similar to my code.


      Here is the test code:


      public class CreateTestaction extends SeamTest {
        @Test
        public void createTestaction() throws Exception {
          new FacesRequest() {
            @Override
            protected void updateModelValues() throws Exception {
              Identity.instance().setUsername("AAA");
            }
            
            @Override
            protected void invokeApplication() throws Exception {
              Identity.instance().authenticate();
              setOutcome("createTestaction");
            }
          }.run();
      
          new FacesRequest("/createTestaction.xhtml") {
            @Override
            protected void processValidations() throws Exception {
              validateValue("#{testactionToCreate.ID}", -1L);
              validateValue("#{testactionToCreate.devUsrID}", "AAA");
              validateValue("#{testactionToCreate.TCaseToken}", "test");
              validateValue("#{testactionToCreate.TCaseDescr}", "test");
              validateValue("#{testactionToCreate.TCaseExpResult}", "test");
              validateValue("#{testactionToCreate.defectDescr}", "test");
              validateValue("#{createTestaction.lastSavedTestactionID}", null);
              assert !isValidationFailure();
            }
      
            @Override
            protected void updateModelValues() throws Exception {
      //        EntityManager em = (EntityManager) getInstance("em");
              User user = new User("AAA", "YTrack Test User", null, "ytrack@ytrack.ytrack", "123456789");
      //        setValue("#{testactionToCreate.ID}", -1L);
      ////        setValue("#{testactionToCreate.devUsrID}", em.find(User.class, "AAA"));
      //        setValue("#{testactionToCreate.devUsrID}", user);
      //        setValue("#{testactionToCreate.TCaseToken}", "test");
      //        setValue("#{testactionToCreate.TCaseDescr}", "test");
      //        setValue("#{testactionToCreate.TCaseExpResult}", "test");
      //        setValue("#{testactionToCreate.defectDescr}", "test");
              Testaction testactionToCreate = (Testaction) getInstance("testactionToCreate");
              testactionToCreate.setID(-1L);
              testactionToCreate.setDevUsrID(user);
              testactionToCreate.setTCaseToken("test");
              testactionToCreate.setTCaseDescr("test");
              testactionToCreate.setTCaseExpResult("test");
              testactionToCreate.setDefectDescr("test");
            }
      
            @Override
            protected void invokeApplication() {
              assert invokeMethod("#{createTestaction.createTestaction}").equals("valid");
            }
          }
        }
      }



      Here is the backing bean of the createTestaction.xhtml page:


      @Stateful
      @Scope(ScopeType.SESSION)
      @Name("createTestaction")
      public class CreateTestaction implements ICreateTestaction, Serializable {  
        private static final long serialVersionUID = 7626485366852012295L;
      
        @In
        private transient FacesContext facesContext;
        
        @In
        private transient ITestactionService testactionService;
        
        @In(required=false)
        @Valid
        private User user;
        
        @Valid
        @In(required=false)
        @Out(required=false)
        private Testaction testactionToCreate;
        
        @In(required=false)
        private ITestcaseSelector testcaseSelector;
        
        @Out(required=false)
        private Long lastSavedTestactionID;
        
        private User developer;
      
      
        @Factory("testactionToCreate")
        public void initTestactionToCreate() {
          testactionToCreate = testactionService.getTestactionForCreation(testcaseSelector.getSelectedTestcase(), user);
          testactionToCreate.setDevUsrID(developer);
        }
        
        public String createTestaction() {
          updateDeveloper();
          boolean success = persistCreatedTestaction();
          
          if(success) {
            lastSavedTestactionID = testactionToCreate.getID();
            initTestactionToCreate();
            return "valid";
          } else {
            lastSavedTestactionID = null;
            return "invalid";
          }
          
        }
        
        private void updateDeveloper() {
          developer = testactionToCreate.getDevUsrID();
        }
      }



      And here is a part from TestactionService, which persists the entity:


      @Stateless
      @Name("testactionService")
      @AutoCreate
      public class TestactionService implements ITestactionService {
      //  @PersistenceContext(unitName = "YTrackDatabase")
        @In
        private transient EntityManager em;
      
        @In
        private transient IEnvironmentService environmentService;
      
        public Testaction getTestactionForCreation(Testcase selectedTestcase, User user) {
          Testaction testactionForCreation = new Testaction();
          testactionForCreation.setTesterUsrID(user);
          testactionForCreation.setValidatorUsrID(user);
      
          testactionForCreation.setTObjID((selectedTestcase == null) ? null : selectedTestcase.getTObjID());
          testactionForCreation.setQATID((selectedTestcase == null) ? null : selectedTestcase.getQATID());
          testactionForCreation.setCompID((selectedTestcase == null) ? null : testobjectService.getComponent(selectedTestcase.getCompID()));
      
          if(selectedTestcase != null) {
            testactionForCreation.setTCaseToken(selectedTestcase.getToken());
            testactionForCreation.setTCaseDescr(selectedTestcase.getDescr());
          }
      
          List<Environment> environments = environmentService.getEnvironments(user);
          if(environments != null && environments.size() > 0) {
            testactionForCreation.setEnvID(environments.get(0));
          }
      
          return testactionForCreation;
        }
        
        public void persistAndProtocol(Testaction testaction, Testaction oldTestaction, User user) {
          if(oldTestaction == null) {
            oldTestaction = em.find(Testaction.class, testaction.getID());
          }
      
          if(oldTestaction == null) {
            em.persist(testaction);
            protocolService.protocolChanges(user, new Testaction(), testaction);
          } else if(testaction.getID() == oldTestaction.getID()) {
            em.merge(testaction);
            protocolService.protocolChanges(user, oldTestaction, testaction);
          }
        }
      }



      The call of em.persist(testaction) in TestactionService.persistAndProtocol fails. Hibernate states that the passed entity is detached.


      But comparing my code with the one from the blog example shows that my code works very similar. I don't recognize any important difference. In the blog example the post to persist is manually created (not by setting the value bindings as they are defined in the page). Then it's injected into the PostAction bean which is stateless and injects an EntityManager. The called method then just uses entityManager.persist to save the post.
      In my test I first tried to set the values via value bindings. The resulting entity is then injected into a (stateful session) backing bean which itself passes this entity to a stateless service bean. This service bean gets an EntityManager injected and uses entityManager.persist to save the entity.


      Could somebody please explain me, why my test doesn't work? I don't have any idea why the service bean works fine when the application is running and why it doesn't work in the TestNG test.


      Thanks in advance
      Jens