I have the following code which evaluates a SpEL expression using data values defined in a Map object.
// data map
Map dataMap = new HashMap();
dataMap.put("abc",1);
dataMap.put("def",2);
dataMap.put("xyz",1);
dataMap.put("qwerty",2);
// spel rule expression
String ruleExpression = "#abc+#def";
// Set evaluation context
StandardEvaluationContext stdContext = new StandardEvaluationContext();
stdContext.setVariables(map);
// Evaluate the SpEL expression
ExpressionParser parser = new SpelExpressionParser();
Object returnValue = parser.parseExpression(ruleExpression).getValue(stdContext);
// returnValue = 3 :-)
In the real world our map is populated based in a DB query result set and the 'ruleExpression' is only known at runtime. I have a new requirement to log the values defined in the 'ruleExpression' such that a string like this is generated
abc=1,def=2
A brute force approach might see us parsing the 'ruleExpression' string to identify fieldnames that start with '#' using regex but i could see how this could get messy as the complexity of the ruleExpression increases.
I'm wondering since the SpEl engine must identify the fields declared in the 'ruleExpression' during the parseExpress() phase is there a way for us to reuse this logic?
EDIT - I did come across the VariableScope private inner class on the org.springframework.expression.spel.ExpressionState class which seems to do what i want but alas the it's not accessible.
You can try overriding lookupVariable in StandardExpressionContext and adding your logging in there. Replacing your stdContext with the following will catch each variable as it is used:
StandardEvaluationContext stdContext = new StandardEvaluationContext() {
#Override
public Object lookupVariable(String name) {
Object value = super.lookupVariable(name);
//do logging here
System.out.println(name + "=" + value);
return value;
}
};
This outputs:
abc=1
def=2
This will only catch the values which are used from variables. Anything that comes from a bean or other object etc. will not go through lookupVariable. Neither will variable values which are never used (due to a conditional for instance).
Related
I'm new to programming so am probably missing something simple.
My aim is to call on a list from a result stored in a variable and pull a random entry from that List. The result 'categoryResult' is the correct List name but [random] is showing a single letter within the List name. When 'categoryResult' is replaced with hard code everything works as it should.
void getQuestion() async {
categoryResult = category[catNo];
final questions = await _firestore
.collection('questions')
.document(category[catNo])
.collection(categoryResult[random])
.document('question')
.get();
newQuestion = questions.data['$questionNumber'];
}
grails 3.3.9, + tag libs
I create a new taglib like this
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
class JavaDateTimeTagLib {
static defaultEncodeAs = [taglib:'html']
//static encodeAsForTags = [tagName: [taglib:'html'], otherTagName: [taglib:'none']]
static encodeAsForTags = [testCall: [taglib:'none']]
static namespace = "jdt" //java8 date time name space for tags
def testCall = { attrs ->
def p1 = attrs.p1
def p2 = attrs.p2
out << "p1:'$p1' with class ${p1.getClass()}"
out << "p2:'$p2' with class ${p2.getClass()}"
}
}
where i want to pass a non string variable to the attrs map.
i then setup the test like this
class JavaDateTimeTagLibSpec extends Specification implements TagLibUnitTest<JavaDateTimeTagLib> {
def setup() {
}
def cleanup() {
}
/**
* restriction params must be quoted values - esentially strings
* taglib has to take that and do any conversions in the taglib
* output by defaults is encoded html using std codec
*/
void "call displayDateTime tag "() {
given:
String result = applyTemplate('<jdt:testCall p1="p1-string" p2="$now" />' , [now:LocalDateTime.now()])
when :
println "$result "
then:
result
}
}
What i'm trying to do is pass a LocalDateTime variable to the attrs map.
if you use applyTemplate and pass p2=ldt, and test map [ldt:LocalDateTime.now()]
the test fails saying the variable must be 'quoted
[Byte array resource [test_1549319950845]:1] Attribute value must be quoted (p1="p1-string" p2=now).
if you quote the p2 variable using p2="$ldt" and test map the sname as [ldt:LocalDateTime.now()], then the test will work - However the type passed to the attrs map is GStringImpl
however reading the OCI guide oci tag lib guide
it implies on page 4 that you can pass attrs.employees as a list of domainobjects and use that in you markup
but theres no way to invoke this using the testing as everything has to be string quoted - which makes it GStringImpl
how do you pass non string variable to a taglibs attrs map from appyTemplate (.. i presume the same restiction applies in live gsp and not just the testing framework )
hah! its such a small thing
if you change the test and write this
String result = applyTemplate('<jdt:testCall p1="p1-string" p2="${now}" />' , [now:LocalDateTime.now()])
with braces round the variable key name from the map - then the actual value of the typed variable is passed into the taglib. so if you look in the attrs.p2 value it has the actual LocalDateTime instance.
now quite sure how applytemplate is working not sure, but expecting its trying to do soemthing like groovy shell evaluate the string - hence the need to use single '' like '
Hope that makes sense
blimey enough to make your head explode sometimes
import groovy.transform.EqualsAndHashCode;
#EqualsAndHashCode(includes="name")
class Activity {
public String name
public buildings = []
public rooms = [] as Set
Activity(name) {
this.name = name
}
}
thisActivity=new Activity("activity")
activityRegistry = []
// is false correct
activityRegistry.contains(thisActivity)
// add new item activity2
activityRegistry << new Activity("activity2")
// is true?????
activityRegistry.contains(thisActivity)
this code is pretty straight forward, I create an activityRegistry list, I compare empty list to object I created. naturally test fails. I create a new object on the fly using new that I insert into the list. I compare the list then to the first object created, which is not part of the list, and contains, or in passes. could someone shed some light on how? or why?
The AST "EqualsAndHashCode" only use 'properties' from the class. Properties, in groovy, are declared without a modifier ('public'), and getter/setter are automatically generated.
In your example, change public String name to String name.
See : What are 'properties' in Groovy?
In an earlier question I asked about Autofixture's CreateProxy method, a potential bug was identified.
I don't think this failing test is as a result of that, but rather my continued confusion about how the Likeness.Without(...).CreateProxy() syntax works. Consider the following failing test in which I make the original test ever so slightly more complex by creating a new instance of the object, considering its creation to be the SUT:
[Fact]
public void Equality_Behaves_As_Expected()
{
// arrange: intent -> use the fixture-created Band as Object Mother
var template = new Fixture().Create<Band>();
// act: intent -> instantiated Band *is* the SUT
var createdBand = new Band {Brass = template.Brass,
Strings = template.Brass};
// intent -> specify that .Brass should not be considered in comparison
var likeness = template.AsSource().OfLikeness<Band>().
Without(x => x.Brass).CreateProxy(); // Ignore .Brass property
// per [https://stackoverflow.com/a/15476108/533958] explicity assign
// properties to likeness
likeness.Strings = template.Strings;
likeness.Brass = "foo"; // should be ignored
// assert: intent -> check equality between created Band & template Band
// to include all members not excluded in likeness definition
likeness.Should().Be(createdBand); // Fails
likeness.ShouldBeEquivalentTo(createdBand); // Fails
Assert.True(likeness.Equals(createdBand)); // Fails
}
Here's the Band:
public class Band
{
public string Strings { get; set; }
public string Brass { get; set; }
}
My earlier question wasn't sufficiently complex to help me understand what the Source of the Likeness should be in general.
Should the source be the output of the SUT, in which case it would be compared to the template instance created by AutoFixture?
Or should the source be the template instance created by AutoFixture, in which case it would be compared to the output of the SUT?
EDIT: Corrected an error in the test
I realized that I had incorrectly assigned the template.Brass property to both the Brass and the Strings property of the new Band instance. The updated test reflects the correction with var createdBand = new Band {Brass = template.Brass, Strings = template.Strings} and all six assertions pass now.
[Fact]
public void Equality_Behaves_As_Expected()
{
// arrange: intent -> use the fixture-created Band as Object Mother
var template = new Fixture().Create<Band>();
// act: intent -> instantiated Band *is* the SUT
var createdBand = new Band {Brass = template.Brass, Strings = template.Strings};
// likeness of created
var createdLikeness = createdBand.AsSource().OfLikeness<Band>().
Without(x => x.Brass).CreateProxy(); // .Brass should not be considered in comparison
// https://stackoverflow.com/a/15476108/533958 (explicity assign properties to likeness)
createdLikeness.Strings = createdBand.Strings;
createdLikeness.Brass = "foo"; // should be ignored
// likeness of template
var templateLikeness = template.AsSource().OfLikeness<Band>()
.Without(x => x.Brass)
.CreateProxy();
templateLikeness.Strings = template.Strings;
templateLikeness.Brass = "foo";
// assert: intent -> compare created Band to template Band
createdLikeness.Should().Be(template);
createdLikeness.ShouldBeEquivalentTo(template);
Assert.True(createdLikeness.Equals(template));
templateLikeness.Should().Be(createdBand);
templateLikeness.ShouldBeEquivalentTo(createdBand);
Assert.True(templateLikeness.Equals(createdBand));
}
What you mean is:
likeness.Should().BeAssignableTo<Band>(); // Returns true.
In the example provided, the proxy generated from Likeness is a type deriving from Band, overriding Equals using the Semantic Comparison algorithm.
Using Reflection that is:
createdBand.GetType().IsAssignableFrom(likeness.GetType()) // Returns true.
Update:
The createBand and template instances are not affected by the CreateProxy method. Why they should?
With Likeness CreateProxy you basically create a Custom Equality Assertion that allows you to do:
Assert.True(likeness.Equals(createdBand)); // Passed.
Without it, the original Equality Assertion would fail:
Assert.True(template.Equals(createdBand)); // Failed.
However, the following will also fail:
Assert.True(likeness.Equals(template));
It fails because the Strings value is the one from the createdBand instance.
This behavior is expected, and you can verify it using Likeness directly:
createdBand.AsSource().OfLikeness<Band>()
.Without(x => x.Brass).ShouldEqual(template);
Output:
The provided value `Band` did not match the expected value `Band`. The following members did not match:
- Strings.
Using NUnit and NMock2 I was not able to compare what I thought were the same SqlParameters:
SqlParameter param1 = new SqlParameter("#Id", 1);
SqlParameter param2 = new SqlParameter("#Id", 1);
Assert.IsTrue(param1.Equals(param2)); // This failed
I stumbled across this problem, when trying to test an execution of a method using NMock2
[Test]
public void UpdateComments()
{
const int arbitraryId = 1;
Comment comment = new Comment();
SqlParameter idParam = new SqlParameter("#ChangeId", arbitraryId);
Expect.Once.On(mockSqlDao).Method("ExecuteNonQuery")
.With("usp_Update_Comment", idParam);
changeDao.UpdateComment(arbitraryId, comment);
mocks.VerifyAllExpectationsHaveBeenMet();
}
I received this error:
NMock2.Internal.ExpectationException: unexpected invocation of sqlDao.ExecuteNonQuery("usp_Update_Comment", )
Expected:
1 time: sqlDao.ExecuteNonQuery(equal to "usp_Update_Comment", equal to <#ChangeId>) [called 0 times]
Questions:
How do you test with NMock2 when you
expected Parameter is SqlParameter?
How do you compare equality of two SqlParameters?
Because .Equals() is using the default implementation of Equals as far as I know (which means that a SqlParameter will only "equal" another SqlParameter if they are the same object), you will need to directly interrogate the properties of the parameter to ensure the correct data is being passed.
The Has.Property call within .With allows you to check the properties of a parameter without requiring that a parameter equals some other value. Try the following:
Expect.Once.On(mockSqlDao).Method("ExecuteNonQuery")
.With("usp_Update_Comment", Has.Property("ParameterName").EqualTo("#Id") &
Has.Property("Value").EqualTo(1));