I try to work with date fields in Freemarker.
Here is my controller methods for creating Account objects:
#GetMapping(value = "/accounts/add")
public String showAddAccount(Model model) {
Account account = new Account();
model.addAttribute("add", true);
model.addAttribute("account", account);
return "account-edit";
}
#PostMapping(value = "/accounts/add")
public String addAccount(
Model model,
#ModelAttribute("account") Account account
) {
try {
Account newAccount = accountService.save(account);
return "redirect:/accounts/" + newAccount.getId();
} catch (Exception e) {
String errorMessage = e.getMessage();
logger.error(errorMessage);
model.addAttribute("errorMessage", errorMessage);
model.addAttribute("add", true);
return "account-edit";
}
}
It is part of my Freemarker template, where I am formatting date:
<#if account.createdOn??>
<tr>
<td>Created On</td>
<td>:</td>
<td>${(account.createdOn).format('yyyy-MM-dd HH:mm:ss')}</td>
</tr>
<tr>
<td>Updated On</td>
<td>:</td>
<td>${(account.updatedOn).format('yyyy-MM-dd HH:mm:ss')}</td>
</tr>
</#if>
I have added dependency:
<dependency>
<groupId>no.api.freemarker</groupId>
<artifactId>freemarker-java8</artifactId>
<version>1.3.0</version>
</dependency>
I found this dependency to use Data/Time in Java 8 in template.
After I added config class for freemarker:
#Configuration
public class FreemarkerConfig implements BeanPostProcessor {
#Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
if (bean instanceof FreeMarkerConfigurer) {
FreeMarkerConfigurer configurer = (FreeMarkerConfigurer) bean;
configurer.getConfiguration().setObjectWrapper(new Java8ObjectWrapper(freemarker.template.Configuration.getVersion()));
}
return bean;
}
}
I am receiving an error "Column 'created_on' cannot be null" and:
could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
Related
I'm writing my first spring boot app and I'm stuck with this problem. I can't show error message to user. Object without that data is not saved in the database and that is OK. But showing error message is the problem. When I debug i get errors size = 0
This is model
#Size(min = 1, message = "Address is invalid.")
#NotNull
#Column
private String address;
Controller
#RequestMapping(value = "/create", method = RequestMethod.POST, consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String createNewBusiness(#Valid #ModelAttribute("business") Business business,
BindingResult result, Model model) {
model.addAttribute("userEmail", getUserEmail());
logger.info("/business/create:" + business.toString());
LocationResponse locationResponse = geoService.getCoords(business.getAddress());
if (locationResponse.getStatus().equals("OK")) {
business.setLatitude(locationResponse.getResults().get(0).getGeometry().getLocation().getLat());
business.setLongitude(locationResponse.getResults().get(0).getGeometry().getLocation().getLng());
business.setUserId(getUserId());
businessService.createNew(business);
model.addAttribute("business", business);
} else {
business.setAddress(null);
model.addAttribute("business", business);
}
if (result.hasErrors()) {
List<FieldError> errors = result.getFieldErrors();
for (FieldError error : errors ) {
System.out.println (error.getObjectName() + " - " + error.getDefaultMessage());
}
return "newBusiness";
}
return "business";
}
Thymeleaf
<div class="input-field left m-0 w-100">
<i class="fa fa-map-marker prefix grey-text" aria-hidden="true"></i>
<input placeholder="Address" id="inputAddress" name="address" type="text" class="validate my-0" th:required="true">
<label th:errors="*{address}" th:if="${#fields.hasErrors('address')}" >Invalid address</label>
</div>
Did you define a Validator in your #SpringBootApplication?
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
#Bean
public Validator validator() {
return new LocalValidatorFactoryBean();
}
}
I am writing unit tests for my code. Now I want to test if a value put into the form is correctly saved in the variable in the Controller. Both tests that depend on this model attribute being correct, fail. Because the model exists but stays null, this must mean I'm sending the value from my test the wrong way. How can I have my test include an entered value to test the post method correctly?
The test testPostValueInModel() fails with an AssertionError:
java.lang.AssertionError: Model attribute 'chosenTemp' does not exist
I must note that I'm pretty new to all this, so if anyone has an answer please provide some more code examples and explain what is going wrong so I can learn from my mistakes. Thank you.
Here's my test class:
#RunWith(SpringRunner.class)
#WebMvcTest(InvoerschermController.class)
#AutoConfigureMockMvc
public class InvoerschermTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testCorrectModel() {
try {
this.mockMvc.perform(get("/invoer", "20")).andExpect(status().isOk())
.andExpect(model().attributeExists("chosenTemp"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Test
public void testPost() {
try {
this.mockMvc.perform(post("/invoer", "20")).andExpect(status().isOk())
.andExpect(view().name("invoerscherm"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Test
public void testPostValueInModel() {
try {
this.mockMvc.perform(post("/invoer", "20")).andExpect(status().isOk())
.andExpect(model().attributeExists("chosenTemp"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
The Controller:
#Controller
public class InvoerschermController {
private String chosenTemp = "20";
private static PostgresDatabase database;
private static Connection connection;
// Static initializer for the database
static {
database = new PostgresDatabase();
connection = database.connectToDatabase();
}
#GetMapping("/invoer")
public String invoer(Model model) {
// int newTemp = Integer.parseInt(getChosenTemp());
chosenTemp = database.getTemperature(connection);
model.addAttribute("chosenTemp", getChosenTemp());
return "invoerscherm";
}
#PostMapping("/invoer")
public String addInputTemp(String chosenTemp, Model model) {
setChosenTemp(chosenTemp);
model.addAttribute("chosenTemp", getChosenTemp());
try {
int newTemp = Integer.parseInt(getChosenTemp());
database.setTemperature(connection, newTemp);
} catch (NumberFormatException nfe) {
System.err.println("Invalid number: " + nfe.getMessage());
}
return "invoerscherm";
}
public String getChosenTemp() {
return chosenTemp;
}
public void setChosenTemp(String chosenTemp) {
this.chosenTemp = chosenTemp;
}
}
The Thymeleaf:
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head th:include="fragments/template :: head"></head>
<head>
<title>Smart CV</title>
</head>
<body>
<nav th:replace="fragments/template :: header"></nav>
<div class="container">
<div class="hero-unit">
<h1>Temperatuur instellen</h1>
</div>
<form action="#" th:action="#{/invoer}" th:object="${invoerscherm}"
method="post">
<div class="form-group">
<label for="chosenTemp">Gewenste temperatuur:</label> <input
type="text" class="form-control" id="chosenTemp" name="chosenTemp"
autocomplete="off" th:value="${chosenTemp}" />
</div>
<button type="submit" class="btn btn-default" name="submitKnop">Stel
in</button>
</form>
</div>
<nav th:replace="fragments/template :: footer"></nav>
</body>
</html>
First of all your controller is flawed. You shouldn't keep local state (try to imagine what happens to the chosenTemp field when 3 users submit at the same time as there is only a single instance of the InvoerschermController.
Your method argument should be annotated with #RequestParam("chosenTemp") to match the form you are sending. Your test should also reflect the fact that you are sending a parameter named chosenTemp.
First your controller
#Controller
public class InvoerschermController {
private static PostgresDatabase database;
private static Connection connection;
// Static initializer for the database
static {
database = new PostgresDatabase();
connection = database.connectToDatabase();
}
#GetMapping("/invoer")
public String invoer(Model model) {
Integer chosenTemp = database.getTemperature(connection);
model.addAttribute("chosenTemp", chosenTemp);
return "invoerscherm";
}
#PostMapping("/invoer")
public String addInputTemp(#RequestParam("chosenTemp") Integer chosenTemp, Model model) {
model.addAttribute("chosenTemp", chosenTemp);
database.setTemperature(connection, chosenTemp);
return "invoerscherm";
}
}
Notice the type change from String to Integer Spring will do the type conversion for you and notice the addition of the #RequestParam. Now your test should also reflect this.
#RunWith(SpringRunner.class)
#WebMvcTest(InvoerschermController.class)
#AutoConfigureMockMvc
public class InvoerschermTest {
#Autowired
private MockMvc mockMvc;
#Test
public void testCorrectModel() {
try {
this.mockMvc.perform(get("/invoer")).andExpect(status().isOk())
.andExpect(model().attributeExists("chosenTemp"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Test
public void testPost() {
try {
this.mockMvc.perform(post("/invoer").param("chosenTemp", "20").andExpect(status().isOk())
.andExpect(view().name("invoerscherm"));
} catch (Exception e) {
e.printStackTrace();
}
}
#Test
public void testPostValueInModel() {
try {
this.mockMvc.perform(post("/invoer").param("chosenTemp", "20")).andExpect(status().isOk())
.andExpect(model().attributeExists("chosenTemp"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
Notice the addition of .param("chosenTemp", "20") to add a request parameter with that name.
Your controller is still flawed imho as it shouldn't care about the Connection that should all be encapsulated in your Database class. Although your test now probably works your actual application will still fail due to the use of Thymeleaf and form binding. The form binding expects an object under the key invoerScherm to be available and that object should have a property named chosenTemp. You are actually lacking a form object. So what your controller should actually look like.
First you need a form object:
public class InvoerScherm {
private Integer chosenTemp;
public InvoerScherm() {}
public InvoerScherm(Integer temp) { this.chosenTemp=temp;}
// Here be getters/setters
}
Then let your controller create and use it
#Controller
public class InvoerschermController {
private static PostgresDatabase database;
private static Connection connection;
// Static initializer for the database
static {
database = new PostgresDatabase();
connection = database.connectToDatabase();
}
#GetMapping("/invoer")
public String invoer(Model model) {
Integer chosenTemp = database.getTemperature(connection);
InvoerScherm invoerScherm = new InvoerScherm(chosenTemp);
model.addAttribute("invoerScherm", invoerScherm);
return "invoerscherm";
}
#PostMapping("/invoer")
public String addInputTemp(#ModelAttribute InvoerScherm invoerScherm, Model model) {
database.setTemperature(connection, invoerScherm.getChosenTemp());
return "invoerscherm";
}
}
Ofcourse now your test will fail again, but I leave that task to you.
I am learning JSF Event Handling and when I try to run some sample code, I am getting a Null Pointer Exception.
This is my index.xhtml snippet,
<h:form>
<h2>Implement valueChangeListener</h2>
<hr />
<h:panelGrid columns="2">
Selected Locale:
<h:selectOneMenu value="#{userData.selectedCountry}" onchange="submit()">
<f:valueChangeListener type="com.cyb3rh4wk.test.LocaleChangeListener" />
<f:selectItems value="#{userData.countries}" />
</h:selectOneMenu>
Country Name:
<h:outputText id="countryInterface" value="#{userData.selectedCountry}" />
</h:panelGrid>
</h:form>
UserData.java
#ManagedBean(name = "userData", eager = true)
#ApplicationScoped
public class UserData implements Serializable{
private static Map<String, String> countryMap;
private String selectedCountry = "United Kingdom";
static {
countryMap = new LinkedHashMap<String, String>();
countryMap.put("en", "United Kingdon");
countryMap.put("fr", "French");
countryMap.put("de", "German");
countryMap.put("def", "Default");
}
public String getSelectedCountry() {
return selectedCountry;
}
public void setSelectedCountry(String selectedCountry) {
this.selectedCountry = selectedCountry;
System.out.println("Locale set");
}
public Map<String, String> getCountries() {
return countryMap;
}
public void localeChanged(ValueChangeEvent event) {
selectedCountry = event.getNewValue().toString();
}
}
LocaleChangeListener.java
public class LocaleChangeListener implements ValueChangeListener {
#Override
public void processValueChange(ValueChangeEvent event) throws AbortProcessingException {
UserData userData = (UserData) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("userData");
String newLocale = event.getNewValue().toString();
if (newLocale != null)
userData.setSelectedCountry(newLocale);
else
userData.setSelectedCountry("Default");
}
}
When I run these on Glassfish Server, I get an error,
java.lang.NullPointerException
at com.cyb3rh4wk.test.LocaleChangeListener.processValueChange(LocaleChangeListener.java:25)
at com.sun.faces.facelets.tag.jsf.core.ValueChangeListenerHandler$LazyValueChangeListener.processValueChange(ValueChangeListenerHandler.java:128)
at javax.faces.event.ValueChangeEvent.processListener(ValueChangeEvent.java:134)
Can anyone help me with this ?
You are getting NullPointerException because userData is not found in the session scope.
The reason this is happening is that you put the userData in the application scope (#ApplicationScoped annotation on your managed bean) and searching it in the session scope.
Eventhough you verified that userData is null it still prints Locale set because the bean is in the application scope as described in 2. above.
So what is the solution? Either change #ApplicationScoped to #SessionScoped or access your userData by changing:
UserData userData = (UserData) FacesContext.getCurrentInstance().getExternalContext().getSessionMap().get("userData");
to
FacesContext ctx = FacesContext.getCurrentInstance();
UserData userData = (UserData)ctx.getExternalContext().getApplicationMap().get("userData");
I am getting java.sql.SQLException: Missing IN or OUT parameter at index:: 2 error when trying to access a stored proc(created in oracle) which is having client name (varchar) as input param & cursor as output param. The error is coming when i test it thru a JSP page but when I tested the stroed proc thru a Junit i am not getting the error. So I am quite confused. Please find below my stored proc & also the DAOImpl class which is having the call. I can see that the JSP page is properly passing the name from a input text box to the name input param of stored proc.
PROCEDURE sp_get_client_details_by_name (
p_client_name IN ncr.ncr_parties.full_legal_name%TYPE,
p_result_set OUT SYS_REFCURSOR)
AS
BEGIN
OPEN p_result_set FOR
SELECT np.newedge_party_id AS client_id,
np.full_legal_name AS client_name,
np.city,
nc.name residence_country_name,
np.life_cycle_status AS status,
ec_addr.addr2 AS client_address1,
ec_addr.title_dist_compl AS client_address2,
ec_addr.zip AS client_address3,
ec_addr.state AS client_address4,
ec_addr.title_compl AS client_address_tc,
ec_addr.locality_compl AS client_address_lc,
le.newedge_legal_entity_id AS legal_entity_id,
le.full_legal_name AS legal_entity_name,
le_addr.addr2 AS legal_entity_address1,
le_addr.title_dist_compl AS legal_entity_address2,
le_addr.zip AS legal_entity_address3,
le_addr.state AS legal_entity_address4,
le_addr.title_compl AS legal_entity_address_tc,
le_addr.locality_compl AS legal_entity_address_lc
FROM ncr.ncr_parties np
JOIN ncr_legal_entities le
ON np.legal_entity_key = le.legal_entity_key
JOIN ncrglobalcountryview_vw nc
ON nc.country_alias_key = np.residence_country_aliases_key
JOIN ncr_cpty_addresses ec_addr
ON ec_addr.cpty_key = np.party_key
AND ec_addr.cpty_level = 'P'
AND ec_addr.addr_type_key = 1
JOIN ncr_cpty_addresses le_addr
ON le_addr.cpty_key = le.legal_entity_key
AND le_addr.cpty_level = 'LE'
AND le_addr.addr_type_key = 1
WHERE np.full_legal_name LIKE '%' || p_client_name || '%';
EXCEPTION
WHEN OTHERS
THEN
DBMS_OUTPUT.put_line (
SQLERRM || ' backtrace: ' || DBMS_UTILITY.format_error_backtrace);
raise_application_error (
'-20000',
'Unknown exception occurred. Please contact support.' || SQLERRM);
END sp_get_client_details_by_name;
END pkg_ocr_gui;
public class ECIDDetailsDAOImpl implements ECIDDetailsDAO {
private DataSource dataSource;
private static final String SP_GET_ECID_DETAILS = "ncr.pkg_ocr_gui.sp_get_client_details_by_name";
private static final String EC_ID_NAME_PARAM = "p_client_name";
private static final String ECID_CUR_TYPES = "p_result_set";
public DataSource getDataSource() {
return dataSource;
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public List<ECIDDetails> getECIDDetails(String elementaryClientName) {
GetECIDDetailsStoredProcedure getECIDDetailsStoreProc = new GetECIDDetailsStoredProcedure(dataSource, SP_GET_ECID_DETAILS);
Map<String, Object> resultsMap = getECIDDetailsStoreProc.executeECIDetails(elementaryClientName);
List<ECIDDetails> ecidDetails = (List<ECIDDetails>) resultsMap.get(ECID_CUR_TYPES);
return ecidDetails;
}
class GetECIDDetailsStoredProcedure extends StoredProcedure {
public GetECIDDetailsStoredProcedure(DataSource dataSource, String sprocName) {
super(dataSource, sprocName);
declareParameter(new SqlParameter(EC_ID_NAME_PARAM, java.sql.Types.VARCHAR));
declareParameter(new SqlOutParameter(ECID_CUR_TYPES, OracleTypes.CURSOR, new BeanPropertyRowMapper<ECIDDetails>(ECIDDetails.class)));
compile();
}
public Map<String, Object> executeECIDetails(String elementaryClientName) {
Map <String, Object> inputs = new HashMap<String, Object>();
inputs.put(EC_ID_NAME_PARAM, elementaryClientName);
return super.execute(inputs);
}
}
}
Below is my JUnit test which is giving back proper data
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath*:test-applicationcontext.xml"})
public class ECIDFetchServiceTest {
#Autowired
ECIDDetailsDAOImpl ecIDDAO;
#Test
public void validategetECIDDetails() {
List<ECIDDetails> ecidDetails = new ArrayList<ECIDDetails>();
ecidDetails = ecIDDAO.getECIDDetails("ABN");
assertNotNull(ecidDetails);
assertTrue(ecidDetails.size() > 0);
}
}
Hi Priyesh,
I am using JSF to create the UI. Please find below the necessary codes.
JSF Code
<h:panelGrid columns="3" cellspacing="5" cellpadding="5">
<h:outputLabel value="Elementary Client Name" />
<h:inputText value="#{ecIDBean.elementaryClientName}" />
<h:commandButton value="Get EC" action="#{ecIDBean.executeEcIDList}">
</h:commandButton>
</h:panelGrid>
Managed Bean Class
public class ECIDFetchBean {
private String elementaryClientName;
private List<ECIDDetails> ecIDList;
private ECIDFetchService ecIDFetchService;
public ECIDFetchBean() {
ApplicationContext ctx = ApplicationContextProvider.getApplicationContext();
ecIDFetchService = (ECIDFetchServiceImpl)ctx.getBean("ecIDFetchService");
}
public String getElementaryClientName() {
return elementaryClientName;
}
public void setElementaryClientName(String elementaryClientName) {
this.elementaryClientName = elementaryClientName;
}
public List<ECIDDetails> getEcIDList() {
return ecIDList;
}
public void setEcIDList(List<ECIDDetails> ecIDList) {
this.ecIDList = ecIDList;
}
public void executeEcIDList() {
ecIDList = ecIDFetchService.getECIDDetails(elementaryClientName);
}
}
Service Class
public class ECIDFetchServiceImpl implements ECIDFetchService {
private ECIDDetailsDAO ecidDetailsDAO;
public List<ECIDDetails> getECIDDetails(String elementaryClientName) throws OCRReportingException {
return ecidDetailsDAO.getECIDDetails(elementaryClientName);
}
Hi Priyesh,
JSP is also pointing to same Database. I changed my DAOImpl class to call stored proc using
SimpleJdbcCall & it's working fine now from both JUnit as well as JSP.
public List<ECIDDetails> getECIDDetailsBySimpleJDBCCall(String elementaryClientName){
SimpleJdbcCall simpleJdbcCall = new SimpleJdbcCall(dataSource);
simpleJdbcCall.withCatalogName("ncr.pkg_ocr_gui").withProcedureName("sp_get_client_details_by_name")
.withoutProcedureColumnMetaDataAccess()
.declareParameters(new SqlParameter(EC_ID_NAME_PARAM, java.sql.Types.VARCHAR),
new SqlOutParameter(ECID_CUR_TYPES, OracleTypes.CURSOR, new BeanPropertyRowMapper<ECIDDetails>(ECIDDetails.class)));
MapSqlParameterSource sqlParameterSource = new MapSqlParameterSource();
sqlParameterSource.addValue(EC_ID_NAME_PARAM, elementaryClientName);
Map<String, Object> results = simpleJdbcCall.execute(sqlParameterSource);
List<ECIDDetails> ecidDetails = (List<ECIDDetails>) results.get(ECID_CUR_TYPES);
return ecidDetails;
}
I'd like to test a controller method named authenticate(), which has very simple logic: validating email and password from request and returning the result as JSON.
public class Users extends Controller {
static Form<User> userForm = Form.form(User.class);
public static Result login() {
return ok(views.html.users.login.render(userForm));
}
public static Result authenticate() {
Form<User> filledForm = userForm.bindFromRequest();
if (filledForm.hasErrors()) {
return badRequest(views.html.users.login.render(filledForm));
} else {
ObjectNode result = Json.newObject();
User u = filledForm.get();
if (User.isAuthValid(u.email, u.password))
result.put("status", "OK");
else
result.put("status", "Authentication failed");
return ok(result);
}
}
}
Following is the test code for authenticate():
#Test
public void callAuthenticate() {
Map<String, String> formData = Maps.newHashMap();
formData.put("email", "aaa#bbb.com");
formData.put("password", "password");
Result result = callAction(controllers.routes.ref.Users.authenticate(),
fakeRequest().withFormUrlEncodedBody(formData));
assertThat(status(result)).isEqualTo(Http.Status.OK);
}
But I got an error with following stacktrace:
javax.validation.ValidationException: HV000041: Call to TraversableResolver.isReachable() threw an exception.
at org.hibernate.validator.internal.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:1230)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:438)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:387)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:351)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:303)
at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:133)
at org.springframework.validation.beanvalidation.SpringValidatorAdapter.validate(SpringValidatorAdapter.java:194)
at play.data.Form.bind(Form.java:327)
at play.data.Form.bindFromRequest(Form.java:215)
at controllers.Users.authenticate(Users.java:50)
at controllers.ref.ReverseUsers$$anonfun$authenticate$1.apply(routes_reverseRouting.scala:477)
at controllers.ref.ReverseUsers$$anonfun$authenticate$1.apply(routes_reverseRouting.scala:477)
at play.core.Router$HandlerInvoker$$anon$6$$anon$2.invocation(Router.scala:164)
at play.core.j.JavaAction$$anon$1.call(JavaAction.scala:31)
at play.core.j.JavaAction$$anon$2.apply(JavaAction.scala:74)
at play.core.j.JavaAction$$anon$2.apply(JavaAction.scala:73)
at play.libs.F$Promise$PromiseActor.onReceive(F.java:420)
at akka.actor.UntypedActor$$anonfun$receive$1.applyOrElse(UntypedActor.scala:159)
at akka.actor.ActorCell.receiveMessage(ActorCell.scala:425)
at akka.actor.ActorCell.invoke(ActorCell.scala:386)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:230)
at akka.dispatch.Mailbox.run(Mailbox.scala:212)
at akka.dispatch.ForkJoinExecutorConfigurator$MailboxExecutionTask.exec(AbstractDispatcher.scala:502)
at scala.concurrent.forkjoin.ForkJoinTask.doExec(ForkJoinTask.java:262)
at scala.concurrent.forkjoin.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:975)
at scala.concurrent.forkjoin.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
at scala.concurrent.forkjoin.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)
Caused by: org.springframework.beans.InvalidPropertyException: Invalid property 'email' of bean class [models.User]: No property 'email' found
at org.springframework.beans.BeanWrapperImpl.getPropertyDescriptor(BeanWrapperImpl.java:337)
at play.db.ebean.Model._idAccessors(Model.java:47)
at play.db.ebean.Model._getId(Model.java:67)
at play.db.ebean.Model.hashCode(Model.java:208)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.buildHashCode(SingleThreadCachedTraversableResolver.java:153)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.<init>(SingleThreadCachedTraversableResolver.java:114)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver$TraversableHolder.<init>(SingleThreadCachedTraversableResolver.java:96)
at org.hibernate.validator.internal.engine.resolver.SingleThreadCachedTraversableResolver.isReachable(SingleThreadCachedTraversableResolver.java:41)
at org.hibernate.validator.internal.engine.ValidatorImpl.isValidationRequired(ValidatorImpl.java:1221)
... 26 more
When testing on the browser, it worked as expected. But only JUnit test fails with InvalidPropertyException. What's wrong with my test code?
FYI, here's the model User:
#Entity
public class User extends Model {
#Id
#Required
#NonEmpty
public String email;
public String nickname;
#Required
public String password;
public String salt;
public static Finder<String, User> find = new Finder<String, User>(
String.class, User.class);
public static User findByEmail(String email) {
return find.where().eq("email", email).findUnique();
}
public static boolean isAuthValid(String email, String password) {
User user = findByEmail(email);
if (user == null)
return false;
return user.isValidPassword(password);
}
public boolean isValidPassword(String password) {
return this.password.equals(DigestUtils.md5Hex(password + this.salt));
}
}
Thanks for any advices/corrections.
You need to run the test inside a "Fake" application, so the binding can work. So, you're Test will look like this:
#Test
public void callAuthenticate() {
running(fakeApplication(), new Runnable() {
public void run() {
Map<String, String> formData = Maps.newHashMap();
formData.put("email", "aaa#bbb.com");
formData.put("password", "password");
Result result = callAction(controllers.routes.ref.Users.authenticate(),
fakeRequest().withFormUrlEncodedBody(formData));
assertThat(status(result)).isEqualTo(Http.Status.OK);
}
}
}