Given the following class...
public class UserAccount {
private Long id;
private String email;
private String activationCode;
private Date createDate;
}
... I need to compare the actual object with an expected object using AssertJ. However the fields id, activationCode and createDate have dynamic value which I can't hard-code into the assertion.
So the following assertion would fail:
assertThat(actualUserAccount).isEqualTo(expectedUserAccount);
Then I found the following which would ignore certain fields:
assertThat(actualUserAccount)
.usingRecursiveComparison()
.ignoringFields("id", "activationCode", "createDate")
.isEqualTo(expectedUserAccount);
But what I'm actually looking for is to assert for the objects being equal with the following special checks on certain fields:
Is there an id with any Long value?
Is there an activationCode with any String value?
Is there an createDate with any Date value?
Or is there no other way than to write one assertion for each field?
You can specify how to compare certain fields with withEqualsForFields so you could write something like:
assertThat(actualUserAccount)
.usingRecursiveComparison()
.withEqualsForFields((id1, id2) -> id1 instanceof Long && id2 instanceof Long, "id")
.isEqualTo(expectedUserAccount);
I'm checking both ids fields since there is no garantee that id1 is the actual id and id2 the expected id.
You could also write a generic method like BiPredicate<A, B> isType(T type) that returns a bipredicate checking both parameters are of the type T (my suggested signature might not work but you get the idea), that would let you write:
assertThat(actualUserAccount)
.usingRecursiveComparison()
.withEqualsForFields(isType(Long.class), "id")
.withEqualsForFields(isType(String.class), "activationCode")
.withEqualsForFields(isType(Date.class), "createDate")
.isEqualTo(expectedUserAccount);
Here's what isType looks like (I haven't tested it though):
<A, B, T extends Class<?>> BiPredicate<A, B> isType(T type) {
return (a, b) -> type.isInstance(a) && type.isInstance(b);
}
Having said that, I would probably not go that way and write additional assertions.
For reference: https://assertj.github.io/doc/#assertj-core-recursive-comparison-comparators
If you want to verify that all the fields have a value you could use hasNoNullFieldsOrProperties, while returns can be used to refine the verification of email (assuming that getters are exposed):
assertThat(actualUserAccount)
.hasNoNullFieldsOrProperties()
.returns(expectedUserAccount.getEmail(), from(UserAccount::getEmail));
If you must enforce the type of the fields, you can chain:
.extracting(UserAccount::getId, UserAccount::getEmail, UserAccount::getActivationCode, UserAccount::getCreateDate)
.hasExactlyElementsOfTypes(Long.class, String.class, String.class, Date.class);
If getters are not available, your original example is probably the only option to verify the value of email:
assertThat(actualUserAccount)
.usingRecursiveComparison()
.ignoringFields("id", "activationCode", "createDate")
.isEqualTo(expectedUserAccount);
But still you can use extracting(String...) instead of extracting(Function...) to enforce the type of the fields:
assertThat(actualUserAccount)
.extracting("id", "email", "activationCode", "createDate")
.hasExactlyElementsOfTypes(Long.class, String.class, String.class, Date.class);
However, these options are not refactoring-friendly.
Related
This is probably very easily answered, but I got a little stumped. Couldn't find a reference this this kind of question anywhere, which seems amazing.
I have a List<Map> and I want to sort it by the key provided through a variable instead of explicitly naming the key to sort by.
Here is what I have:
adminList.sort((a, b) => (b.sortField).compareTo(a.sortField));
AdminList is my List<Attendee> and a & b want my constructor (key) names ("firstName," "lastName," etc) and won't let me substitute a variable, as toString or anything I've tried. I want to use sortName, which might be assigned as "firstName" now, "lastName" later.
I figured this out. So technically, my named class was not a map, though it feels like one to this novice coder, and in order to access members by a string variable, I needed to create a toMap function within my class, call that, and reference my variable like so:
class AttendeeData {
final String firstName;
final String lastName;
AttendeeData({this.firstName, this.lastName,});
Map<String, dynamic> toMap() {
return {
'firstName':firstName,
'lastName:'lastName,};
}}
Now, I can define sortField, and filter by it. Changing it to lastName whenever I want.
String sortField = 'firstName';
adminList.sort(
(a, b) => (a.toMap()[sortField]).compareTo(b.toMap()[sortField]));
I have simplified the following example from my code and hoping there's no obvious compilation errors because of it. Lets say I have the following entities (not what i actually have, please assume I have no EF or schema issues, this is just for example):
public class Company
{
public string GroupProperty {get;set;}
public virtual ICollection<PricingForm> PricingForms {get;set;}
}
public class PricingForm
{
public decimal Cost {get;set;}
}
And I want to query like so:
IQueryable DynamicGrouping<T>(IQueryable<T> query)
{
Expression<Func<Company, decimal?>> exp = c => c.PricingForms.Sum(fr => fr.Cost);
string selector = "new (it.Key as Key, #0(it) as Value)";
IQueryable grouping = query.GroupBy("it.GroupProperty", "it").Select(selector, exp);
return grouping;
}
I get the following error when calling the groupby/select line:
System.Linq.Dynamic.ParseException: 'Argument list incompatible with lambda expression'
What type is "it" when grouped? I have tried using other expressions that assume it is an IGrouping<string, Company>, or a IQueryable<Company>, same error. I've tried just selecting "Cost" and moving the Sum() aggregate into the selector string (i.e. Sum(#0(it)) as Value) and always seem to get the same error.
I eventually tried something along the lines of:
Expression<Func<IEnumerable<Company>, decimal?>> exp = l => l.SelectMany(c => c.PricingForms).Sum(fr => fr.Cost);
However this one, I get farther but when attempting to iterate through the results I got a different error.
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities.
So, with this dynamic grouping and injecting my own select expression, what should I assume the datatype of 'it' is? Will this even work?
The type of it is IGrouping<TKey, TElement>, where TKey is dynamic based on the keySelector result type, and TElement is the element type of the input IQueryable. Luckily IGrouping<TKey, TElement> inherits (is a) IEnumerable<TElement>, so as soon as you know the input element type, you can safely base selector on IEnumerable<TElement>.
In other words, the last attempt based on Expression<Func<IEnumerable<Company>, decimal?>> is correct.
The new error you are getting is because #0(it) generates Expression.Invoke call which is not supported by EF. The easiest way to fix that is to use LINQKit Expand method:
Expression<Func<Company, decimal?>> exp = c => c.PricingForms.Sum(fr => fr.Cost);
string selector = "new (it.Key as Key, #0(it) as Value)";
IQueryable grouping = query.GroupBy("it.GroupProperty", "it").Select(selector, exp);
// This would fix the EF invocation expression error
grouping = grouping.Provider.CreateQuery(grouping.Expression.Expand());
return grouping;
I have two structures:
type GoogleAccount struct {
Id uint64
Token string
}
It represent my custom PostgreSQL object type (i created myself):
CREATE TYPE GOOGLE_ACCOUNT AS
(
id NUMERIC,
token TEXT
);
And next structure is table in DB:
type Client struct {
IdClient uint64 `gorm:"primary_key"`
Name string
PhotoUrl string
ApprovalNumber uint16
Phone string
Password string
HoursOfNotice int8
Google GoogleAccount
}
And my custom object nested in type Client and named as google. I've tried to read data by the next way:
var users model.Client
db.First(&users)
But unfortunately I can't read field google (have a default value). I don't want to create separate table with google_account, or make this structure as separated fields in client table or packed it as json (created separate entity, because this structure used not only in this table and I'm searching new ways, that get the same result, but more gracefully). The task is not to simplify the presentation of data in the table. I need to make the correct mapping of the object from postgres to the entity.
Right now I found one solution - implement Scanner to GoogleAccount. But value in the input method is []uint8. As I can suppose, []uint8 can cast to string, and after that I can parse this string. This string (that keep in db) look like (x,x) - where x - is value. Is the right way, to parse string and set value to object? Or is way to get this result by ORM?
Is the possible way, to read this data as nested structure object?
It looks like you'll want to do two things with what you have: (1) update the model so you have the right relationship binding, and (2) use the .Preload() method if you're trying to get it to associate the data on read.
Model Changes
Gorm automatically infers relationships based on the name of the attributes in your struct and the name of the referenced struct. The problem is that Google attribute of type GoogleAccount isn't associating because gorm is looking for a type Google struct.
You're also missing a foreign key on GoogleAccount. How would the ORM know which GoogleAccount to associate with which Client? You should add a ClientId to your GoogleAccount struct definition.
Also, I would change the primary keys you're using to type uint since that's what gorm defaults to (unless you have a good reason not to use it)
If I were you, I would change my struct definitions to the following:
type Client struct {
IdClient uint `gorm:"primary_key"`
Name string
PhotoUrl string
ApprovalNumber uint16
Phone string
Password string
HoursOfNotice int8
GoogleAccount GoogleAccount // Change this to `GoogleAccount`, the same name of your struct
}
type GoogleAccount struct {
Id uint
ClientId uint // Foreign key
Token string
}
For more information on this, take a look at the associations documentation here: http://gorm.io/associations.html#has-one
Preloading associations
Now that you actually have them properly related, you can .Preload() get the nested object you want:
db.Preload("GoogleAccount").First(&user)
Using .Preload() will populate the user.GoogleAccount attribute with the correctly associated GoogleAccount based on the ClientId.
For more information on this, take a look at the preloading documentation: http://gorm.io/crud.html#preloading-eager-loading
Right now I found one solution - implement Scanner to GoogleAccount. At input of the Scan method I got []uint8, that I cast to string and parse in the end. This string (that keeping in db) look like (x,x) - where x - is value. Of course, it is not correct way to achieve my goal. But I couldn't found other solution.
I highly recommended use classical binding by relationship or simple keeping these fields in table as simplest value (not as object).
But if you would like to experiment with nested object in table, you can look at my realization, maybe it would be useful for you:
type Client struct {
// many others fields
Google GoogleAccount `json:"google"`
}
type GoogleAccount struct {
Id uint64 `json:"id"`
Token string `json:"token"`
}
func (google GoogleAccount) Value() (driver.Value, error) {
return "(" + strconv.FormatUint(google.Id, 10) + "," + google.Token + ")", nil
}
func (google *GoogleAccount) Scan(value interface{}) error {
values := utils.GetValuesFromObject(value)
google.Id, _ = strconv.ParseUint(values[0], 10, 64)
google.Token = values[1]
return nil
}
I am trying to compare two jsons, expected and the API Response using Javers, as part of testing. I want the comparison to exclude the ID parameters that are dynamically generated by response.
My VO is like
public class expectedResponse{
#DiffIgnore
private String id;
private String name;
}
Both my expectedResponse- which is read from excel file and the actual response from API are deserialized into this format and then both the responses are compared.
JsonNode expectedOutput = mapper.readTree(expected.toString());
JsonNode apiResponse = mapper.readTree(actual.toString());
diff=javers.compare(expectedOutput, apiResponse);
But this comparison doesn't exclude/ignore the ID field. Any Idea how I can get it to work? I want only the ID field excluded in comparison results, diff in name should be listed.
Also question 2> I am trying to list the changes from diff
if (diff.hasChanges())
{
List<ValueChange> changes=diff.getChangesByType(ValueChange.class);
for (ValueChange change : changes)
{
logger.info(change.getPropertyName()+ "||" +change.getLeft().toString() + "||" +change.getRight().toString());
change.getPropertyName()- doesnt print the property's name but simply prints "_value" as its value.
Can you pls help in identifying what is going wrong with the code and how can I get this fixed? I am not finding much useful documentations about Javers anywhere in google. Any help is appreciated.
You should compare you domain object instead of object with JsonNode class, look that #DiffIgnore annotation is present only in your domain class and there is no connection between JsonNode and ExpectedResponse, thats why Javers doesn't know to ignore this field.
To summarise, your code should looks like this:
ExpectedResponse expectedOutput = ...
ExpectedResponse apiResponse = ...
diff=javers.compare(expectedOutput, apiResponse);
I am writing a translator which converts DSL to multiple programming language (It seems like Apache Thrift).
For example,
// an example DSL
LOG_TYPE: COMMERCE
COMMON_FIELD : session_id
KEY: buy
FIELD: item_id, transaction_id
KEY: add_to_cart
FIELD: item_id
// will be converted to Java
class Commerce {
private String session_id
private String key;
private String item_id;
private String transaction_id
// auto-created setter, getter, helper methods
...
}
It also should be translated into objective-c and javascript.
To implement it, I have to replace string
// 1. create or load code fragments
String variableDeclarationInJava = "private String {$field};";
String variableDeclarationInJavascript = "...";
String variableDeclarationInObjC = "...";
// 2. replace it
variableDeclarationInJava.replace(pattern, fieldName)
...
Replacing code fragment in String is not type safe and frustrating since it does not any information even if there are errors.
So, my question is It is possible to parse String at compile time? like Scala sqltyped library
If it is possible, I would like to know how can I achieve it.
Thanks.
As far, as I understand, it could be. Please take a look at string interpolation. You implement a custom interpolator, (like it was done for quasi quotations or in Slick).
A nice example of the thing you may want to do is here