I am creating a custom annotation for firebase events. My inspiration comes from official android docs and other #StringDef annotations. It looks like this:
#Retention(SOURCE)
#StringDef({
EVENT_1, EVENT_2
})
#Size(max = 32) // firebase limit
public #interface UserActivityEvents {
String EVENT_1 = "ev_1";
String EVENT_2 = "really_long_event_name_that_exceeds_32_chars_limit";
...
}
The class that encapsulates the logging is defined as follows:
public class UserActivityEvent {
private #UserActivityEvents String eventName;
private UserActivityEvent() {
}
public UserActivityEvent(#UserActivityEvents String eventName) {
this.eventName = eventName;
}
...
}
The problem is that the AS lint is never triggered, no matter what I specify in the constructor, even if I do something like this:
new UserActivityEvent("asd");
My expectations for #UserActivityEvents are:
The parameter in constructor of UserActivityEvent can only be one of the strings defined in #StringDef
Event length doesn't exceed the 32 chars limit
If one of these rules are violated, the code is underlined (classic AS error highlight) and the error message is displayed when hovering over the code.
Edit: I found out that if I define a new event EVENT_3 in UserActivityEvents but don't include it in #UserActivityEvents and I try to reference it as new UserActivityEvent(UserActivityEvents.EVENT_3), the lint is properly triggered and says this: "Must be one of: EVENT_1, EVENT_2 or length must be at most 32". In case of using new UserActivityEvent("asd") it doesn't work
Related
I'm wondering if there is a clean and complete way to assert on the message attached to a thrown exception when that message was generated using String.format(). For example, a class like:
public class Car {
public static final String DRIVE_ERROR = "Can't drive while car %s is parked!";
private String name;
private boolean parked;
public Car(String name) {
this.name = name;
this.parked = true;
}
public void drive() {
if (parked) {
throw new IllegalStateException(String.format(DRIVE_ERROR, name));
}
}
}
(Sorry for the weird example, just trying to keep it as simple as possible)
Now if I were testing the car, I'd have a class like this:
public class CarTest {
#Test
public void drive_test() {
Car car = new Car("Greased Lightning");
assertThatThrownBy(() -> car.drive())
.isInstanceOf(IllegalStateException.class)
.hasMessageContaining("???");
}
}
The question is, what is the best way to assert on the message? In this example, I could separate out the declaration of the name of the car, then use String format myself to grab the static string from Car and format in the name, but that seems like a lot of extra code, and can't be easily used in a lot of instances (eg. when the item that goes in the formatted string is determined at runtime). What I'd really like to be able to do is pass the error message string to hasMessageContaining and have it ignore the "%s" placeholder and accept anything in that spot. Is there a way to do regex matching of Strings with assertJ? Or some other way of doing this cleanly?
EDIT: I'm also open to alternatives on throwing exceptions that have messages that are easier to test. One solution is just using String concatenation, like throw new Exception(STATIC_ERROR_MESSAGE + name) and then testing that the message contains the first part, but that really limits your message formatting ability and doesn't look very clean.
Exception message assertions are limited compared to regular String assertion.
What you could do is use matches or containsPattern assertions, ex:
#Test
public void test() {
// GIVEN some preconditions
// WHEN
Throwable thrown = catchThrowableOfType(() -> { throw new IllegalStateException("boom!"); },
IllegalStateException.class);
// THEN
assertThat(thrown.getMessage()).matches(".oo.")
.containsPattern("oo.");
// or even better thanks to Rolland Illig suggestion
assertThat(thrown).hasMessageMatching(".oo.");
}
Note that by using catchThrowableOfType you don't have to check that the caught exception is of the expected type anymore.
Suppose I have a class such as this :
#Builder
class AnIdentifier {
#NonNull //to indicate required to builder
private String mandatoryIdPart;
private Optional<String> optionalIdPart1;
private Optional<String> optionalIdPart2;
}
When a client creates an object using the builder generated by lombok for this class and does not set the optional parts, what are they set to?
Also does the client have to pass a value wrapped in Optional or just the value to the builder for the optional parts?
As of Lombok 1.16.16 you can do the following, to default it to Optional.empty()
#Builder.Default private Optional<String> optionalIdPart1 = Optional.empty()
see https://projectlombok.org/features/Builder
Until recently Intellij had issues with this which may have put some developers off, but they have been now been resolved
Here's what I tried out :
public static void main(String[] args) {
AnIdentifier id = AnIdentifier.builder().mandatoryIdPart("added")
.optionalIdPart1(Optional.of("abs")).build(); //line 1
id.optionalIdPart2.isPresent(); //line2
}
Firstly line2 generated a NPE so optionalIdPart2 was set to null not Optional.empty()
Secondly from line1, setting the optional value required putting the string in an Optional, lombok doesn't take care of that.
So use a constructor such as this with the annotation:
#Builder
public AnIdentifier(#NonNull String mandatoryIdPart, String optionalIdPart1, String optionalIdPart2) {
this.mandatoryIdPart = mandatoryIdPart;
this.optionalIdPart1 = Optional.ofNullable(optionalIdPart1);
this.optionalIdPart2 = Optional.ofNullable(optionalIdPart2);
}
I'm looking for a proper way to use property name inside validation messages, like {min} or {regexp}.
I've googled this question a few times now and apparently there isn't a native method for doing this.
#NotNull(message = "The property {propertyName} may not be null.")
private String property;
Has anyone experienced this before and has managed to find a solution for this?
UPDATE 1
Using a custom message interpolator should be something like:
public class CustomMessageInterpolator implements MessageInterpolator {
#Override
public String interpolate(String templateString, Context cntxt) {
return templateString.replace("{propertyName}", getPropertyName(cntxt));
}
#Override
public String interpolate(String templateString, Context cntxt, Locale locale) {
return templateString.replace("{propertyName}", getPropertyName(cntxt));
}
private String getPropertyName(Context cntxt) {
//TODO:
return "";
}
}
One solution is to use two messages and sandwich your property name between them:
#NotBlank(message = "{error.notblank.part1of2}Address Line 1{error.notblank.part2of2}")
private String addressLineOne;
Then in your message resource file:
error.notblank.part1of2=The following field must be supplied: '
error.notblank.part2of2='. Correct and resubmit.
When validation fails, it produces the message "The following field must be supplied: 'Address Line 1'. Correct and resubmit."
As another workaround, you may also pass the property name to the message text as shown below:
Add the validation message to the properties file (e.g. text.properties):
javax.validation.constraints.NotNull.message=may not be null.
Then define the annotation in the code as shown below:
#NotNull(message = "propertyName {javax.validation.constraints.NotNull.message}")
private String property;
Let's say I have a model class like this:
public class User {
#NotEmpty(message = "Email is required")
private String email;
}
I want to be able to unit test for the custom error message "Email is required"
So far, I have this Unit Test code which works, but does not check for the error message.
#Test
public void should_respond_bad_request_with_errors_when_invalid() throws Exception
{
mock().perform(post("/registration/new"))
.andExpect(status().isBadRequest())
.andExpect(view().name("registration-form"))
.andExpect(model().attributeHasFieldErrors("user", "email"));
}
Seems you can't.
But I suggest you work around through the attributeHasFieldErrorCode method.
Having the following:
#NotNull(message="{field.notnull}")
#Size(min=3, max=3, message="{field.size}")
public String getId() {
return id;
}
I have in my test methods the following (in case my data fails for Minimum or Maximum constraints, it belongs to the #Size annotation)
.andExpect(model().attributeHasFieldErrorCode("persona", "id", is("Size")))
or with
.andExpect(model().attributeHasFieldErrorCode("persona", "id", "Size"))
The method has two versions, with and without Hamcrest
Even when you use message = "Email is required" (raw/direct content) and I use message="{field.size}") the key to be used from a .properties file, our message attribute content belongs practically for a unique kind of annotation. In your case for a #NotNull and in my case for a Size.
Therefore, in some way does not matter what is the message (raw or key), the important is that the owner of that message (in this case the annotation) did its work and it has been validated through the attributeHasFieldErrorCode method.
Do not confuse with the similar methods names: attributeHasFieldErrors and attributeHasFieldErrorCode.
I have had the same issue. I have found some solution described below. The main idea is to check error code, not the error message.
BindingResult bindingResult = (BindingResult)
mock().perform(post("/registration/new")).andReturn().getModelAndView().getModelMap().get("org.springframework.validation.BindingResult.user");
assertEquals(bindingResult.getFieldError(email).getCode(), "error.code");
I have enum say ErrorCodes that
public enum ErrorCodes {
INVALID_LOGIN(100),
INVALID_PASSWORD(101),
SESSION_EXPIRED(102) ...;
private int errorCode;
private ErrorCodes(int error){
this.errorCode = error;
} //setter and getter and other codes
}
now I check my exception error codes with this error codes. I don't want to write if this do this, if this do this. How I can solve this problem (writing 10+ if blocks)
Is there any design patter to that situation ?
Thanks
Either you do it with a if-statement or a switch, or you just implement the logic in question into the ErrorCode somehow.
In an OO fashion it all depends on how you want the application or system react to the error code. Lets say you just want it to output somekind of dialog:
public doSomethingWithError() {
ErrorCodes e = getError();
// the source of error, or originator, returns the enum
switch(e) {
case ErrorCodes.INVALID_LOGIN:
prompt('Invalid Login');
case ErrorCodes.INVALID_PASSWORD:
prompt('Invalid password');
// and so on
}
}
We could instead create an ErrorHandler class that does this instead:
// We'll implement this using OO instead
public doSomethingWithError() {
ErrorHandler e = getError();
// the originator now returns an ErrorHandler object instead
e.handleMessage();
}
// We will need the following abstract class:
public abstract class ErrorHandler {
// Lets say we have a prompter class that prompts the message
private Prompter prompter = new Prompter();
public final void handleMessage() {
String message = this.getMessage();
prompter.prompt(message);
}
// This needs to be implemented in subclasses because
// handleMessage() method is using it.
public abstract String getMessage();
}
// And you'll have the following implementations, e.g.
// for invalid logins:
public final class InvalidLoginHandler() {
public final String getMessage() {
return "Invalid login";
}
}
// E.g. for invalid password:
public final class InvalidPasswordHandler() {
public final String getMessage() {
return "Invalid password";
}
}
The former solution is easy to implement, but becomes difficult to maintain as the code grows larger. The latter solution is more complex, (aka. Template Method pattern following the Open-Closed Principle) but enables you to add more methods into the ErrorHandler when you need it (such as restoring resources or whatever). You can also implement this with the Strategy pattern.
You won't get away completely with the conditional statements, but in the latter the conditional is pushed to the part of the code where the error is originated. That way you won't have double maintenance on conditional statements both at the originator and the error handling code.
EDIT:
See this answer by Michael Borgwardt and this answer by oksayt for how to implement methods on Java Enums if you want to do that instead.
Java enums are very powerful and allow per-instance method implementations:
public enum ErrorCode {
INVALID_LOGIN {
public void handleError() {
// do something
}
},
INVALID_PASSWORD {
public void handleError() {
// do something else
}
},
SESSION_EXPIRED {
public void handleError() {
// do something else again
}
};
public abstract void handleError();
}
Then you can simply call errorCode.handleError();. However, it is questionable whether an ErrorCode enum is really the right place for that logic.
As pointed out by Spoike, using polymorphism to pick the right error handling method is an option. This approach basically defers the 10+ if blocks to the JVM's virtual method lookup, by defining a class hierarchy.
But before going for a full-blown class hierarchy, also consider using enum methods. This option works well if what you plan to do in each case is fairly similar.
For example, if you want to return a different error message for each ErrorCode, you can simply do this:
// Note singular name for enum
public enum ErrorCode {
INVALID_LOGIN(100, "Your login is invalid"),
INVALID_PASSWORD(101, "Your password is invalid"),
SESSION_EXPIRED(102, "Your session has expired");
private final int code;
private final String
private ErrorCode(int code, String message){
this.code = code;
this.message = message;
}
public String getMessage() {
return message;
}
}
Then your error handling code becomes just:
ErrorCode errorCode = getErrorCode();
prompt(errorCode.getMessage());
One drawback of this approach is that if you want to add additional cases, you'll need to modify the enum itself, whereas with a class hierarchy you can add new cases without modifying existing code.
I believe the best you can do is implementing the strategy pattern. This way you won't have to change existing classes when adding new enums but will still be able to extend them. (Open-Closed-Principle).
Search for Strategy Pattern and Open Closed Principle.
You can create a map of error codes(Integer) against enum types
Edit
In this solution, once the map is prepared, you can look up an error code in the map and thus will not require if..else look ups.
E.g.
Map<Integer, ErrorCodes> errorMap = new HashMap<Integer, ErrorCodes>();
for (ErrorCodes error : ErrorCodes.values()) {
errorMap.put(error.getCode(), error);
}
Now when you want to check an error code coming from your aplpication, all you need to do is,
ErrorCodes error = errorMap.get(erro_code_from_application);
Thus removing the need for all the if..else.
You just need to set up the map in a way that adding error codes doesn't require changes in other code. Preparation of the map is one time activity and can be linked to a database, property file etc during the initialization of your application
In my opinion there is nothing wrong with ErrorCodes as enums and a switch statement to dispatch error handling. Enums and switch fit together really well.
However, maybe you find the following insteresting (kind of over-design), see an Example
or "Double dispatching" on Wikipedia.
Assumed requirements:
Error-handling should be encapsulated in an own class
Error-handling should be replacable
Type safety: Whenever an error is added, you are forced to add error handling at each error-handler implementation. It is not possible to "forget" an error in one (of maybe many) switch statments.
The code:
//Inteface for type-safe error handler
interface ErrorHandler {
void handleInvalidLoginError(InvalidLoginError error);
void handleInvalidPasswordError(InvalidLoginError error);
//One method must be added for each kind error. No chance to "forget" one.
}
//The error hierachy
public class AbstractError(Exception) {
private int code;
abstract public void handle(ErrorHandler);
}
public class InvalidLoginError(AbstractError) {
private String additionalStuff;
public void handle(ErrorHandler handler) {
handler.handleInvalidLoginError(this);
}
public String getAdditionalStuff();
}
public class InvalidPasswordError(AbstractError) {
private int code;
public void handle(ErrorHandler handler) {
handler.handleInvalidPasswordError(this);
}
}
//Test class
public class Test {
public void test() {
//Create an error handler instance.
ErrorHandler handler = new LoggingErrorHandler();
try {
doSomething();//throws AbstractError
}
catch (AbstractError e) {
e.handle(handler);
}
}
}