How to use power of varargs while defining step definitions in cucumber java bindings. I have below step
Given I have following product: prod1, prod2, prod3
My Step definition
#Given("^I have following product [(exhaustive list of products or pattern of the product names)]$")
public void stepDef(String...args)
{
//Process varargs array "args" in here
}
I know workaround can be to except the complete string after colon and then in the code break the string using split(",") and dump it into array. But i just want to know if cucumber on its own supports varargs pattern.
TIA!!!
I dont know if varargs are supported in cucumber, but maybe you can archieve your goal with direct list matchings?
You can define Example Lists in the Feature files in Cucumber
You can define them at the end of a Step:
#Given i want a list
|Entry1|
|Entry2|
Or inline:
#Given i want a list: Entry1, Entry2
Then you can have glue code like:
#Given(^i want a list$)
public void i_want_a_list(List<String> entries) {
//do stuff
}
#Given(^i want a list: (.*)$)
public void i_want_a_list(List<String> entries){
//Do something with the list
}
you can find more info here: https://cukes.info/docs/reference/jvm#step-definitions
If your steps like below-
Given I have following product
|prod1|
|prod2|
|prod3|
Then step definition becomes-
#Given("^I have following product$")
public void i_have_following_product(DataTable dt) throws Exception
{
List<List<String>> outerList = dt.rows();
for(List<String> innerList : outerList)
{
System.out.println(innerLlist.get(0));
}
}
Related
I'm trying to validate some properties of my response as shown in the rest assured tutorial.
The problem is that, when testing properties inside an array, I can verify, as in the example, that they appear, but not that they're matched to other properties of the element as they should.
To clarify, let's say I have the response from the tutorial (added "prize")
{
"lotto":{
"lottoId":5,
"winning-numbers":[2,45,34,23,7,5,3],
"winners":[{
"winnerId":23,
"prize":5000,
"numbers":[2,45,34,23,3,5]
},{
"winnerId":54,
"prize":100000,
"numbers":[52,3,12,11,18,22]
}]
}
}
I can validate that the winnerIds as 23, and 54
expect().
body("lotto.lottoId", equalTo(5)).
body("lotto.winners.winnderId", hasItems(23, 54)).
when().
get("/lotto");
and I could validate that the prizes are 500 and 100000, but I can't validate that the winnerId=23 has a prize=500 and the winnerId=54 a prize=100000. The response would show the winnerId=23 with a prize=100000 and the tests would pass.
I can't use contains() because the elements in the array can come in any order, so I need to user containsInAnyOrder().
As far as I know, Rest-Assured only allows to verify straight forward value verification. For conditional verification, you have to use jsonpath instead:
$.lotto.winners.[?(#.winnerId==23)].prize
Above jsonpath searches for winners array under lotto and selects the array item which has winnerId==23 and then retrieves the prize;
expect().
body("$.lotto.winners.[?(#.winnerId==23)].prize", equalTo(5000)).
when().
get("/lotto");
There are other posts in SO that you can referenc are here and here
Try the expression in this link
JsonPath expression syntax can be found here.
Using hamcrest, note to take, one item in the winners list is considered as a map.
#Test
void test() {
expect()
.body("lotto.lottoId", equalTo(5))
.body("lotto.winners.", allOf(
hasWinner(23, 5000),
hasWinner(54, 100000)
))
.when()
.get("/lotto");
}
private Matcher<Iterable<? super Map<? extends String, ? extends Integer>>> hasWinner(int winnerId, int prize) {
return hasItem(allOf(
hasEntry(equalTo("winnerId"), equalTo(winnerId)),
hasEntry(equalTo("prize"), equalTo(prize))
));
}
We have some code like:
public class ErrorCodeUtil {
public static void handleErrorCode(String errorCode) {
if (errorCode.equals("1")) {
handleErrorCode1();
} else if (errorCode.equals("2")) {
handleErrorCode2();
} else if (errorCode.equals("3")) {
handleErrorCode3();
} else {
handleErrorCodeByDefault(errorCode);
}
}
public static void logByErrorCode(String errorCode) {
if (errorCode.equals("1")) {
logErrorCode1();
} else if (errorCode.equals("2")) {
logErrorCode2();
} else if (errorCode.equals("3")) {
logErrorCode3();
} else {
logErrorCodeByDefault(errorCode);
}
}
//... a lot of method about error code
}
As you see, we have a Util to handle all things about ErrorCode, and when we want to add a special logic to an error code, we have to change many method of that utils class.
As expected, the value of error code varies in large range(possibly "112345" or "error_code_001"). So what design pattern is proper for that case?
I would implement a decision table.
The table would consist of a set of mappings between one or more Predicates as key and Function as a value. If a Predicate condition is met, then the corresponding Function is executed. If no Predicate condition is met, then a default Function should be executed. This can (easily) replace the humongous "if-else" statement and should be easier for maintenance.
How a Predicate should look like? It should take a String (in your case) and should return a boolean indicating whether a condition is met or no:
interface Predicate {
public boolean test(String x);
}
In the decision table, you'd add (anonymous) implementations of this interface as keys.
Hint: If you are already on Java8, even better, there's a built-in Predicate<T> interface. But if you're not, then you can introduce a Predicate interface of your own. :-)
The Function for the decision table's values will be a similar interface. It may (or may not) use an input parameters and should return void. In Java8 this is called a Consumer, however in my example I'll stick to the Function naming:
interface Function<T> {
void apply(T t);
}
By constructing pairs between Predicate as a key and Function<ErrorCodeUtil> as a value, we'll populate the decision table. When a Predicate condition is met, then we'll invoke the corresponding Function's .apply() method:
The decision table itself can be a simple Map<Predicate, Function<ErrorCodeUtil>>:
Map<Predicate, Function<ErrorCodeUtil>> decisionTable = new HashMap<>();
and you should populate it at construction time or whenever you wish (just before the handleErrorCode() method logic):
Predicate equalsOne = new Predicate() {
public void test(String x) {
return "1".equals(x);
}
};
Function<ErrorCodeUtil> actionOne = new Function<ErrorCodeUtil>() {
public void apply(ErrorCodeUtil t) {
t.handleErrorCode1();
}
}
decisionTable.put(equalsOne, actionOne);
and so for the other "condition-action" pairs, including the default action (i.e. the last else statement) for which the Predicate will always return true.
Note that in Java8, those anonymous classes can be significantly reduced by just using lambdas.
Finally, your "if-elseif" statements would be re-factored to a simple loop:
for (Map.Entry<Predicate, Function<ErrorCodeUtil>> entry: decisionTable.entrySet()){
Predicate condition = entry.getKey();
Function<ErrorCodeUtil> action = entry.getValue();
if (condition.test(errorCode)) {
action.apply(this);
}
}
So, everytime you add a new condition, you won't have to touch the handleErrorCode(String error) method, but you'll have to just introduce a new (anonymous) implementation of Predicate and Function and .put() it into the decision table.
I'd use Enum in that case.
public enum ErrorCodeEnum {
1 {
#Override
public void handleErrorCode() {
//doSomething
}
},
2 {
#Override
public void handleErrorCode() {
//doSomething
}
};
public abstract void handleErrorCode();
}
Then, having the error code in hands...
ErrorCodeEnum.valueOf("1").handleErrorCode();
PS: This is what I'd use to replace if-else statement, as you asked. But I'd use a Logger API for that specific problem (seems like you're logging erros).
You can keep all errorcodes in a list in one class. And check if list contains errorcode or not.
So this will reduce your if...else logic.
You have written different methods to handle error codes like handleErrorCode1(), handleErrorCode2() etc. Now if list contains desired error code then you can invoke these methods through java reflection.
regarding logging of errors, if all that is required is matching a code with a message, then a text file with mapping of codes to messages is the right way. the text file may be properties:
1=Item not Found
2=Item not valid
that can be loaded to a java.util.Properties instance, it may be xml that can be loaded into DOM or HashMap
<errors>
<error>
<code>1</code>
<msg>Item not Found</msg>
</error>
<error>
<code>2</code>
<msg>Item not Valid</msg>
</error>
<errors>
one advantage of this approach is that it can be made to support i18n if you specify language code in the file name and then get user language code from your client
I want to iterate through values of KV pCollection on perKey basis. I used below code to combine using custom class,
PCollection<KV<String, String>> combinesAttributes =
valExtract.get(extAttUsers).apply(Combine.<String, String>perKey(
new CombineAttributes()));
And below is my custom combine class,
public static class CombineAttributes implements SerializableFunction<Iterable<String>, String> {
#Override
public String apply(Iterable<String> input) {...}..}
This was working fine for small inputs but for large inputs the combine was not as expected. The output had combined only few values for a key, others were missing. I was assuming that the output had only combined data from one node.
The documentation in https://cloud.google.com/dataflow/model/combine mentions to use CombineFn in order to combine full collection-of-values per key in all nodes.
But when I changed the custom combine function as below, I am getting following error,
incompatible types: CombineAttributes cannot be converted to com.google.cloud.dataflow.sdk.transforms.SerializableFunction<java.lang.Iterable<java.lang.String>,java.lang.String>
Combine function
public static class CombineAttributes extends CombineFn<Iterable<String>, CombineAttributes.Accum, String> {
public static class Accum {
List<String> inputList = new ArrayList<String>();
}
public Accum createAccumulator() { return new Accum(); }
public Accum addInput(Accum accum, Iterable<String> input) {
for (String item : input) {
accum.inputList.add(item);
}
return accum;
}
public Accum mergeAccumulators(Iterable<Accum> accums) {
Accum merged = createAccumulator();
for (Accum accum : accums) {
for (String item : accum.inputList) {
merged.inputList.add(item);
}
}
return merged;
}
public String extractOutput(Accum accum) {
return "";
}
}
There was no sample code available for combine perKey extending CombineFn. Please let me know what is wrong with the code above.
If you just want to iterate through all the values you can use GroupByKey to turn a PCollection<KV<K, V>> into PCollection<KV<K, Iterable<V>>. Then you can write a DoFn that processes each element of that, and inside iterate over the Iterable<V>.
Note that you'll only receive all values associated with a key in the same window. If you're using the default global window, that will be all values.
Combine and CombineFn are most useful when you want to combine all the values into a smaller output. For instance, if you want to take the sum or mean of all the values it will be more efficient to do so using Sum.perKey() or Mean.perKey(). The efficiency comes from being able to pass around (and merge) accumulators. In the case of Sum, this corresponds to a partial sum.
As an example, say the pipeline runs on two machines. The first machine processes KV<user1, attr1a>, KV<user1, attr1b>, KV<user2, attr2a> and the second machine processes KV<user1, attr1c>, KV<user2, attr2b>.
The CombineAttributes (either way it was implemented) would first be invoked on each machine. So it could combine [attr1a, attr1b] into a single string or accumulator (say attr1a+attr1b). Then it would run on the other machine to combine [attr1c] to attr1c. Then it would merge all of these partial results to get a final accumulator -- attr1a+attr1b+attr1c. In the case of the original implementation, that would be the final answer. In the latter, extractOutput would be called on this accumulator.
Is there analog of everyItem() from Hamcrest in AssertJ?
I have a list of emails and need to do Assertion to check that each email contains substring "alex". Currently the only way I can do it with AssertJ is as follows:
List<String> actual = Arrays.asList("alex#gmail.com", "alex1#gmail.com", "ale2#hotmail.com", "bred#gmail.com");
SoftAssertions softly = new SoftAssertions();
for(String email: actual ) {
softly.assertThat(email).contains("alex");
}
softly.assertAll();
Can be done without Soft Assertions there as well, but I'd prefer to check all the item of the list.
Is there any more compact way to do so? To be specific, is there a way in AssertJ to check each item of the list to match a substring?
In Hamcrest I can do it in one line:
assertThat(actual, everyItem(containsString("alex")));
But in AssertJ looks like in any way I have to manually iterate through the list.
Assertj 3.6.0 introduced the allSatisfy assertion, which allows you to perform scoped assertions on each element of the iterable.
Therefore you could do what you want with
assertThat(actual).allSatisfy(elem -> assertThat(elem).contains("alex"));
I found 2 solutions:
1) use java 8
actual.forEach( val -> softly.assertThat(val).contains("alex"));
2) make an utility class
public class AssertUtils {
public static Condition<String> ContainsCondition(String val) {
return new Condition<String>() {
#Override
public boolean matches(String value) {
return value.contains(val);
}
};
}
}
and use it:
softly.assertThat(actual).are(AssertUtils.ContainsCondition("alex"));
You can build AssertJ condition with predicate and use are/have assertion:
#Test
public void condition_built_with_predicate_example() {
Condition<String> fairyTale = new Condition<String>(s -> s.startsWith("Once upon a time"), "a %s tale", "fairy");
String littleRedCap = "Once upon a time there was a dear little girl ...";
String cindirella = "Once upon a time there was a ...";
assertThat(asList(littleRedCap, cindirella)).are(fairyTale);
}
Edit: As pointed by Dan I would now use allSatisfy.
I prefer to use this form of allMatch as follow:
assertThat(movies).extracting("title").allMatch(s -> s.toString().contains("the"));
I just rely on Java 8 stream functionality for that kind of stuff:
assertThat(actual.stream().allMatch(s -> s.contains("alex"))).isTrue();
I'm sorting like this:
RealmResults<Show> shows = realm.where(Show.class).findAll();
shows.sort("venueTitle", RealmResults.SORT_ORDER_ASCENDING);
How can I sort by multiple properties? Adding another sort line just resets the order of the results entirely.
Looks like they just added this in 0.77. I was using 0.76. Here's the Github issue:
https://github.com/realm/realm-java/issues/648
and here's the API reference:
http://realm.io/docs/java/0.77.0/api/
public void sort(java.lang.String[] fieldNames,
boolean[] sortAscending)
try below code
public RealmResults getSortedList(Class aClass) {
String []fieldNames={"field1","field2"};
Sort sort[]={Sort.ASCENDING,Sort.ASCENDING};
return realm.where(YourClass.class).findAllSorted(fieldNames,sort);
}