1

In below two methods I would like to extract common methods, the problem I have is that aisleID in one of it is optional - is it any way to put optional argument in java method ?

private void putPageUnderAisleId(String aisleId) {
        given()
                .spec(prepareApplicationJsonHeaders())
                .header(HttpHeaders.AUTHORIZATION, verifiableToken(Roles.WRITE_MERCHANDISING))
                .body(readResource(NAMED_PAGE_CONTENT_FILE, String.format("%s.html", TEST_PAGE.value()), AISLE_TEST_LOCATION))
                .put(AISLE_HTML_URL, aisleId)
                .then()
                .statusCode(200)
                .body(not(isEmptyOrNullString()))
                .body(JSON_RESPONSE_MESSAGE, not(isEmptyOrNullString()));
    }

    private void putPageUnderRootAisleId() {
        given()
                .spec(prepareApplicationJsonHeaders())
                .header(HttpHeaders.AUTHORIZATION, verifiableToken(Roles.WRITE_MERCHANDISING))
                .body(readResource(NAMED_PAGE_CONTENT_FILE, String.format("%s.html", TEST_PAGE.value()), AISLE_TEST_LOCATION))
                .put(ROOT_AISLE_HTML_URL)
                .then()
                .statusCode(200)
                .body(not(isEmptyOrNullString()))
                .body(JSON_RESPONSE_MESSAGE, not(isEmptyOrNullString()));
    }
Michal
  • 563
  • 3
  • 14
  • 24
  • Extract a 3rd method to which you pass a lambda that does the `put` part. You invoke the lambda on the builder object at the appropriate step, then do the rest of the steps. – Marko Topolnik Nov 16 '16 at 08:59
  • could you please provide ready example ... ? – Michal Nov 16 '16 at 09:00
  • `void putPage(Consumer c) { Builder b = given().spec(...)...; c.accept(b); b.then().statusCode(...)...; }` Invoke with `putPage(b -> b.put(AISLE_HTML_URL, aisleId);` and `putPage(b -> b.put(ROOT_AISLE_HTML_ID)`. – Marko Topolnik Nov 16 '16 at 09:02
  • Possible duplicate of [Java optional parameters](http://stackoverflow.com/questions/965690/java-optional-parameters) – AxelH Nov 16 '16 at 09:17

5 Answers5

3

There may be a sleaker way of handling this with Java 8 lambda expressions, but you can expose putPageUnderAisleId(String aisleId), and when the input is null, then you would treat it as no parameter being passed. Something like this:

private void putPageUnderAisleId(String aisleId) {
    RequestSpecification instance = given()
            .spec(prepareApplicationJsonHeaders())
            .header(HttpHeaders.AUTHORIZATION, verifiableToken(Roles.WRITE_MERCHANDISING))
            .body(readResource(NAMED_PAGE_CONTENT_FILE, String.format("%s.html", TEST_PAGE.value()), AISLE_TEST_LOCATION));

    if (aisleId != null) {
        instance.put(AISLE_HTML_URL, aisleId)
    } else {
        instance.put(ROOT_AISLE_HTML_URL)
    }

    instance.then()
           .statusCode(200)
           .body(not(isEmptyOrNullString()))
           .body(JSON_RESPONSE_MESSAGE, not(isEmptyOrNullString()));
}
AxelH
  • 14,325
  • 2
  • 25
  • 55
Tim Biegeleisen
  • 502,043
  • 27
  • 286
  • 360
  • 1
    At the very least you could first do `given()...body(...)`, then the ifology _just_ for the `put()` call, then continue on the common path with `then()...`. – Marko Topolnik Nov 16 '16 at 09:07
  • Yes, but what does `put()` actually return? – Tim Biegeleisen Nov 16 '16 at 09:08
  • `put` has this line inside: `return this;`. It's not the `this` you have on the caller side :) – Marko Topolnik Nov 16 '16 at 09:11
  • @TimBiegeleisen This is like a Builder return itself on each methods. – AxelH Nov 16 '16 at 09:11
  • @TimBiegeleisen, the edit was what Marko was suggesting, no need to write the first lines twice. Since We don't now the type of the instance return be `given()`, I let that empty (Object would have ben wrong...) – AxelH Nov 16 '16 at 09:22
  • 2
    Take note that instance should be type RequestSpecification, this solution works fine and thanks for that :) ! – Michal Nov 16 '16 at 09:32
2

Infortunately, you can't do this in Java.

You would have to declare both methods, but you can for example inprivate void putPageUnderRootAisleId() only call private void putPageUnderAisleId(null), and just handle the null case.

This discussion gives more information about your problem : Java optional parameters

Community
  • 1
  • 1
Davezedave
  • 35
  • 9
  • You can do it using overloading. This will not be written like C++ or other langage using Default value but the usage is the same. – AxelH Nov 16 '16 at 09:14
  • This is exactly what I meant, overloading and calling otherMethod(null) – Davezedave Nov 16 '16 at 23:32
  • I understood, even if you don't explain it with the correct term. But please, remove that first line or replace it by **you can't do it in one method in Java** ;) – AxelH Nov 17 '16 at 06:19
  • The first question was : "is it possible in Java to have an optional/default parameter ?". In Java you can't. Please give a little more attention – Davezedave Nov 17 '16 at 07:55
  • This is just a matter of HOW to do it. Ow, and check `Optional` from Java 8 too ! – AxelH Nov 17 '16 at 08:16
1

