/** * Copyright 2009-2013 Oy Vaadin Ltd * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.vaadin.addon.jpacontainer; import static org.easymock.EasyMock.createNiceMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import java.text.NumberFormat; import java.text.ParseException; import java.util.Collection; import java.util.Date; import org.junit.Before; import org.junit.Test; import com.vaadin.addon.jpacontainer.testdata.Address; import com.vaadin.addon.jpacontainer.testdata.Person; import com.vaadin.v7.data.Property; import com.vaadin.v7.data.Property.ReadOnlyException; import com.vaadin.v7.data.Property.ValueChangeEvent; import com.vaadin.v7.data.util.converter.StringToDoubleConverter; import com.vaadin.v7.ui.CheckBox; import com.vaadin.v7.ui.TextField; /** * Test case for {@link JPAContainerItem}. * * @author Petter Holmström (Vaadin Ltd) * @since 1.0 */ @SuppressWarnings("serial") public class JPAContainerItemTest { private JPAContainerItem<Person> item; private Person entity; private JPAContainer<Person> container; private JPAContainerItem<Person> modifiedItem; private String modifiedPropertyId; @SuppressWarnings("unchecked") @Before public void setUp() { container = new JPAContainer<Person>(Person.class) { /* * The following two methods are used to verify that the item * notifies the container when items or item properties are changed. * * The assertions in the beginning of each method make sure that the * test fails if any of the methods are called twice in a row * without the modifiedItem and modifiedPropertyId values being * explicitly set to null in between. */ @Override protected void containerItemPropertyModified( JPAContainerItem<Person> item, String propertyId) { assertNull("modifiedItem was not null", modifiedItem); assertNull("modifiedPropertyId was not null", modifiedPropertyId); modifiedItem = item; modifiedPropertyId = propertyId; } @Override protected void containerItemModified(JPAContainerItem<Person> item) { assertNull("modifiedItem was not null", modifiedItem); assertNull("modifiedPropertyId was not null", modifiedPropertyId); modifiedItem = item; } }; EntityProvider<Person> entityProviderMock = createNiceMock(EntityProvider.class); expect(entityProviderMock.getLazyLoadingDelegate()).andStubReturn(null); replay(entityProviderMock); container.setEntityProvider(entityProviderMock); modifiedItem = null; modifiedPropertyId = null; container.addNestedContainerProperty("address.street"); container.addNestedContainerProperty("address.fullAddress"); entity = new Person(); entity.setId(123l); entity.setAddress(new Address()); Person person = new Person(); person.setId(124l); entity.setManager(person); item = new JPAContainerItem<Person>(container, entity); } @Test public void testPrimitiveTypesWithFields() throws ParseException { CheckBox checkBox = new CheckBox(); TextField textField = new TextField(); textField.setConverter(new StringToDoubleConverter()); EntityItemProperty maleProperty = item.getItemProperty("male"); EntityItemProperty doubleProperty = item .getItemProperty("primitiveDouble"); checkBox.setPropertyDataSource(maleProperty); textField.setPropertyDataSource(doubleProperty); Boolean originalMaleValue = (Boolean) checkBox.getValue(); Double originalDoubleValue = Double.parseDouble((String) textField .getValue()); assertEquals(false, originalMaleValue); assertEquals(0.0, originalDoubleValue.doubleValue(), 0.000001); checkBox.setValue(true); modifiedItem = null; modifiedPropertyId = null; // since the locale decimal separator may not be a dot textField.setValue(NumberFormat.getInstance().format(3.55)); modifiedItem = null; modifiedPropertyId = null; Boolean newMaleValue = (Boolean) checkBox.getValue(); Double newDoubleValue = NumberFormat.getInstance().parse((String) textField .getValue()).doubleValue(); assertEquals(true, newMaleValue); assertEquals((Double) 3.55, newDoubleValue); Person p = item.getEntity(); assertEquals(true, p.isMale()); assertEquals(3.55, p.getPrimitiveDouble(), 0.0000001); } @Test public void testGetItemPropertyIds() { Collection<String> propertyIds = item.getItemPropertyIds(); assertEquals( container.getPropertyList().getAllAvailablePropertyNames(), propertyIds); } @Test public void testGetItemProperty() { assertNotNull(item.getItemProperty("firstName")); assertNull(item.getItemProperty("nonexistent")); } @Test public void testIsPersistent() { assertTrue(item.isPersistent()); item.setPersistent(false); assertFalse(item.isPersistent()); // Try the other constructor item = new JPAContainerItem<Person>(container, entity, null, true); assertFalse(item.isPersistent()); } @Test public void testIsDirty_NonPersistent() { item.setPersistent(false); item.setDirty(true); assertFalse(item.isDirty()); } @Test public void testGetItemId() { assertEquals(123l, item.getItemId()); // Try the other constructor item = new JPAContainerItem<Person>(container, entity, null, true); assertNull(item.getItemId()); } @Test public void testGetEntity() { assertSame(entity, item.getEntity()); } @Test public void testGetContainer() { assertSame(container, item.getContainer()); } @Test public void testPropertyType() { assertEquals(String.class, item.getItemProperty("firstName").getType()); assertEquals(Date.class, item.getItemProperty("dateOfBirth").getType()); assertEquals(String.class, item.getItemProperty("address.street") .getType()); assertEquals(Address.class, item.getItemProperty("address").getType()); assertEquals(Person.class, item.getItemProperty("manager").getType()); // should report wrapper types for beans primitive types assertEquals(Boolean.class, item.getItemProperty("male").getType()); assertEquals(Double.class, item.getItemProperty("primitiveDouble") .getType()); } @Test public void testPropertyReadOnly() { assertFalse(item.getItemProperty("firstName").isReadOnly()); assertFalse(item.getItemProperty("address.street").isReadOnly()); assertTrue(item.getItemProperty("fullName").isReadOnly()); assertTrue(item.getItemProperty("address.fullAddress").isReadOnly()); } @Test public void testAddNestedProperty() { item.addNestedContainerProperty("address.postalCode"); assertNotNull(item.getItemProperty("address.postalCode")); } @Test public void testRemoveProperty() { item.addNestedContainerProperty("address.postalCode"); assertFalse(item.removeItemProperty("firstName")); assertTrue(item.removeItemProperty("address.postalCode")); assertNotNull(item.getItemProperty("firstName")); assertNull(item.getItemProperty("address.postalCode")); } @Test public void testPropertyValue_Unbuffered() { final Property prop = item.getItemProperty("firstName"); final boolean[] listenerCalled = new boolean[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0] = true; } }); assertTrue(item.isReadThrough()); assertTrue(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertFalse(listenerCalled[0]); prop.setValue("Hello"); assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertEquals("Hello", item.getEntity().getFirstName()); assertFalse(item.isModified()); assertTrue(item.isDirty()); assertTrue(listenerCalled[0]); assertEquals("firstName", modifiedPropertyId); assertSame(item, modifiedItem); } @Test public void testPropertyValueFromString_Unbuffered() { final Property prop = item.getItemProperty("id"); assertEquals(123l, prop.getValue()); prop.setValue("1234"); assertEquals(1234l, prop.getValue()); } @Test public void testNestedPropertyValue_Unbuffered() { final Property prop = item.getItemProperty("address.street"); final boolean[] listenerCalled = new boolean[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0] = true; } }); assertTrue(item.isReadThrough()); assertTrue(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertFalse(listenerCalled[0]); prop.setValue("World"); assertEquals("World", prop.getValue()); assertEquals("World", prop.toString()); assertEquals("World", item.getEntity().getAddress().getStreet()); assertFalse(item.isModified()); assertTrue(item.isDirty()); assertTrue(listenerCalled[0]); assertEquals("address.street", modifiedPropertyId); assertSame(item, modifiedItem); } @Test public void testLocalNestedPropertyValue_Unbuffered() { item.addNestedContainerProperty("address.postalCode"); final Property prop = item.getItemProperty("address.postalCode"); final boolean[] listenerCalled = new boolean[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0] = true; } }); assertTrue(item.isReadThrough()); assertTrue(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertFalse(listenerCalled[0]); prop.setValue("World"); assertEquals("World", prop.getValue()); assertEquals("World", prop.toString()); assertEquals("World", item.getEntity().getAddress().getPostalCode()); assertFalse(item.isModified()); assertTrue(item.isDirty()); assertTrue(listenerCalled[0]); assertEquals("address.postalCode", modifiedPropertyId); assertSame(item, modifiedItem); } @Test(expected = ReadOnlyException.class) public void testPropertyValue_Unbuffered_ReadOnly_throwsException() { entity.setFirstName("Joe"); entity.setLastName("Cool"); item.getItemProperty("fullName").setValue("Blah"); } @Test(expected = ReadOnlyException.class) public void testPropertyValue_Unbuffered_ReadOnly_valueChangeNotFired() { entity.setFirstName("Joe"); entity.setLastName("Cool"); final Property prop = item.getItemProperty("fullName"); ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { fail("No listener should be called"); } }); prop.setValue("Blah"); } @Test public void testPropertyValue_Unbuffered_ReadOnly_nothingChanges() { entity.setFirstName("Joe"); entity.setLastName("Cool"); final Property prop = item.getItemProperty("fullName"); try { prop.setValue("Blah"); } catch (ReadOnlyException e) { assertEquals("Joe Cool", prop.getValue()); assertFalse(item.isDirty()); assertFalse(item.isModified()); } assertNull(modifiedPropertyId); assertNull(modifiedItem); } @Test public void testNestedPropertyValue_Unbuffered_ReadOnly() { entity.getAddress().setStreet("Street"); entity.getAddress().setPostalCode("1234"); entity.getAddress().setPostOffice("Office"); final Property prop = item.getItemProperty("address.fullAddress"); ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { fail("No listener should be called"); } }); try { prop.setValue("Blah"); fail("No exception thrown"); } catch (ReadOnlyException e) { assertEquals("Street 1234 Office", prop.toString()); assertFalse(item.isDirty()); assertFalse(item.isModified()); } assertNull(modifiedPropertyId); assertNull(modifiedItem); } @Test public void testPropertyValue_Buffered_NoReadThrough_Commit() { item.setWriteThrough(false); final Property prop = item.getItemProperty("firstName"); final int[] listenerCalled = new int[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0]++; } }); assertTrue(item.isReadThrough()); assertFalse(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertNull(prop.toString()); assertEquals(0, listenerCalled[0]); prop.setValue("Hello"); // Write through is false, so we should always get the cached value assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getFirstName()); assertTrue(item.isModified()); assertFalse(item.isDirty()); assertEquals(1, listenerCalled[0]); item.commit(); assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertEquals("Hello", item.getEntity().getFirstName()); assertFalse(item.isModified()); assertTrue(item.isDirty()); assertEquals(1, listenerCalled[0]); assertNull(modifiedPropertyId); assertSame(item, modifiedItem); } @Test public void testNestedPropertyValue_Buffered_NoReadThrough_Commit() { item.setWriteThrough(false); final Property prop = item.getItemProperty("address.street"); final int[] listenerCalled = new int[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0]++; } }); assertTrue(item.isReadThrough()); assertFalse(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertNull(prop.toString()); assertEquals(0, listenerCalled[0]); prop.setValue("Hello"); // Read through is false, so we should get the cached value assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getAddress().getStreet()); assertTrue(item.isModified()); assertFalse(item.isDirty()); assertEquals(1, listenerCalled[0]); item.commit(); assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertEquals("Hello", item.getEntity().getAddress().getStreet()); assertFalse(item.isModified()); assertTrue(item.isDirty()); assertEquals(1, listenerCalled[0]); assertNull(modifiedPropertyId); assertSame(item, modifiedItem); } @Test public void testLocalNestedPropertyValue_Buffered_NoReadThrough_Commit() { item.setWriteThrough(false); item.addNestedContainerProperty("address.postalCode"); final Property prop = item.getItemProperty("address.postalCode"); final int[] listenerCalled = new int[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0]++; } }); assertTrue(item.isReadThrough()); assertFalse(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertNull(prop.toString()); assertEquals(0, listenerCalled[0]); prop.setValue("Hello"); // Read through is false, so we should get the cached value assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getAddress().getPostalCode()); assertTrue(item.isModified()); assertFalse(item.isDirty()); assertEquals(1, listenerCalled[0]); item.commit(); assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertEquals("Hello", item.getEntity().getAddress().getPostalCode()); assertFalse(item.isModified()); assertTrue(item.isDirty()); assertEquals(1, listenerCalled[0]); assertNull(modifiedPropertyId); assertSame(item, modifiedItem); } @Test public void testPropertyValue_Buffered_NoReadThrough_Discard() { item.setWriteThrough(false); final Property prop = item.getItemProperty("firstName"); final int[] listenerCalled = new int[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0]++; } }); assertTrue(item.isReadThrough()); assertFalse(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertNull(prop.toString()); assertEquals(0, listenerCalled[0]); prop.setValue("Hello"); // Read through is false, so we should get the cached value assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getFirstName()); assertTrue(item.isModified()); assertFalse(item.isDirty()); assertEquals(1, listenerCalled[0]); item.discard(); assertNull(prop.getValue()); assertNull(prop.toString()); assertNull(item.getEntity().getFirstName()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertEquals(2, listenerCalled[0]); assertNull(modifiedPropertyId); assertNull(modifiedItem); } @Test public void testNestedPropertyValue_Buffered_NoReadThrough_Discard() { item.setWriteThrough(false); final Property prop = item.getItemProperty("address.street"); final int[] listenerCalled = new int[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0]++; } }); assertTrue(item.isReadThrough()); assertFalse(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertNull(prop.toString()); assertEquals(0, listenerCalled[0]); prop.setValue("Hello"); // Read through is false, so we should get the cached value assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getAddress().getStreet()); assertTrue(item.isModified()); assertFalse(item.isDirty()); assertEquals(1, listenerCalled[0]); item.discard(); assertNull(prop.getValue()); assertNull(prop.toString()); assertNull(item.getEntity().getAddress().getStreet()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertEquals(2, listenerCalled[0]); assertNull(modifiedPropertyId); assertNull(modifiedItem); } @Test public void testLocalNestedPropertyValue_Buffered_NoReadThrough_Discard() { item.addNestedContainerProperty("address.postalCode"); item.setWriteThrough(false); final Property prop = item.getItemProperty("address.postalCode"); final int[] listenerCalled = new int[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0]++; } }); assertTrue(item.isReadThrough()); assertFalse(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertNull(prop.toString()); assertEquals(0, listenerCalled[0]); prop.setValue("Hello"); // Read through is false, so we should get the cached value assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getAddress().getPostalCode()); assertTrue(item.isModified()); assertFalse(item.isDirty()); assertEquals(1, listenerCalled[0]); item.discard(); assertNull(prop.getValue()); assertNull(prop.toString()); assertNull(item.getEntity().getAddress().getPostalCode()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertEquals(2, listenerCalled[0]); assertNull(modifiedPropertyId); assertNull(modifiedItem); } @Test public void testPropertyValue_Buffered_ReadThrough_Commit() { item.setWriteThrough(false); item.setReadThrough(true); final Property prop = item.getItemProperty("firstName"); final int[] listenerCalled = new int[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0]++; } }); assertTrue(item.isReadThrough()); assertFalse(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertNull(prop.toString()); assertEquals(0, listenerCalled[0]); prop.setValue("Hello"); // Write through is false, so we should get the buffered value assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getFirstName()); assertTrue(item.isModified()); assertFalse(item.isDirty()); // ValueChanges should always be fired if the value is changed whether // buffered or not. assertEquals(1, listenerCalled[0]); // Now, we temporarily turn off read through item.setReadThrough(false); assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getFirstName()); assertEquals(1, listenerCalled[0]); // Should not have been triggered // again. // Now, we turn on read through again item.setReadThrough(true); assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getFirstName()); assertEquals(1, listenerCalled[0]); // Should not have been triggered // again item.commit(); assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertEquals("Hello", item.getEntity().getFirstName()); assertFalse(item.isModified()); assertTrue(item.isDirty()); assertEquals(1, listenerCalled[0]); // Should not have been triggered // again assertNull(modifiedPropertyId); assertSame(item, modifiedItem); } @Test public void testPropertyValue_Buffered_ReadThrough_Discard() { item.setWriteThrough(false); item.setReadThrough(true); final Property prop = item.getItemProperty("firstName"); final int[] listenerCalled = new int[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0]++; } }); assertTrue(item.isReadThrough()); assertFalse(item.isWriteThrough()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertNull(prop.getValue()); assertNull(prop.toString()); assertEquals(0, listenerCalled[0]); prop.setValue("Hello"); // Read through is true, so we should still get the real value assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getFirstName()); assertTrue(item.isModified()); assertFalse(item.isDirty()); assertEquals(1, listenerCalled[0]); // Now, we temporarily turn off read through item.setReadThrough(false); assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getFirstName()); assertEquals(1, listenerCalled[0]); // Should not have been called again // Now, we turn on read through again item.setReadThrough(true); assertEquals("Hello", prop.getValue()); assertEquals("Hello", prop.toString()); assertNull(item.getEntity().getFirstName()); assertEquals(1, listenerCalled[0]); // Should not have been called again item.discard(); assertNull(prop.getValue()); assertNull(prop.toString()); assertNull(item.getEntity().getFirstName()); assertFalse(item.isModified()); assertFalse(item.isDirty()); assertEquals(2, listenerCalled[0]); // The value changes back when // changes are discarded assertNull(modifiedPropertyId); assertNull(modifiedItem); } @Test public void testTurnOnWriteThrough() { item.setWriteThrough(false); final Property prop = item.getItemProperty("address.street"); final int[] listenerCalled = new int[1]; ((Property.ValueChangeNotifier) prop) .addListener(new Property.ValueChangeListener() { public void valueChange(ValueChangeEvent event) { assertSame(prop, event.getProperty()); listenerCalled[0]++; } }); prop.setValue("Hello"); assertEquals(1, listenerCalled[0]); item.setWriteThrough(true); assertEquals("Hello", prop.getValue()); assertEquals("Hello", item.getEntity().getAddress().getStreet()); assertFalse(item.isModified()); assertTrue(item.isDirty()); assertEquals(1, listenerCalled[0]); // The value has not changed, only // written to the entity assertTrue(item.isWriteThrough()); assertTrue(item.isReadThrough()); // Is the default assertNull(modifiedPropertyId); assertSame(item, modifiedItem); } @Test public void testTurnOffReadThroughWithWriteThroughActive() { try { item.setReadThrough(false); } catch (IllegalStateException e) { assertTrue(item.isReadThrough()); } } @Test public void testWriteTroughOffReadThroughOn() { item.setReadThrough(true); item.setWriteThrough(false); // sets the buffered value item.getItemProperty("firstName").setValue("foo"); // gets the buffered value assertEquals("foo", item.getItemProperty("firstName").getValue()); // make sure the value has not been written through assertFalse("foo".equals(item.getEntity().getFirstName())); // after commit the entity should be updated. item.commit(); assertEquals("foo", item.getEntity().getFirstName()); } // TODO Test registering property listeners through item }