Neo4jOperations#queryForObjects() doesn't seem to play well with #QueryResult POJOs - it always says that result set is empty.
Trying Neo4jOperations#queryForObjects - it says result is empty:
#Test
public void thisDoesNotWork() {
Iterable<ClassNodeIdAndName> result = neo4jOperations.queryForObjects(
ClassNodeIdAndName.class,
"MATCH (c:ClassNode) RETURN ID(c) AS id, c.name AS name",
new HashMap<>());
assertTrue(result.iterator().hasNext());
}
Trying Neo4jOperations#query - says result is NOT empty:
#Test
public void thisWorksFine() {
Result result = neo4jOperations.query(
"MATCH (c:ClassNode) RETURN ID(c) AS id, c.name AS name",
new HashMap<>());
assertTrue(result.iterator().hasNext());
}
Trying repository with #Query - says result is NOT empty:
#Test
public void thisWorksFineAsWell() {
List<ClassNodeIdAndName> classNodeIdsAndNames = classNodeRepository.getAllIdsAndNames();
assertFalse(classNodeIdsAndNames.isEmpty());
}
public interface ClassNodeRepository extends GraphRepository<ClassNode> {
#Query("MATCH (c:ClassNode) RETURN ID(c) AS id, c.name AS name")
List<ClassNodeIdAndName> getAllIdsAndNames();
}
#QueryResult
public class ClassNodeIdAndName {
public Long id;
public String name;
}
Documentation says that
Iterable queryForObjects(Class entityType,
entityType - The Class denoting the type of entity to return
But I'm confused whether I should look at type of entity or at for objects. If it's not supposed to handle #QueryResult, I would expect it to throw instead of returning no results.
I'm using spring-data-neo4j 4.1.3.RELEASE
#QueryResult is a Spring Data Neo4j concept that applies only to Spring Repositorys.
Neo4jOperations is a thin wrapper around the Neo4j OGM's Session class and consequently does not handle the concept of returning query result objects.
Also see: SDN 4 Session.query doesn't work for #QueryResult
Related
I have this class created:
public with sharing class CheckingClass{
public String listObjects{
get{
if(String.isEmpty(listObjects)){
listObjects = this.getListObjects();
}
return (listObjects);
}
set;
}
getListObjects()is a private method that gets a list of objects from database.
Point is that when I try to call the listObjects in my test class I can achieve to get through the get
Test class
#isTest(SeeAllData=false)
private class CheckingClassTest {
#istest static void test_method(){
Test.startTest();
CheckingClass chClass = new CheckingClass();
chClass.listObjects;
Test.stopTest();
}
}
But it's giving me:
classes/CheckingClassTest.cls: Expression cannot be a statement.
Does anyone knows how to achieve this?
You have to actually accept that return value, you're trying to set it so
String aString = chClass.listObjects;
NodeEntity:
#NodeEntity(label = "User")
public class UserNode {
#GraphId
private Long _gid;
#Index(unique = true, primary = true)
#Convert(ObjectIdConverter.class)
private ObjectId id;
}
Converter:
public class ObjectIdConverter implements AttributeConverter<ObjectId, String>{
#Override
public String toGraphProperty(ObjectId value) {
return ObjectIdUtils.compressed(value);
}
#Override
public ObjectId toEntityAttribute(String value) {
return ObjectIdUtils.uncompress(value);
}
}
Repository:
public interface UserNodeRepository extends GraphRepository<UserNode> {
#Query("MATCH (user:User) WHERE user.id IN {0} RETURN user")
List<UserNode> findByIdIn(List<ObjectId> ids);
}
UserNodeRepository#findByIdIn is custom query. but the request parameter directly convert to json without using ObjectIdConverter.
Log:
o.n.o.drivers.bolt.request.BoltRequest : Request: MATCH (user:User) WHERE user.id IN {0} RETURN user with params {0=[{timestamp=1500442853, machineIdentifier=11302907, processIdentifier=11906, counter=4709865, time=1500442853000, date=1500442853000, timeSecond=1500442853}, {timestamp=1500445335, machineIdentifier=11302907, processIdentifier=11906, counter=4709946, time=1500445335000, date=1500445335000, timeSecond=1500445335}, {timestamp=1500447522, machineIdentifier=11302907, processIdentifier=11906, counter=4710014, time=1500447522000, date=1500447522000, timeSecond=1500447522}, {timestamp=1500448399, machineIdentifier=11302907, processIdentifier=11906, counter=4710092, time=1500448399000, date=1500448399000, timeSecond=1500448399}]}
Is this the SDN expectation or what concepts have I lost?
This is not possible for finders with custom #Query. There is no way for OGM to know that your parameter relates to a property with #Convert annotation (the method name is not used at all). To solve this convert to your property type manually instead and use that as a parameter:
#Query("MATCH (user:User) WHERE user.id IN {0} RETURN user")
List<UserNode> findByIdIn(List<String> ids);
However this works fine for derived finders - OGM knows the field, and the #Convert annotation with the right converter, from the method name.
Example with single parameter:
List<UserNode> findById(ObjectId id);
Unfortunately there seems to be a bug for the IN operator and a collection parameter with:
// doesn't work
List<UserNode> findByIdIn(List<ObjectId> ids);
I have created a jira issue.
I wrote the following customization and have it applied as part of a composite on most of my tests. My entities have a read-only Id, but I'm using their SetId method in this customization to make sure all entities have some Id if they are transient (don't have an Id already).
public class SetEntityIdCustomization : ICustomization {
public void Customize(IFixture fixture) {
var engine = ((Fixture)fixture).Engine;
fixture.Customizations.Add(new Postprocessor(
engine, o => {
var entity = o as BaseEntity;
if (entity == null || !entity.IsTransient()) {
return;
}
entity.SetId(fixture.CreateAnonymous<Guid>());
}));
}
}
This has been working great, until I discovered a very odd thing today. If I feed a test one of my entities that directly inherits from BaseEntity, all is well and it's writeable properties are auto-filled. However, if I ask for an entity that inherits from something further down from BaseEntity, my customization prevents the properties from auto-filling.
The User entity in this test method is filled properly:
public class User : BaseEntity {
public string Email { get; set; }
public int CoolThings { get; set; }
}
...
[Theory, AutoDomainData]
public void SomeTest(User user, ...) {
// user.Email and user.CoolThings have auto-filled values, as expected.
...
}
However, the AwesomeUser entity in the following test does not get any of the same properties auto-filled.
public class AwesomeUser : User {
...
}
...
[Theory, AutoDomainData]
public void SomeOtherTest(AwesomeUser user, ...) {
// user.Email nor user.CoolThings have auto-filled values. What gives?
...
}
In both test cases, the Id property is auto-filled because of my customization. If I remove my customization, the SomeOtherTest's AwesomeUser instance gets its inherited properties auto-filled just fine. I must assume that my customization is what is messing things up.
Is there a better way to get all my BaseEntity instances to set their Id, or is there something else I'm missing with AutoFixture? I've applied my customization first, in the middle, and last, to no avail.
The solution provided above is a pretty clever attempt, but not something I've seen before. A more idiomatic solution would be something like this:
public void Customize(IFixture fixture)
{
fixture.Customizations.Add(
new FilteringSpecimenBuilder(
new Postprocessor(
new BaseEntityBuilder(
new ConstructorInvoker(
new ModestConstructorQuery())),
new AutoPropertiesCommand().Execute),
new BaseEntitySpecification()));
}
private class BaseEntityBuilder : ISpecimenBuilder
{
private readonly ISpecimenBuilder builder;
private readonly IRequestSpecification specification;
public BaseEntityBuilder(ISpecimenBuilder builder)
{
this.builder = builder;
this.specification = new BaseEntitySpecification();
}
public object Create(object request, ISpecimenContext context)
{
if (!this.specification.IsSatisfiedBy(request))
return new NoSpecimen(request);
var b = (BaseEntity)this.builder.Create(request, context);
b.SetId((Guid)context.Resolve(typeof(Guid)));
return b;
}
}
private class BaseEntitySpecification : IRequestSpecification
{
public bool IsSatisfiedBy(object request)
{
var t = request as Type;
if (t == null)
return false;
if (!typeof(BaseEntity).IsAssignableFrom(t))
return false;
return true;
}
}
As you can see, this isn't a simple one-liner, which is indicative of AutoFixture being a rather opinionated library. In this case, AutoFixture's opinion is:
Favor object composition over class inheritance.
-Design Patterns, p. 20
AutoFixture is first and foremost a TDD tool, and one of the main advantages of TDD is that it provides feedback about class design. In this case, the feedback is: Inheritance is awkward and troublesome. Reconsider the design.
I have the following class that has two static methods Retrieve and RetrieveWithQuery.
Below the classes listed here, I have included a snippet of the test.
All but the last assert of the test fails with the following message:
Failed TestMethod2 MoqTest Assert.AreEqual failed. Expected:. Actual:<(null)>.
I understand that the problem may be that the query that I setup in the mock
is a different instance from the query used in the RetrieveWithQuery method.
And that is why is would be returning null.
In a perfect world I would simply re-factor the service class, unfortunately I am
working with legacy code that is already production. The goal is to first complete
tests, then re-factor code and run regression testing before updating production
environment.
Is there a workaround or different way to test this?
public class MyService
{
public virtual string RetrieveMethod(string account)
{
if (account == "The abc company")
{
return "Peter Smith";
}
return "John Doe";
}
public virtual string RetrieveMethod(MyQuery query)
{
return RetrieveMethod(query.QueryString);
}
public static string Retrieve(MyService service, string value)
{
return service.RetrieveMethod(value);
}
public static string RetrieveWithQuery(MyService service, string value)
{
var query = new MyQuery
{
QueryString = value
};
return service.RetrieveMethod(query);
}
}
public class MyQuery
{
public string QueryString;
}
[TestMethod]
public void TestMethod2()
{
var mockService = new Mock<MyService>();
const string company = "The abc company";
const string expectedContact = "Peter Smith";
var queryAccount = new MyQuery
{
QueryString = company
};
// Setup base retrieve
mockService.Setup(myServ => myServ.RetrieveMethod(company)).Returns(expectedContact);
// Setup base retrieve with query
mockService.Setup(myServ => myServ.RetrieveMethod(queryAccount)).Returns(expectedContact);
// test base retrieve with query - PASS
Assert.AreEqual(expectedContact, mockService.Object.RetrieveMethod(queryAccount));
// test static method retrieve - PASS
Assert.AreEqual(expectedContact, MyService.Retrieve(mockService.Object, company));
// test static method retrieve with query - FAIL
Assert.AreEqual(expectedContact, MyService.RetrieveWithQuery(mockService.Object, company));
}
Try this for your setup:
// Setup base retrieve with query
mockService.Setup(myServ => myServ.RetrieveMethod(It.Is<Query>(q=>q.QueryString == queryAccount.QueryString)).Returns(expectedContact);
Or you could overload on Equals for Query so that the Query that gets created is equal to expectedQuery.
The Moq QuickStart page has good examples of this and more which should help a lot.
I've got the following code and I need help to write a unit test for it. I'm using Moq library.
Here's the deal. I have a business class with a dependency to a repository (interface), so I can use it to save my entities to the database. My entity is basically a list of strings. The method AddAndSave, from MyBusinessClass, grab the value it receives as a parameters, put it into the list and call Save method from IRepository. Then, I clear the list of my entity. The code below show this example (I've made it simple so I can explain it here).
There's a unit test, too.
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
namespace TestesGerais
{
public class MyEntity
{
public MyEntity()
{
MyList = new List<string>();
}
public List<string> MyList { get; set; }
}
public interface IRepository
{
void Save(MyEntity entity);
}
public class MyBusinessClass
{
public IRepository Repository { get; set; }
private MyEntity _entity = new MyEntity();
public void AddAndSave(string info)
{
_entity.MyList.Add(info);
Repository.Save(_entity);
_entity.MyList.Clear(); // for some reason I need to clear it
}
}
[TestClass]
public class UnitTest10
{
[TestMethod]
public void TestMethod1()
{
var mock = new Mock<IRepository>();
MyBusinessClass b = new MyBusinessClass() { Repository = mock.Object };
b.AddAndSave("xpto");
mock.Verify(m => m.Save(It.Is<MyEntity>(x => x.MyList[0] == "xpto")), Times.Exactly(1));
}
}
}
My unit-test check if the IRepository's Save method was called with its parameter (an entity) having one element in the list, and having the value "xpto" in this element.
When I run this test, it turns red with the error message "Test method TestesGerais.UnitTest10.TestMethod1 threw exception:
System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection.
Parameter name: index".
Ok, this is caused by the list that has been cleaned. If I comment the line "_entity.MyList.Clear();", everything goes well.
My question is: how can I test this without commenting the "Clear" line in my business class, and making sure that my repository's method is called passing the specific value (entity with one element with value "xpto")?
Thanks
I've changed my unit test using the Callback feature of Moq. This way, I can setup the mock so when AddAndSave is called, the parameter it receives is saved into a variable from my unit test, and I can assert it later.
[TestMethod]
public void TestMethod1()
{
var mock = new Mock<IRepository>();
string result = string.Empty;
mock.Setup(m => m.Save(It.IsAny<MyEntity>())).Callback((MyEntity e) => { result = e.MyList[0]; });
MyBusinessClass b = new MyBusinessClass() { Repository = mock.Object };
b.AddAndSave("xpto");
Assert.AreEqual(result, "xpto");
}
You could split your method up a bit. "AddAndSave" isn't all it does. You could then just test the behaviour of the adding and saving bit in isolation.