Verificar os dados no aplicativo inserido pelo usuário ou obtido de outra maneira no sentido clássico implica o uso de apenas duas expressões no código: TRUE e FALSE. Em outra modalidade, são usadas exceções que claramente não se destinam a isso. Existe uma opção melhor?A validação é feita pelos chamados Validadores (que são apenas parte de todo o processo de validação de dados). O artigo Validação do servidor de dados do usuário fornece uma versão interessante da implementação do validador, mas existem várias nuances na forma de localização da mensagem e no próprio formato de erro .Considere o formato do erro primeiro.A abordagem proposta é para o método validador que verifica os dados para retornar uma coleção (matriz, lista, etc.) de seqüências de caracteres em vez de valores booleanos ou lançar exceções. Esse formato será mais flexível e informativo.Vou dar um exemplo em Java:import java.util.ArrayList;
import java.util.Collection;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public interface ValidateUser {
String OK = "OK:";
String FAIL= "FAIL:";
Collection<String> apply(String username, String password, String email);
default Collection<String> passwordValidate(String password){
var result = new ArrayList<String>(1);
int size = password.trim().length();
if(size < 3 || size > 20) result.add("Password error:too short value or too long name. Password must be greater or 3 characters and smaller then 20 simbols.");
return result;
}
default Collection<String> usernameValidate(String name){
var result = new ArrayList<String>(1);
int size = name.trim().length();
if(size < 3 || size > 30) result.add("Username error:too short or too long name. Name must be greater or 3 characters and smaller then 30 simbols.");
return result;
}
default Collection<String> emailValidate(String email){
var result = new ArrayList<String>(1);
String regex = "^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(email);
if (!matcher.find()) result.add("Email error:" + email + " is not valid");
return result;
}
class Default implements ValidateUser{
@Override
public Collection<String> apply(String username, String password,
String email) {
var errors = passwordValidate(password.trim());
errors.addAll(usernameValidate(username.trim()));
errors.addAll(emailValidate(email.trim()));
return errors;
}
}
}
Como você pode ver aqui, todos os métodos retornam uma coleção de strings.Exemplo de teste de unidade: @Test
void validateUserTest() {
var validate = new ValidateUser2.Default();
var result = validate.apply("aaa", "qwe", "aaa@mail.ru");
assertTrue(result.isEmpty());
result = validate.apply("aaa", "qwe", "");
assertFalse(result.isEmpty());
assertEquals(1, result.size());
result = validate.apply("aa", "qwe", "aaa@mail.ru");
assertFalse(result.isEmpty());
assertEquals(1, result.size());
result = validate.apply("aaa", "qwe", "@mail.qweqwe");
assertFalse(result.isEmpty());
assertEquals(1, result.size());
result = validate.apply("aa", "qw", "");
assertFalse(result.isEmpty());
assertEquals(3, result.size());
}
Agora considere a localização das mensagens de erro.Exemplo novamente em Java:public interface LocalizedValidation {
String OK = "OK:";
String FAIL= "FAIL:";
Collection<String> apply(String username, String password, String email, Locale locale);
default Collection<String> passwordValidate(String password, ResourceBundle bundle){
var result = new ArrayList<String>(1);
int size = password.trim().length();
if(size < 3 || size > 20) result.add(bundle.getString("password"));
return result;
}
default Collection<String> usernameValidate(String name, ResourceBundle bundle){
var result = new ArrayList<String>(1);
int size = name.trim().length();
if(size < 3 || size > 30) result.add(bundle.getString("username"));
return result;
}
default Collection<String> emailValidate(String email, ResourceBundle bundle){
var result = new ArrayList<String>(1);
String regex = "^[\\w!#$%&'*+/=?`{|}~^-]+(?:\\.[\\w!#$%&'*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\.)+[a-zA-Z]{2,6}$";
Pattern pattern = Pattern.compile(regex);
Matcher matcher = pattern.matcher(email);
if (!matcher.find()) result.add(bundle.getString("username")+email);
return result;
}
class Default implements LocalizedValidation{
@Override
public Collection<String> apply(String username, String password,
String email, Locale locale) {
ResourceBundle bundle = ResourceBundle.getBundle("errors", locale);
var errors = passwordValidate(password.trim(), bundle);
errors.addAll(usernameValidate(username.trim(), bundle));
errors.addAll(emailValidate(email.trim(), bundle));
return errors;
}
}
}
@Test
void localizedUserTest() {
var validate = new LocalizedValidation.Default();
var result = validate.apply("aaa", "qwe", "aaa@mail.ru", Locale.ENGLISH);
assertTrue(result.isEmpty());
result = validate.apply("aaa", "qwe", "", Locale.ENGLISH);
assertFalse(result.isEmpty());
assertEquals(1, result.size());
System.out.println(result.iterator().next());
result = validate.apply("aaa", "qwe", "", new Locale("ru"));
assertFalse(result.isEmpty());
assertEquals(1, result.size());
System.out.println(result.iterator().next());
}
Os arquivos de localização estão em src / main / resources.errors_ru.properties:mail=Email : Email:
username= : .
password= : .
errors.properties:mail=Email error: is not valid:
username=Username error:too short or too long name. Name must be greater or 3 characters and smaller then 30 simbols.
password=Password error:too short value or too long name. Password must be greater or 3 characters and smaller then 20 simbols.
Espero que outros programadores que escrevem em seus próprios idiomas achem essa abordagem prática e conveniente e possam aplicá-la em casa.PSUma variante do método de autenticação do usuário que retorna a implementação da interface java.util.Map.Entry <K, V> e pode conter o objeto do usuário e os dados de erro como uma string (essa abordagem também pode ser usada para evitar o retorno de Null de método quando o objeto do usuário é esperado):@Override
public Entry<String, User> auth(String username, String password, String email) {
var errors = passwordValidation.apply(password.trim());
errors.addAll(userNameValidation.apply(username.trim()));
errors.addAll(emailValidation.apply(email.trim()));
if(!errors.isEmpty()) {
return REntry.ofI(null, errors.stream().collect(Collectors.joining(";")));
}else {
try {
var passwordEncrypted = new Encrypt().apply(password.trim());
var user = new User.Default(0L, username.trim(), email.trim(), passwordEncrypted, "").create(dataSource);
return REntry.ofI(user, "user is null!");
} catch (RuntimeException e) {
return REntry.ofI(null, e.getMessage());
}
}
}
Obviamente, existe um artigo maravilhoso no hub sobre validação em Java , mas a abordagem sugerida pode ser usada longe de todos os casos e é baseada em anotações e exceções.No final do artigo, alguns links úteis sobre testes:- Antipadrões de teste de software
- Conceitos de teste automático