I'm trying to walk through this guide:
http://www.playframework.com/documentation/2.2.x/JavaGuide2 , but it lacks some explanations which make me lost. More preciesly:
a)at the end of "Starting with the User class" paragraph, I should have got error. I didn't. I thought "whatever" and moved along, which, in retrospect might have been a mistake.
b) I progressed to the "first test" , but it did not write where am I supposed to put my test. So, I put it in ApplicationTest.java. It failed my tests however, saying ebean was not defined. So, after googling a bit I tried to add ebean.default="models.*" in application.conf. It worked, but now I have
[error] Test ApplicationTest.createAndRetrieveUser failed:
javax.persistence.PersistenceException:
java.sql.SQLException: Attempting to obtain a connection from a pool
that has already been shutdown.
I don't understand what's wrong.
my application test
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.JsonNode;
import models.User;
import org.junit.*;
import play.mvc.*;
import play.test.*;
import play.data.DynamicForm;
import play.data.validation.ValidationError;
import play.data.validation.Constraints.RequiredValidator;
import play.i18n.Lang;
import play.libs.F;
import play.libs.F.*;
import static play.test.Helpers.*;
import static org.fest.assertions.Assertions.*;
import static org.junit.Assert.*;
import play.libs.*;
import com.avaje.ebean.Ebean;
public class ApplicationTest {
#Test
public void simpleCheck() {
int a = 1 + 1;
assertThat(a).isEqualTo(2);
}
#Test
public void renderTemplate() {
Content html = views.html.index.render("Your new application is ready.");
assertThat(contentType(html)).isEqualTo("text/html");
assertThat(contentAsString(html)).contains("Your new application is ready.");
}
#Test
public void createAndRetrieveUser() {
new User("bob#gmail.com", "Bob", "secret").save();
User bob = User.find.where().eq("email", "bob#gmail.com").findUnique();
assertNotNull(bob);
assertEquals("Bob", bob.name);
}
}
User class
package models;
import javax.persistence.*;
import play.db.ebean.*;
import com.avaje.ebean.*;
#Entity
public class User extends Model {
#Id
public String email;
public String name;
public String password;
public User(String email, String name, String password) {
this.email = email;
this.name = name;
this.password = password;
}
public static Finder<String,User> find = new Finder<String,User>(
String.class, User.class
);
}
application.conf
#tried with 'db.*' uncommented as well as with commented
db.default.driver=org.h2.Driver
db.default.url="jdbc:h2:mem:play"
db.default.user=sa
db.default.password=""
db.default.jndiName=DefaultDS
# end of 'db.*'
ebean.default="models.*"
# Root logger:
logger.root=ERROR
# Logger used by the framework:
logger.play=INFO
# Logger provided to your application:
logger.application=DEBUG
The test createAndRetrieveUser() according to the tutorial should be in test/models/ModelsTest.java. You put it in ApplicationTest.java.
Related
hi i am learning Java and Spring Framework, i don't even consider myself beginner. The other day i found great study material "Spring-Boot Masterclass". I am learning from this source, first part was about making simple REST API CRUD application with custom methods, custom exceptions, by using MySQL database. I've learned a lot from this source, and everything went smooth, i understood everything till now, i am at testing repository, service and controller layers. I managed to create tests for CRUD methods + few custom methods for all 3 layers, and all tests passed with green result in Eclipse, i just stopped at void deleteMinisterstvo() method. Question is in Controller Layer above mentioned method in comment, but i'll share it here too, because i am getting lost:
HOW is this working, when i create database record with id=15, then delete ID=4 from endPoint for ID= 55, and TEST passes? Which one is being deleted then?
Maybe i did not even write method as it should be written, so i would like to ask you, experienced developers on your opinion, idea, maybe explanation. Thanks
*Note1: I started this thread with Hi, i... but it somehow did not save, so i tried to edit and even edit does not give Hi there. Sorry for that.
*Note2: I removed all other methods (commented them out) from Repository, Service and Controller layers for focusing only at deleteMethod. I thought, that this test should not pass, but throw error:
Project
MinisterstvoRepository
package com.Ministerstvo.Repository;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import com.Ministerstvo.Model.Ministerstvo;
#Repository
public interface MinisterstvoRepository extends JpaRepository<Ministerstvo, Long>{
}
MinisterstvoService
package com.Ministerstvo.Service;
import java.util.List;
import com.Ministerstvo.Exceptions.MinisterstvoNotFoundException;
import com.Ministerstvo.Model.Ministerstvo;
public interface MinisterstvoService {
void odstranMinisterstvo(Long id) throws MinisterstvoNotFoundException;
}
MinisterstvoServiceImpl
package com.Ministerstvo.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.Ministerstvo.Controller.MinisterstvoController;
import com.Ministerstvo.Exceptions.MinisterstvoNotFoundException;
import com.Ministerstvo.Model.Ministerstvo;
import com.Ministerstvo.Repository.MinisterstvoRepository;
import lombok.AllArgsConstructor;
import lombok.Data;
#Service
#Data
#AllArgsConstructor
public class MinisterstvoServiceImpl implements MinisterstvoService {
#Autowired
private final MinisterstvoRepository ministerstvoRepository;
private final Logger LOGGER = LoggerFactory.getLogger(MinisterstvoController.class);
#Override
public void odstranMinisterstvo(Long id) throws MinisterstvoNotFoundException {
LOGGER.info("Metoda odstranMinisterstvo() v MinisterstvoServiceImpl");
Optional<Ministerstvo> ministerstvo = ministerstvoRepository.findById(id);
if(!ministerstvo.isPresent()) {
throw new MinisterstvoNotFoundException("Ministerstvo so zadanym ID neexistuje...");
}
ministerstvoRepository.deleteById(id);
}
}
MinisterstvoController
package com.Ministerstvo.Controller;
import java.util.List;
import javax.validation.Valid;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
import com.Ministerstvo.Exceptions.MinisterstvoNotFoundException;
import com.Ministerstvo.Model.Ministerstvo;
import com.Ministerstvo.Service.MinisterstvoService;
import lombok.AllArgsConstructor;
import lombok.Data;
#RestController
#Data
#AllArgsConstructor
public class MinisterstvoController {
#Autowired
private final MinisterstvoService ministerstvoService;
private final Logger LOGGER = LoggerFactory.getLogger(MinisterstvoController.class);
// API - DELETE
#DeleteMapping("/ministerstva/{id}")
public ResponseEntity<String> odstranMinisterstvo(#PathVariable("id") Long id) throws MinisterstvoNotFoundException {
LOGGER.info("Metoda odstranMinisterstvo() v MinisterstvoController");
ministerstvoService.odstranMinisterstvo(id);
return new ResponseEntity<String>("Uspesne odstranene ministerstvo", HttpStatus.OK);
}
}
MinisterstvoControllerTest
package com.Ministerstvo.Controller;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.List;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import com.Ministerstvo.Model.Ministerstvo;
import com.Ministerstvo.Service.MinisterstvoService;
import com.fasterxml.jackson.databind.ObjectMapper;
#WebMvcTest
#ExtendWith(MockitoExtension.class)
class MinisterstvoControllerTest {
#Autowired
private MockMvc mockMvc;
#MockBean
private MinisterstvoService ministerstvoService;
Ministerstvo ministerstvo;
List<Ministerstvo> zoznamMinisterstiev;
#BeforeEach
void setUp() throws Exception {
ministerstvo = Ministerstvo.builder()
.ministerstvoId(6L)
.ministerstvoName("MinisterstvoKultiry")
.ministerstvoEmail("kultura#gmail.com")
.ministerstvoPocetZamestnancov(5)
.build();
}
#AfterEach
void tearDown()
{
ministerstvo = null;
}
// DELETE TEST
// HOW IS THIS WORKING, WHEN I CREATE DB RECORD WITH ID 15, then delete ID 4 from endPoint for ID 55, and TEST passes?
#Test
void deleteMinisterstvo() throws Exception
{
Ministerstvo ministerstvoNadstranenie = Ministerstvo.builder()
.ministerstvoId(15L)
.ministerstvoName("Ministerstvo of Nothing")
.ministerstvoEmail("nothing#gmail.com")
.ministerstvoPocetZamestnancov(789)
.build();
doNothing().when(ministerstvoService).odstranMinisterstvo(4L);
// same result as from doNothing() above //doNothing().when(ministerstvoService).odstranMinisterstvo(ministerstvo.getMinisterstvoId());
// same result as from mockMvc.perform() below
//mockMvc.perform(delete("/ministerstva/55")
mockMvc.perform(delete("/ministerstva/"+ ministerstvoNadstranenie.getMinisterstvoId().toString())
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andDo(print());
}
}
If i forgot to provide more details, i will add them based on instructions. I am just curious, if test for delete method is working properly, would be great to know the way, why test passes with different data.
Thanks for reading,
have a nice day
I've been going round and round on this problem for a couple of weeks. So, there's this project where I should get all entries from a SpringBoot Backend (RestAPI) from MongoDB repository.
How my Backend looks like
My lecturers have given us a database in MongoDB to work with. We are not allowed to change anything. Only reading from it is allowed. To simplify the problem, let's just say there are two columns. On one column, there are multiple entries with multiple data types.
This is how the table would look like in MongoDB Compass:
_id (ObjectID) | Number (Mixed)
60ab1 | 104 // int
60ab2 | 103 // int
60ab3 | "102,3" // string, this is where the problem begins
Here's the class model Example.java
package com.project.api.model;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
import java.util.Date;
import lombok.Getter;
import lombok.Setter;
#Getter
#Setter
#Document
public class Example {
#Id
private String _id;
private Integer Number; //This is where the problem begins
}
Here's the Repository ExampleRepository.java
package com.project.api.repository;
import com.project.api.model.Example;
import org.json.JSONObject;
import org.springframework.data.mongodb.repository.MongoRepository;
import org.springframework.data.mongodb.repository.Query;
import org.springframework.stereotype.Repository;
import java.util.List;
import java.util.Optional;
#Repository
public interface ExampleRepository extends MongoRepository<Example,String>{
}
Here's the Service ExampleService.java
package com.project.api.service;
import ch.qos.logback.core.encoder.EchoEncoder;
import com.project.api.model.Example;
import com.project.api.repository.ExampleRepository;
import com.project.api.exception.EntityNotFoundException;
import com.fasterxml.jackson.databind.util.JSONPObject;
import org.json.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.mapping.Field;
import org.springframework.data.mongodb.core.query.UntypedExampleMatcher;
import org.springframework.stereotype.Service;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.MongoTemplate;
import lombok.RequiredArgsConstructor;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.*;
#Service
#RequiredArgsConstructor
public class ExampleService {
#Autowired
private final ExampleRepository exampleRepository;
private int i;
String myString;
public List<Example> getAllExample()
{
return exampleRepository.findAll();
}
}
And here's the Controller ExampleController.java
package com.project.api.controller;
import com.project.api.model.Example;
import com.project.api.service.ExampleService;
import com.fasterxml.jackson.databind.util.JSONPObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.json.JSONObject;
import lombok.RequiredArgsConstructor;
#RestController
#RequestMapping(value = "/api")
#RequiredArgsConstructor
public class ExampleController {
#Autowired
private final ExampleService exampleService;
#GetMapping("/all")
public List<Example> getAllExample(#RequestParam(required = false) String ORT)
{
return exampleService.getAllExample();
}
}
The Problem
The build and the connection to MongoDB was ok, but when I tried to test the API in localhost:8080/api/all, there is an error in the terminal:
java.lang.NumberFormatException: For input string: "102,3"
...
Based on my educated guess, it should be caused by a contradiction between the int type on the model and the string type on the database entry. Again, we are not allowed to modify the entries of database
Question
Are there any way that I could extract entries from MongoDB with different data types on a property with Spring Boot?
Another Paraphrase of the question: are there any way to extract the database entries in MongoDB as raw JSON?
Thank you :)
you can replace the data type from Integer to String and add new method to get list Integer to your entity (convert String to Integer):
public List<Interger> getListNumber(){
return Arrays.stream(this.Number.split(",")).map(number -> Integer.valueOf(number)).collect(Collectors.toList());
}
And please reading java code conventions
I wanted to write unit test for my addTask method with mockito.
Here is the class that contains this method.
package controller;
import model.Task;
import model.User;
import repository.TaskActions;
import repository.UserActions;
import java.sql.SQLException;
import java.util.List;
public class ToDoEngine {
private TaskActions taskActions;
private UserActions userActions;
private User connectedUser;
public ToDoEngine(UserActions userStorage, TaskActions taskStorage) {
this.taskActions = taskStorage;
this.userActions = userStorage;
}
public boolean signIn(String username, String password) throws SQLException {
connectedUser = new User(username, password);
if (!userActions.signIn(connectedUser)) {
return false;
}
connectedUser.setID(retrieveConnectedUserID(connectedUser));
return true;
}
private int retrieveConnectedUserID(User connectedUser) throws SQLException {
return userActions.retrieveUserID(connectedUser);
}
public void addTask(String taskName) throws SQLException {
taskActions.addTask(new Task(taskName), connectedUser);
}
}
Here are my attempts. Unfortunately, I've got error. Below, I am gonna present you stacktrace:
package controller;
import model.Task;
import model.User;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import repository.TaskActions;
import repository.UserActions;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
public class ToDoEngineTest {
#Mock
TaskActions taskActionsMock;
#Mock
UserActions userActionsMock;
private ToDoEngine toDoEngine;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
toDoEngine = new ToDoEngine(userActionsMock, taskActionsMock);
}
#Test
public void addTask() throws SQLException {
Task taskName = new Task("wash");
User user = new User("admin","123");
toDoEngine.addTask("wash");
verify(taskActionsMock).addTask(taskName,user);
}
}
stacktrace:
Argument(s) are different! Wanted:
taskActionsMock.addTask(
Task(taskName=wash),
model.User#1b71f500
);
-> at controller.ToDoEngineTest.addTask(ToDoEngineTest.java:68)
Actual invocation has different arguments:
taskActionsMock.addTask(
Task(taskName=wash),
null
);
-> at controller.ToDoEngine.addTask(ToDoEngine.java:40)
Comparison Failure: <Click to see difference>
Argument(s) are different! Wanted:
taskActionsMock.addTask(
Task(taskName=wash),
model.User#1b71f500
);
-> at controller.ToDoEngineTest.addTask(ToDoEngineTest.java:68)
Actual invocation has different arguments:
taskActionsMock.addTask(
Task(taskName=wash),
null
);
-> at controller.ToDoEngine.addTask(ToDoEngine.java:40)
at controller.ToDoEngineTest.addTask(ToDoEngineTest.java:68)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
...
User and Task class contains hashCode and Equals method.
Task
package model;
import lombok.*;
#Getter
#Setter
#AllArgsConstructor
#ToString
#NoArgsConstructor
#EqualsAndHashCode
public class Task {
private String taskName;
}
User:
package model;
import lombok.*;
#RequiredArgsConstructor
#Getter
#Setter
#EqualsAndHashCode
public class User {
private final String name;
private final String password;
private int ID;
}
Thanks in advance for help. :D
In your test case you are attempting to verify a call onto this method:
public void addTask(String taskName) throws SQLException {
taskActions.addTask(new Task(taskName), connectedUser);
}
With this:
User user = new User("admin","123");
...
verify(taskActionsMock).addTask(taskName,user);
The failure message ...
Actual invocation has different arguments:
taskActionsMock.addTask(
Task(taskName=wash),
null
);
... tells us that the value of connectedUser in the test call is null.
Looking at your code the connectedUser member of ToDoEngine is populated by a call to the signIn() method but your test case is not invoking that method and hence connectedUser is null when addTask is invoked by your test.
So, if you don't need/want to test that the correct user is supplied to addTask then just change your verify call to: verify(taskActionsMock).addTask(taskName,null)
However, that feels like a sidestep so instead you should ensure that connectedUser is not null and is the value you supplied to the verify call in your test case.
Your verify method specifies that you expect addTask to be called with specific taskName and user objects.
verify(taskActionsMock).addTask(taskName,user);
But since your connected user is null this expectation fails.
If you do not care about the connected user you can use matchers to tell Mockito to ignore its actual value. E.G.
verify(taskActionsMock).addTask(ArgumentMatchers.eq(taskName), ArgumentMatchers.any());
Or if you do care about the user just setup your ToDoEngine to have connected user.
I have created an Event handler by following https://github.com/nateyolles/aem-osgi-annotation-demo/blob/master/core/src/main/java/com/nateyolles/aem/osgiannotationdemo/core/listeners/SampleOsgiResourceListener.java and it works fine. However, I get the warning "The field SlingConstants.TOPIC_RESOURCE_ADDED is deprecated". I did some searching and found this thread :https://forums.adobe.com/thread/2325819
Here are the challenges that I am facing:
1) I want to create a separate configuration interface for my event handler. I tried this and it isn't working
package com.aem.sites.interfaces;
import org.apache.sling.api.SlingConstants;
import org.osgi.service.event.EventConstants;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
#ObjectClassDefinition(name = "Temperature Listener Configuration")
public #interface TemperatureListenerConfiguration {
#AttributeDefinition(
name = EventConstants.EVENT_FILTER,
description = "Configurable paths for temperature event listener",
type = AttributeType.STRING
)
String getPaths() default "/content/aemsite/en/jcr:content/root/responsivegrid/banner";
#AttributeDefinition(
name = EventConstants.EVENT_TOPIC,
description = "Event types",
type = AttributeType.STRING
)
String[] getEventTypes() default {SlingConstants.TOPIC_RESOURCE_ADDED,SlingConstants.TOPIC_RESOURCE_CHANGED, SlingConstants.TOPIC_RESOURCE_REMOVED};
}
package com.aem.sites.listeners;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventHandler;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aem.sites.interfaces.TemperatureListenerConfiguration;
#Component(immediate=true,
service=EventHandler.class,
configurationPid = "com.aem.sites.listeners.EventHandler")
#Designate(ocd=TemperatureListenerConfiguration.class)
public class TemperaturePropertyListener implements EventHandler{
private final Logger logger = LoggerFactory.getLogger(getClass());
#Override
public void handleEvent(Event event) {
logger.info("*********************Event handler*****************************");
}
#Activate
#Modified
public void activate(TemperatureListenerConfiguration config) {
//config.getPaths();
logger.info("**************************TemperaturePropertyListener******************activate**********************");
}
}
I also want the solution for SlingConstants deprecated issue. Not sure if ResourceChangeListener is the answer to my problem and if yes then how everything is going to work together in the code.
Thanks in advance
===============================
Latest Code
package com.aem.sites.listeners;
import java.util.List;
import org.apache.sling.api.resource.observation.ResourceChange;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.Designate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.aem.sites.interfaces.TemperatureListenerConfiguration;
#Component(immediate=true,
service=ResourceChangeListener.class,
configurationPid = "com.aem.sites.listeners.TemperaturePropertyListener")
#Designate(ocd=TemperatureListenerConfiguration.class)
public class TemperaturePropertyListener implements ResourceChangeListener{
private final Logger logger = LoggerFactory.getLogger(getClass());
#Override
public void onChange(List<ResourceChange> changes) {
for (final ResourceChange change : changes) {
logger.info("**************************TemperaturePropertyListener******************change type**********************"+change.getType());
}
}
#Activate
#Modified
public void activate(TemperatureListenerConfiguration config) {
//config.getPaths();
logger.info("**************************TemperaturePropertyListener******************activate**********************");
}
}
The Interface
package com.aem.sites.interfaces;
import org.apache.sling.api.resource.observation.ResourceChangeListener;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.AttributeType;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
#ObjectClassDefinition(name = "Temperature Listener Configuration")
public #interface TemperatureListenerConfiguration {
#AttributeDefinition(
name = ResourceChangeListener.PATHS,
description = "Configurable paths for temperature event listener",
type = AttributeType.STRING
)
String[] getPaths() default {"/content/aemsite/en/jcr:content/root/responsivegrid/banner"};
#AttributeDefinition(
name = ResourceChangeListener.CHANGES,
description = "Event types",
type = AttributeType.STRING
)
String[] getEventTypes() default {"ADDED","REMOVED","CHANGED","PROVIDER_ADDED", "PROVIDER_REMOVED"};
}
Looking at the Javadoc for org.apache.sling.api.SlingConstants in sling 9 documentation here: http://sling.apache.org/apidocs/sling9/org/apache/sling/api/SlingConstants.html
it tells you specifically that TOPIC_RESOURCE_ADDED is deprecated:
Deprecated. Register a ResourceChangeListener instead
Read the documentation for ResourceChangeListener, additionally, you can take a look at a sample SCR service impl from ACS Samples:
It should not be hard to convert that to R6 declarative service.
Also, here are two examples from the sling project ResourceBackedPojoChangeMonitor and OsgiObservationBridge
Try to mimic those classes with the properties in the same class.
For the following classes Texts ...
import android.support.annotation.NonNull;
import android.text.TextUtils;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import java.util.Collections;
import java.util.List;
import hrisey.Parcelable;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#Parcelable
public final class Texts implements android.os.Parcelable {
#NonNull List<Text> texts = Collections.emptyList();
public boolean hasTexts() {
return !texts.isEmpty() && textsHaveValues();
}
private boolean textsHaveValues() {
for (Text text : texts) {
if (TextUtils.isEmpty(text.getValue())) {
return false;
}
}
return true;
}
}
... and Text ...
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import hrisey.Parcelable;
import lombok.Data;
import lombok.NoArgsConstructor;
#Data
#NoArgsConstructor
#JsonIgnoreProperties(ignoreUnknown = true)
#Parcelable
public final class Text implements android.os.Parcelable {
private String textKey;
private String value;
}
... I wrote this unit test:
#RunWith(JUnit4.class)
public class TextsTest {
private Texts texts;
#Before
public void setUp() {
texts = new Texts();
}
#Test
public void hasTextsWithSingleEmptyItem() throws Exception {
texts.setTexts(Collections.singletonList(new Text()));
assertThat(texts.hasTexts()).isFalse();
}
}
The test succeeds in Android Studio 2.1.3 but it fails when I run ./gradlew clean test on my machine (MacOS 10.11.6, El Capitain, Java 1.7.0_79). Here is the error output:
com.example.model.TextsTest > hasTextsWithSingleEmptyItem FAILED
org.junit.ComparisonFailure: expected:<[fals]e> but was:<[tru]e>
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(
NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(
DelegatingConstructorAccessorImpl.java:45)
at com.example.model.TextsTest.hasTextsWithSingleEmptyItem(TextsTest.java:31)
Related posts
creating instance of object using reflection , when the constructor takes an array of strings as argument
IllegalArgumentException with constructing class using reflection and array arguments
How do you mock TextUtils? The part TextUtils.isEmpty(text.getValue()) should always be false when using the default Android test stubs.
Be sure to use a suitable implementation or consider using a different set of string utilities you already might have available with some other dependencies.
Edit by JJD
You are right, thanks! I use the Unmock plugin. So I had to unmock the relevant package to expose TextUtils in the unit tests:
unMock {
keepStartingWith "android.text."
}