Updating object created by test class's ObjectScope

Thread is closed for posting
7 posts, 0 answers
  1. Denis Vulinovich
    Denis Vulinovich avatar
    29 posts
    Member since:
    Jun 2009

    Posted 26 Jul 2009 Link to this post

    I'm trying to unit test a presenter class that updates a "Project" object. The test class has it's own ObjectScope, as per the help topic "How to: Use OpenAccess ORM with Test Frameworks". I use that ObjectScope to create the Project object, and then I pass it to the presenter. But as soon as the presenter's own ObjectScope tries to access the object, the program immediately jumps out of the current method without executing the rest of the statements.

    I know this happens because I'm using two different ObjectScopes to access the same object, but I haven't found a way around it yet. I've tried using the ObjectScope from the presenter's Visual Studio project in the test class, but it behaves just the same.
  2. Dimitar Kapitanov
    Admin
    Dimitar Kapitanov avatar
    632 posts

    Posted 29 Jul 2009 Link to this post

    Hello Denis Vulinovich,
    Usually when something like this happens (in MSTest framework) it is caused by exception. Can you provide us with more details?

    Sincerely yours,
    Dimitar Kapitanov
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  3. Denis Vulinovich
    Denis Vulinovich avatar
    29 posts
    Member since:
    Jun 2009

    Posted 29 Jul 2009 Link to this post

    Hi Dimitar,

    I'm using MbUnit for my test framework. Here is the Project class.
        [Telerik.OpenAccess.Persistent()] 
        public class Project 
        { 
            private string _name; 
            private string _description; 
     
            public Project() { } 
     
            public Project( string name, string description ) 
            { 
                Name = name
                _description = description; 
            } 
     
            [FieldAlias( "_name" )] 
            public string Name 
            { 
                get { return _name; } 
                set { _name = value; } 
            } 
        } 
     

    This is my TestBase class:
        [TestFixture] 
        public class TestBase 
        { 
            protected IObjectScope _scope; 
     
            [SetUp] 
            public virtual void SetUp() 
            { 
                _scope = ManagementUITestProvider.GetNewObjectScope(); 
                _scope.TransactionProperties.AutomaticBegin = true
            } 
     
            [TearDown] 
            public virtual void TearDown() 
            { 
                if ( _scope.Transaction.IsActive ) 
                    _scope.Transaction.Rollback(); 
                _scope.Dispose(); 
            } 
        } 
     
    You'll see that I'm using the AutomaticBegin property. I've tried setting it to false and using Transaction.Begin() instead, but it doesn't seem to make any difference.

    Here is the test class. I'm using TypeMock to mock the view.
        [TestFixture] 
        [Isolated] 
        public class ProjectEditPresenterFixture : TestBase 
        { 
            IProjectView _viewFake; 
            ProjectEditPresenter _presenter; 
     
            string _projectName = "Project Name"
            Project _project; 
     
            [SetUp] 
            public override void SetUp() 
            { 
                base.SetUp(); 
                _viewFake = Isolate.Fake.Instance<IProjectView>(); 
                CreateDefaultProject();
                _presenter = new ProjectEditPresenter( _viewFake, _project ); 
            } 
     
            private void CreateDefaultProject()
            {
                _project = new Project( _projectName, _projectDesc );
                _scope.Add( _project );
                _scope.Transaction.Commit();
            }

            /// <summary> 
            /// When the OK button is clicked, check that the existing Project object is updated  
            /// in the database using the text from the Name and Description textboxes 
            /// </summary> 
            [Test] 
            public void OKButtonClicked_UpdatesProject() 
            { 
                string newName = "New Project Name"
                _viewFake.NameText = newName
     
                _presenter.OKButtonClicked(); 
     
                Project readProject = new ProjectQuery().Read( newName, _scope );   // Does a LINQ query 
                Assert.IsNotNull( readProject );    // Fails - object in database has original "Project Name"
            } 
        } 
     
    The Assert fails and the database shows that the Project object is unchanged.

    And here is the Presenter:
        public class ProjectEditPresenter : ProjectPresenter 
        { 
            Project _project; 
     
            public ProjectEditPresenter( IProjectView view, Project project ) : base( view ) 
            { 
                _project = project;   // _view is set in base class 
            } 
     
            public void OKButtonClicked() 
            { 
                using ( IObjectScope scope = ManagementUIProvider.GetNewObjectScope() ) 
                { 
                    try 
                    { 
                        scope.Transaction.Begin(); 
                        _project.Name = _view.NameText; 
                        _project.Description = _view.DescriptionText; 
                        System.Console.WriteLine( "It gets to here..." ); 
                        scope.Add( _project ); 
                        System.Console.WriteLine( "... but it doesn't get to here" ); 
                        scope.Transaction.Commit(); 
                        _view.CloseForm(); 
                    } 
                    catch ( Exception e ) 
                    { 
                        _view.DisplayMessage( e.Message ); 
                    } 
                } 
            } 
        } 
     
    The ManagementUIProvider in the OKButtonClicked() method differs from the ManagementUITestProvider in the TestBase class. The two WriteLines in this method show that it executes up to the scope.Add() statement but it doesn't get to the Commit().
  4. Dimitar Kapitanov
    Admin
    Dimitar Kapitanov avatar
    632 posts

    Posted 03 Aug 2009 Link to this post

    Hello Denis Vulinovich,
    Are you able to break in the catch block and see if/what exception is raised there?
    When I looked at your code, I think you are adding the same instance to two different scopes, and I think that might cause the problem (because underneath the second scope possibly knows as well for this entity, and it might raise exception of the type "The XXX is already in the scope" or similar). Please check whether this is the case.

    Sincerely yours,
    Dimitar Kapitanov
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
  5. Denis Vulinovich
    Denis Vulinovich avatar
    29 posts
    Member since:
    Jun 2009

    Posted 03 Aug 2009 Link to this post

    Hi Dimitar,

    Thanks for your reply. Here's the exception that's caught in the catch block:

    Object references between two different object scopes are not allowed. The object 'Integrator.Management.Project' is already managed by 'ObjectScopeImpl 0x3f72ff5 Telerik.OpenAccess.RT.ObjectScope' and was tried to be managed again by 'ObjectScopeImpl 0x209ad8d Telerik.OpenAccess.RT.ObjectScope'

    I already knew this is happening because I'm using two different ObjectScopes to access the same object, as I said in my original post. The problem is how to get around it?

    I'm trying to unit test the methods in the Presenter class in isolation. So the SetUp() method of the Fixture class uses CreateDefaultProject() to store a Project object before each test, as follows:

            private void CreateDefaultProject()
            {
                ProjectFixture.DeleteAllProjects( _scope );
                _project = new Project( _projectName, _projectDesc );
                _scope.Add( _project );
                _scope.Transaction.Commit();
            }

    So it's using the _scope object from the TestBase class to create the object, as per the recommendation in the help topic "How to: Use OpenAccess ORM with Test Frameworks". Then the test method calls ProjectEditPresenter.OKButtonClicked() which uses a different ObjectScope to update the object - which causes the exception.

    So either I'm doing something wrong, or the help topic isn't very helpful.

  6. Denis Vulinovich
    Denis Vulinovich avatar
    29 posts
    Member since:
    Jun 2009

    Posted 05 Aug 2009 Link to this post

    Hi Dimitar,

    I've got it to work by using the ObjectScope from the Presenter in the Test class as well, using the following code in each:

        IObjectScope _scope = ManagementUIProvider.ObjectScope(); 
        _scope.TransactionProperties.AutomaticBegin = true
     

    Also, I do 2 things differently to what it says in the help topic "How to: Use OpenAccess ORM with Test Frameworks":
    • I call ...Provider.ObjectScope() in each case to obtain the singleton ObjectScope instance, instead of calling GetNewObjectScope() to obtain a new instance.
    • I don't call _scope.Dispose() at the end of each test in the TestCleanup/TearDown method of the TestBase class.

    The approach in the help topic doesn't work with Update and Delete operations - at least not in my case.
  7. Ady
    Admin
    Ady avatar
    589 posts

    Posted 05 Aug 2009 Link to this post

    Hello Denis Vulinovich,

     What you've done is perfectly fine. The setup in the article uses the scope defined in the TestBase class and all tests reuse this single scope instance. Your setup differs in the usage of the scope where the Presenter maintains its own scope instance.

     The ObjectScopeProvider( in your case the ManagementUIProvider) caches the single scope instance and it is returned when you call the 'ObjectScope' instance. If disposed after each test, you should set it to null so that you get a fresh instance the next time you call 'ObjectScope'.

    Kind regards,
    Ady
    the Telerik team

    Instantly find answers to your questions on the new Telerik Support Portal.
    Check out the tips for optimizing your support resource searches.
Back to Top