I have a lazy one-to-many relationship between two classes AdministrativeUnit (one) and WorkPosition (many). I am creating an integration test using these classes:
// Initialize the database
AdministrativeUnit administrativeUnit = createEntity(em); // create AdministrativeUnit
administrativeUnitRepository.saveAndFlush(administrativeUnit);
// Create a work position under administrative unit
WorkPosition workPosition = WorkPositionResourceIT.createEntity(em); // create WorkPosition with administrativeUnit set as parent
workPositionRepository.saveAndFlush(workPosition);
List<WorkPosition> workPositionList = workPositionRepository.findAll();
assertThat(workPositionList.size()).isEqualTo(1);
assertThat(workPositionList.get(0).getAdministrativeUnit()).isEqualTo(administrativeUnit);
Set<WorkPosition> workPositions = administrativeUnitRepository.getById(administrativeUnit.getId()).getWorkPositions();
assertThat(workPositions).hasSize(1); // fail: expected size 1, actual 0 in []
Why does the last assertion fail? I understand that I have not really set "both sides of the link", but shouldn't it still know that the created WorkPosition belongs to the AdministrativeUnit as I have set the AdministrativeUnit as the parent of the WorkPosition?
The assertion is successful if I, before the getById query, say administrativeUnit.setWorkPositions(Set.of(workPosition));, which seems unnecessary as I actually do not wish to call administrativeUnit.getWorkPositions(); anywhere in the test. It will be called during an http request afterwards, where it behaves the same way.
The assertion is also successful if I, again before the getById query, say em.detach(administrativeUnit), but how this works, I do not know.
Related
I'm new with unit tests. I'm trying test Service Layer in a SPRING APP.
Good, i have any relationships in my Service.
VirtualDatacenterModel vdc = vdcRepository.findById(vmDTO.getVdc()).orElseThrow(() -> new ClientException("Invalid VDC id"));
DataCenterModel dc = vdc.getDatacenter();
String vmName = vdc.getTenant().getName() + "_[" + vmDTO.getName() + "]";
In my test i used MOCKITO, dependencies already is mocked, then i cannot see where is wrong
CreateVmDTO vmDTO = Mockito.mock(CreateVmDTO.class);
VmModel vm = Mockito.mock(VmModel.class);
VirtualDatacenterModel vdc = Mockito.mock(VirtualDatacenterModel.class, Mockito.RETURNS_DEEP_STUBS);
TenantModel tenant = Mockito.mock(TenantModel.class);
Mockito.when(vmRepository.save(vm)).thenReturn(new VmModel());
Mockito.when(vdcRepository.findById(vmDTO.getVdc())).thenReturn(Optional.of(new VirtualDatacenterModel()));
Mockito.doReturn(tenant).when(vdc).getTenant();
Mockito.when(vdc.getTenant().getName()).thenReturn("Olivia");
VmModel vmReturn = vmService.createVM(vmDTO);
And i receive NullPointerException, i probably don't know how to use Mockito correctly
You can only mock one action at the time, the following line will certainly be a problem:
Mockito.when(vdcRepository.findById(vmDTO.getVdc())).thenReturn(Optional.of(new VirtualDatacenterModel()));
cause vmDTO.getVdc() will return a null pointer. (vmDTO is a mocked object itself, and has no instruction set for that call). Assuming vmDTO.getVdc() returns the vdc, you can fix as follows:
CreateVmDTO vmDTO = Mockito.mock(CreateVmDTO.class);
VmModel vm = Mockito.mock(VmModel.class);
VirtualDatacenterModel vdc = Mockito.mock(VirtualDatacenterModel.class, Mockito.RETURNS_DEEP_STUBS);
//example fix:
Mockito.when(vmDTO.getVdc()).thenReturn(vdc);
TenantModel tenant = Mockito.mock(TenantModel.class);
Mockito.when(vmRepository.save(vm)).thenReturn(new VmModel());
//also you can do directly:
Mockito.when(vdcRepository.findById(vdc)).thenReturn(Optional.of(new VirtualDatacenterModel()));
Mockito.doReturn(tenant).when(vdc).getTenant();
Mockito.when(vdc.getTenant().getName()).thenReturn("Olivia");
VmModel vmReturn = vmService.createVM(vmDTO);
Also the naming of your methods throws me off:
You have a method vdcRepository.findById, but your input is a vdc object, and not an id? Either the naming is confusing or your input is wrong. If getVdc returns an Id, then you can fix the code by mocking the return of an Id (rename the method to getVdcId or something).
Note: Personally, I seldom mock a DTO. It is just as easy to make the real DTO object, since they often come with a builder or getter/setter.
Considering an IT with Spring-Boot and JUnit that would test whether a collection returned from database contains all needed elements. What would be the best way to do that?
To illustrate, consider a JPA class/entity such as the following:
class Person {
Integer id;
String name;
String lastName;
Address address;
Account account;
}
Consider that ids of Person, Address and Account would be auto-generated, so I can't infer them.
Any help will be appreciated.
I identity 3 points :
1) Invoke the method under test that is save and flush the entity instance with the JpaRepository dedicated to your entity
2) Make sure that your integration test is reliable/valuable.
Here it matters to clear the first level cache of JPA (EntityManager.clear()) to test the actual retrieval from the database. The cache may hide some issue in your mapping that will be seen only as the object is actually found from the database.
3) Assert the expected behavior that is retrieve the saved entity from the DB and assert its state according to your expected.
For asserting fields of a object AssertJ could interest you.
It doesn't force you to override equals()/hashCode() and it is very simple and meaningful.
As you want to assert nested objects I advise to use a distinct assertThat() by object.
For example :
Person person = new Person()...;
// action
personRepository.saveAndFlush(person);
// clear the first level cache
em.clear();
// assertions
Optional<Person> optPerson = personRepository.findById(person.getId());
// JUnit
Assert.assertTrue(optPerson.isPresent());
// AssertJ
person = optPerson.get();
Assertions.assertThat(person)
.extracting(Person::getName, Person::getLastName)
.containsExactly("expected name", "expected last name");
Assertions.assertThat(person.getAccount())
.extracting(Account::getFoo, Account::getBar)
.containsExactly("expected foo", "expected bar");
Assertions.assertThat(person.getAddress())
.extracting(Address::getStreet, Address::getZip)
.containsExactly("expected street", "expected zip");
I can't get my head around this:
$fragment = factory(Fragment::class)->create();
$this->assertCount(0, $fragment->values);
fragment->fetch(); // updates the 'values' by adding one Value object.
var_dump($fragment->id); // i.e. 6
var_dump(Fragment::first()->id); // 6
var_dump($fragment->values->count()); // 0
var_dump(Fragment::first()->values->count()); // 1
$this->assertCount(1, $fragment->values);
I use DatabaseTranscations, so after a Fragment is created, there is always one and only one. Thus, $fragment and Fragment::first() are the exact same instance. Yet... the values relationship is different. How can this be the case?
Note that this happens only during testing, when I manually test this through my controller (and the values are passed to the blade template page) it works just fine. I am confused :S.
Any ideas?
Relationship attributes ($fragment->values) are only loaded once. They are not kept up to date when you add or delete items from the relationship. They do not hit the database every time to check for changes.
Your second line is $this->assertCount(0, $fragment->values);. Accessing $fragment->values here lazy loads the relationship, and as your assert proves, it is empty.
You then call $fragment->fetch(), in which your comment says it adds a Value object to the fragment. However, your relationship attribute ($fragment->values) has already been loaded from the previous statement, so it will not reflect the additional Value object you added to the relationship.
Therefore, even after the call to fetch(), $fragment->values is still going to be an empty collection. Fragment::first()->values will contain the newly related Value though, because it is getting a new instance of the Fragment, and when it loads the values for the first time, it will pick up the related Value.
When you need to reload the relationship, you can use the load() method. If you add this after your call to fetch() (or put it in your fetch() method, whichever makes sense for you), your test will work fine.
$fragment = factory(Fragment::class)->create();
$this->assertCount(0, $fragment->values);
$fragment->fetch(); // updates the 'values' by adding one Value object.
var_dump($fragment->id); // i.e. 6
var_dump(Fragment::first()->id); // 6
var_dump($fragment->values->count()); // 0
// reload the values relationship
$fragment->load('values');
var_dump($fragment->values->count()); // 1
var_dump(Fragment::first()->values->count()); // 1
$this->assertCount(1, $fragment->values);
The other option you have is to use the relationship query by accessing the relationship method instead of the relationship attribute. If you do $fragment->values()->count() (note: values(), not values), that will hit the database every time and always return the current count.
I have three entities generated by Entity Framework. One is event and this contains navigation properties called frogs and user_bookings. I posted a related question before about performing a sub-query which seems to work but it prevents me overriding lazy loading of a property.
var evts = from evt in context.events.Include("frogs")
where evt.event_id < 10
select evt;
This works - the navigation property frogs gets loaded.
However, when I alter the LINQ to this:
var evts = from evt in context.events.Include("frogs")
where evt.event_id < 10
select new
{
Event = evt,
HasBooked = evt.user_bookings.Any(x => x.user_id == 1)
};
I get an error trying to access the frogs because the ObjectContext no longer exists. I tried removing virtual from the class definition for the event class, but this just results in an empty list of frogs, when they are definitely there!
This is by design. Include is ignored when the query result is a projection, even when the projection contains an entity that could contain the Included properties.
I don't know why EF implemented it this way. If the projection doesn't contain any entities, but is just some type (anonymous or not), there is no Include target, so ignoring it makes sense. But if the projection does contain an Include target (Event in your case) it seems to me that they could have decided to make that work. But, well, they didn't.
Maybe it's because the rules when Include actually has an effect are less obvious than you might expect. In your case, the shape of the query changes after the Include, so it's ignored.
You could work around this by also querying the frogs:
from evt in context.events.Include("frogs")
where evt.event_id < 10
select new
{
Event = evt,
Frogs = evt.frogs,
HasBooked = evt.user_bookings.Any(x => x.user_id == 1)
};
Now each Event will also have its frogs collection filled (because of relationship fixup). But there are two gotchas. The collections are not marked as loaded, so -
if lazy load can occur afterwards, it will occur, making the initial load useless.
if lazy loading can't occur any more (because the context is disposed) you will get an exception.
This means that to make this work you have to disable lazy loading.
Exception I get when saving child record before the parent one:
GrailsWrappedRuntimeException: ORA-02291:
integrity constraint (GRB.FKF8F6B734389E9766) violated -
parent key not found
The code:
class Child {
Parent parent
static constraints = {
parent nullable: true
}
}
class Parent {
static hasMany = [ children: Child ]
}
def child = new Child()
child.save() // first save of Child
def parent = new Parent()
parent.save()
//session.flush() // the sudden flush
child.parent = parent
child.save()
// here is 'constraint violation' when transaction is committed
As it is said in the exception message, Child has parent key Parent set, but somehow the key is not seen as an existing row by Hibernate at that moment...
Above is a simplified reconstruction of events happening in my code. Please note these actions are performed in different places of code (designed to be separated from each other). Unfortunately such a minified code works well and does not reproduce the problem... Also I never met such problem before - I used to save child records before parent ones and vice versa without any problem.
I could fix the code in two ways:
commenting "first save" of Child (ordering of saves is important?!)
or uncommenting the session flush
Although I could fix it I really don't want depend on such things like order of save's or sudden flush'es... Any idea how to cure the problem still having reliable code?
Grails 2.2.0, Hibernate 3.6, Oracle XE
Your exception is probably because you tested around without having the nullable parent constraint on your child table. Without having nullable there will be a 0 or something in your existing entries and new ones, becase DB is doing some magic. GORM will not remove existing FK and it will not add a nullable constraint to existing columns. Try to drop the constraints manually or drop the complete column. Double check for nullable constraint on your parent_id column.
Regarding your problem I would advance you to add the child on the parent, instead of saving the child to be a child:
parent.addToChildren(child)
This also implicitly saves the child, if it was not saved before.