You can try with method overloading. Assuming that the put method accepts nulls aisleId, you can try the following.

 private void putPageUnderAisleId(String aisleId) {
          url=(aisleId!=null)?AISLE_HTML_URL:ROOT_AISLE_HTML_URL;

          given()
            .spec(prepareApplicationJsonHeaders())
            .header(HttpHeaders.AUTHORIZATION, verifiableToken(Roles.WRITE_MERCHANDISING))
            .body(readResource(NAMED_PAGE_CONTENT_FILE, String.format("%s.html", TEST_PAGE.value()), AISLE_TEST_LOCATION))
            .put(url, aisleId)
            .then()
            .statusCode(200)
            .body(not(isEmptyOrNullString()))
            .body(JSON_RESPONSE_MESSAGE, not(isEmptyOrNullString()));
}

private void putPageUnderRootAisleId() {

              putPageUnderAisleId(null);
}

There are several methods to handle optional parameters, see this answer.

Java optional parameters

Community
  • 1
  • 1
Massimo Petrus
  • 1,881
  • 2
  • 13
  • 26
  • Again, are you sure that you can pass a null value without risk ? – AxelH Nov 16 '16 at 09:17
  • It depends by the API of course. Which API is it? Normally this method is enough safe, but it works as principle. Do you know which API is it ? – Massimo Petrus Nov 16 '16 at 09:24
  • `Normally this method is enough safe` how could you assume that ? Since this is called `put`, I would guess this will insert values into a list, map, array... to generate some sort of parameter list. So maybe it will insert each parameters into a list. Meaning you are inserting a `null` here into the seconds parameter. Since the OP didn't insert null in his example, your assumption are incorrect until some proof ;) – AxelH Nov 16 '16 at 09:29
  • I was meaning the method overloading. As I don't know the specific API, I made some basic assumption: - the put is not the HTML PUT method, but a method which puts an URL to call in the chain - the second argument is the ID to use for the possible object to retrieve. – Massimo Petrus Nov 16 '16 at 09:39
1

Why not something like this?

private void putPageUnderRootAisleId() {
    return putPageUnderAisleId(null);
}

private void putPageUnderAisleId(String aisleId) {
    Object putKey = aisleId != null ? AISLE_HTML_URL : ROOT_AISLE_HTML_URL;
    given()
            .spec(prepareApplicationJsonHeaders())
            .header(HttpHeaders.AUTHORIZATION, verifiableToken(Roles.WRITE_MERCHANDISING))
            .body(readResource(NAMED_PAGE_CONTENT_FILE, String.format("%s.html", TEST_PAGE.value()), AISLE_TEST_LOCATION))
            .put(putKey, aisleId)
            .then()
            .statusCode(200)
            .body(not(isEmptyOrNullString()))
            .body(JSON_RESPONSE_MESSAGE, not(isEmptyOrNullString()));
}

The good part on this approach is that you only have to maintain one method. I defined the putKey as Object because I don't know wich library are you using. The only different part between both methods is the put() part. Just check the put method implementation. 99% sure the one with 1 argument calls the one with 2 providing as the second one a default value. If this default argument is null you are ready to go with mine. If it differs from null, then you only have to do an assignation like this to aisleId:

aisleId != null ? aisleId : %defaultValue;

being %defaultValue the default value the method put is using on the one argument implementation.

As pointed by @AxelH, if put receives varargs, the approach above is risky (and probably not correct), so in that case you should create an Object[] to provide these arguments:

private void putPageUnderRootAisleId() {
    return putPageUnderAisleId(null);
}

private void putPageUnderAisleId(String aisleId) {
    Object putKey = aisleId != null ? AISLE_HTML_URL : ROOT_AISLE_HTML_URL;
    Object[] putArgs = aisleId != null ? new Object[]{aisleId} : new Object[]{};

    given()
            .spec(prepareApplicationJsonHeaders())
            .header(HttpHeaders.AUTHORIZATION, verifiableToken(Roles.WRITE_MERCHANDISING))
            .body(readResource(NAMED_PAGE_CONTENT_FILE, String.format("%s.html", TEST_PAGE.value()), AISLE_TEST_LOCATION))
            .put(putKey, putArgs)
            .then()
            .statusCode(200)
            .body(not(isEmptyOrNullString()))
            .body(JSON_RESPONSE_MESSAGE, not(isEmptyOrNullString()));
}
Rubasace
  • 939
  • 8
  • 18
  • Are you certain that you know the behavior or the `put()` method such that assuming a default value won't break something? – Tim Biegeleisen Nov 16 '16 at 09:14
  • 1
    The structure is good but the implementation is risky, are you sure that passing a `null` value is the same as nothing passed ? if this is a varargs parameters, the number of parameter might be important, this need to be pointed ;) – AxelH Nov 16 '16 at 09:16
  • You need `Builder b = given().spec(...).header(...).body(...); b = aisleId != null ? b.put(AISLE_HTML_URL, aisleId) : b.put(AISLE_ROOT_HTML_URL); b.then().statusCode...` – Marko Topolnik Nov 16 '16 at 09:17
  • I'm not certain in this case, as I don't know which library is this. In most cases I've faced this problem one calls the other so the logic is not replicated all along the class (maybe it's not null and its "", but I have already explained that on my answer). Of course, this doesn't apply if we are talking of varargs, but in that case you can use an Object[] instead, and only add the aisleId in case it's not null – Rubasace Nov 16 '16 at 09:21
0

Have you considered Java 8's new Optional container as a parameter?

slashdottir
  • 7,835
  • 7
  • 55
  • 71