Replace field values of a nested object dynamically - java

I am trying to write integration test for my scala application(with akka-http). I am running into a problem, for which I am not able to find a solution.
My Case classes are as below:
case class Employee(id:Long, name:String, departmentId:Long, createdDate:Timestamp) extends BaseEntity
case class EmployeeContainer(employee:Employee, department:Department) extends BaseEntity
I have a method like this
trait BaseTrait[E<:BaseEntity, C <: BaseEntity]{
def getById(id:Long): Future[List[C]] = {
//query from db and return result.
}
def save(obj:E) = {
//set the createDate field to the current timestamp
//insert into database
}
}
I can extend my class with BaseTrait and just override the getById() method. Rest of the layers are provided by our internal framework.
class MyDao extends BaseTrait[Employee, EmployeeContainer] {
override def getById(id:Long) = {
for {
val emp <- getFromDb(id)
val dept <- DeptDao.getFromDb(emp.departmentId)
val container = EmployeeContainer(emp,dept)
} yield(container)
}
}
So in the rest layer, I will be getting the response as the EmployeeContainer. The problem now I am facing is that, the modified date is automaticaally updated with the current timestamp. So, when I get back the result, the timestamp in the object I passed to save() method will be overwritten with the current time. When I write the test case, I need to have an object to compare to. But the timestamp of that object and the one I get abck will never be the same.
Is there anyway, in which I can replace all the occurrance of createDate with a known value of timestamp so that I can compare it in my testcase? The main problem is that I can not predict the structure of the container (it can have multiple case classes(nested or flat) with or without createDate fields).
I was able to replace the field using reflection if it comes in the main case class, but unable to do for nested structures.

You probably need to use some for of Inversion of Control. Your main problem is that you are calling the db directly: val emp <- getFromDb(id) and thus have no control on a test of the values that are received. Calling the DB on a unit test is also arguably a bad idea, since it expands the unit to the entire database layer. You want to test a small, self-contained unit.
A simple solution is to encapsulate your DB calls as an interface and pass an instance of that interface. For instance:
class MyDao extends BaseTrait[Employee, EmployeeContainer](val dbCall: Long => Employee) {
override def getById(id:Long) = {
for {
val emp <- dbCall(id)
val dept <- DeptDao.getFromDb(emp.departmentId)
val container = EmployeeContainer(emp,dept)
} yield(container)
}
}
Then you can simply use new MyDao(getFromDb) for normal code and val testDao = new MyDao(id => Employee(id, myFixedName, myFixedDeptID, myFixedTestDate)) from test code.

Related

Accessing Reactive CRUD repository from MapStruct Mapper stuck in block()

I am somewhat new to Spring and have recently generated a JHipster monolith application with the WebFlux option. My current aim is to make it compatible with Firestore and implement some missing features like inserting document references. To do so, I am currently having the following structure:
A domain object class "Device" which holds a field String firmwareType;
A domain object class "FirmwareType"
A DTO object DeviceDTO which holds a field FirmwareType firmwareType;
Correspondingly, I also have the corresponding Repository (extending FirestoreReactiveRepository which extends ReactiveCrudRepository) and Controller implementations, which all work fine. To perform the conversion from a "full object" of FirmwareType in the DTO-object to a String firmwareTypeId; in the Device-object, I implemented a MapStruct Mapper:
#Mapper(unmappedTargetPolicy = org.mapstruct.ReportingPolicy.IGNORE, componentModel = "spring")
public abstract class DeviceMapper {
private final Logger logger = LoggerFactory.getLogger(DeviceMapper.class);
#Autowired
protected FirmwareTypeRepository fwTypeRepo;
public abstract Device dtoToDevice(DeviceDTO deviceDTO);
public abstract DeviceDTO deviceToDto(Device device);
public abstract List<DeviceDTO> devicesToDTOs(List<Device> devices);
public abstract List<Device> dtosToDevices(List<DeviceDTO> dtos);
public String map(FirmwareType value) {
if (value == null || value.getId() == null || value.getId().isEmpty()) {
return null;
}
return value.getId();
}
public FirmwareType map(String value) {
if (value == null || value.isEmpty()) {
return null;
}
return fwTypeRepo.findById(value).block(); // <<-- this gets stuck!
}
}
The FirmwareTypeRepository which is autowired as fwTypeRepo field:
#Repository
public interface FirmwareTypeRepository extends FirestoreReactiveRepository<FirmwareType> {
Mono<FirmwareType> findById(String id);
}
The corresponding map functions get called perfectly fine, but the fwTypeRepo.findById(..) call in the marked line (third-last line) seems to get stuck somewhere and never returns or throws an error. When the "fwTypeRepo" via its Controller-endpoint is called, it works without any issues.
I suppose it must be some kind of calling context issue or something? Is there another way to force a result by Mono synchronously than block?
Thanks for your help in advance, everyone!
Edit: At this point, I am sure it has something to do with Autowiring the Repository. It seems to not correctly do so / use the correct instance. While a customized Interface+Impl is called correctly, the underlying logic (from FirestoreReactive/ReactiveCrudRepository) doesn't seem to supply data correctly (also when #Autowire is used in other components!). I found some hints pointing at the package-structure but (i.e. Application class needs to be in a root package) but that isn't an issue.
Mapstruct is not reactive as i know so this approach won't work, you'll need mapstruct to return a mono that builds the object itself but that wouldn't make sense as it's a mapping lib which is only for doing blocking things.
Could try use 2 Mono/Mappers, 1 for each DB call and then just Mono.zip(dbCall1, dbCall2) and set the the mapped db call output into the other objects field.
var call1 = Mono.fromFuture(() -> db.loadObject1()).map(o -> mapper1.map(o));
var call2 = Mono.fromFuture(() -> db.loadObject2()).map(o -> mapper2.map(o));
Mono.zip(call1, call2)
.map(t -> {
var o1 = t.getT1();
var o2 = t.getT2();
o1.setField(o2);
});

UnitTesting: how to pass my mock class in the read code

I am using hazelcast in my project and I want to unit test some function but i do not want it to connect to real hazelcast and perform test on it for that i created a custom mock class which simply uses scala map because in hazelcast maps also there
here is my code
trait UserRepository {
def getUserObj(id: String):Option[User]
def addToUserRepo(user: User)
}
class UserRepo extends UserRepository{
def getUserObj(id: String):Option[User] = {
val userMap = hcastClient.getMap[String, User]("UserMap")
val userObj = userMap.get(id)
Option(userObj)
}
def addToUserRepo(user: User) = {
val directUserMap: IMap[String, User] = hcastClient.getMap[String,User]("UserMap")
directUserMap.set(user.uuid, user)
}
and here i created a simple customized mocked version class where the functionality is same just; replaced it with scala map:
class UserRepoMock extends UserRepository {
val map:Map[String,User]=Map[String,User]()
def getUserMap:Map[String,User] = {
map
}
def getUserObj(id: String):User = {
val userMap = getUserMap
val userObj = userMap.get(id)
userObj
}
def addToUserRepo(user: User) = {
val userMap = getUserMap
userMap.put(user.uuid, user)
}
class UserUtil(userRepo:UserRepo) {
def addUser(user:User):Boolean={
try{
userRepo.addToUserRepo(user)
true
}
catch {
case e:Exception=>false
}
def getUser(id:String):User={
val user=userRepo.getUserObj(id)
user
}
Mow i want to unit test methods addUser and getUserof UserUtil class
by doing like this:
class UserUtilTest extends funSpec {
val userUtil=new UserUtil(new UserRepoMock)
userUtil.addUser //perform unit test on it
userUtil.getUser //perform unit test on it
// instead of doing this val userUtil=new UserUtil(new UserRepo)
}
but the compiler not allowing me to do that,there is something which i am missing, Please help me how can i achieve the desired functionality
This is the compiler error:
type mismatch; found : testhcastrepo.UserRepoMock required: com.repositories.UserRepo
Well: your utils class says:
class UserUtil(userRepo:UserRepo)
So it needs an instance of UserRepo.
But then your are passing an instance of UserRepoMock. A UserRepoMock is a UserRepository, as UserRepo is; but a UserRepoMock is not a UserRepo!
Probably it is as simple as changing the utils to
class UserUtil(userRepo:UserRepository)
to indicate that you don't want to specify a specific class. Instead you simply say: anything that has the trait will do!
Beyond that: the real answer might be: have a look at your naming habits. You see, those two names UserRepositor and UserRepo; they are pretty "close" to each other; and it is not at all clear, what the difference between the two is. If the names would be more distinct, like UserRepositoryTrait and HCastUserRepository you probably would not have made this mistake in the first place (not sure my suggestions are "good" names according to scala conventions; but they are just meant to give you an idea).

accessing child constant in parent class in java

OK, so I have an interesting problem. I am using java/maven/spring-boot/cassandra... and I am trying to create a dynamic instantiation of the Mapper setup they use.
I.E.
//Users.java
import com.datastax.driver.mapping.annotations.Table;
#Table(keyspace="mykeyspace", name="users")
public class Users {
#PartitionKey
public UUID id;
//...
}
Now, in order to use this I would have to explicitly say ...
Users user = (DB).mapper(Users.class);
obviously replacing (DB) with my db class.
Which is a great model, but I am running into the problem of code repetition. My Cassandra database has 2 keyspaces, both keyspaces have the exact same tables with the exact same columns in the tables, (this is not my choice, this is an absolute must have according to my company). So when I need to access one or the other based on a form submission it becomes a mess of duplicated code, example:
//myWebController.java
import ...;
#RestController
public class MyRestController {
#RequestMapping(value="/orders", method=RequestMethod.POST)
public string getOrders(...) {
if(Objects.equals(client, "first_client_name") {
//do all the things to get first keyspace objects like....
FirstClientUsers users = (db).Mapper(FirstClientUsers.class);
//...
} else if(Objects.equals(client, "second_client_name") {
SecondClientUsers users = (db).Mapper(SecondClientUsers.class);
//....
}
return "";
}
I have been trying to use methods like...
Class cls = Class.forName(STRING_INPUT_VARIABLE_HERE);
and that works ok for base classes but when trying to use the Accessor stuff it no longer works because Accessors have to be interfaces, so when you do Class cls, it is no longer an interface.
I am trying to find any other solution on how to dynamically have this work and not have to have duplicate code for every possible client. Each client will have it's own namespace in Cassandra, with the exact same tables as all other ones.
I cannot change the database model, this is a must according to the company.
With PHP this is extremely simple since it doesn't care about typecasting as much, I can easily do...
function getData($name) {
$className = $name . 'Accessor';
$class = new $className();
}
and poof I have a dynamic class, but the problem I am running into is the Type specification where I have to explicitly say...
FirstClientUsers users = new FirstClientUsers();
//or even
FirstClientUsers users = Class.forName("FirstClientUsers");
I hope this is making sense, I can't imagine that I am the first person to have this problem, but I can't find any solutions online. So I am really hoping that someone knows how I can get this accomplished without duplicating the exact same logic for every single keyspace we have. It makes the code not maintainable and unnecessarily long.
Thank you in advance for any help you can offer.
Do not specify the keyspace in your model classes, and instead, use the so-called "session per keyspace" pattern.
Your model class would look like this (note that the keyspace is left undefined):
#Table(name = "users")
public class Users {
#PartitionKey
public UUID id;
//...
}
Your initialization code would have something like this:
Map<String, Mapper<Users>> mappers = new ConcurrentHashMap<String, Mapper<Users>>();
Cluster cluster = ...;
Session firstClientSession = cluster.connect("keyspace_first_client");
Session secondClientSession = cluster.connect("keyspace_second_client");
MappingManager firstClientManager = new MappingManager(firstClientSession);
MappingManager secondClientManager = new MappingManager(secondClientSession);
mappers.put("first_client", firstClientManager.mapper(Users.class));
mappers.put("second_client", secondClientManager.mapper(Users.class));
// etc. for all clients
You would then store the mappers object and make it available through dependency injection to other components in your application.
Finally, your REST service would look like this:
import ...
#RestController
public class MyRestController {
#javax.inject.Inject
private Map<String, Mapper<Users>> mappers;
#RequestMapping(value = "/orders", method = RequestMethod.POST)
public string getOrders(...) {
Mapper<Users> usersMapper = getUsersMapperForClient(client);
// process the request with the right client's mapper
}
private Mapper<Users> getUsersMapperForClient(String client) {
if (mappers.containsKey(client))
return mappers.get(client);
throw new RuntimeException("Unknown client: " + client);
}
}
Note how the mappers object is injected.
Small nit: I would name your class User in the singular instead of Users (in the plural).

Unit testing Service class in Java Play Framework with Mockito

I'm starting some testing using Mockito on some service classes I use for connecting to my data store. I now want to determine the best practice way writing tests for it. The principle is for each entity there is a way to list, add, delete etc a row from the data store(mongo/mysql etc) for a specific entity.
Take this class that allows me to talk to my database that stores a list of companies
public class CompanyService extends Service{
public CompanyService() {
...
}
public saveCompany(Company company) {
...
}
// get a list of all companies
public List<Company> getCompanies() {
List<Company> companies = new ArrayList<Company>();
try {
CompanyResult<Rows<String, String>> rows = db.query(....);
for (Row<String, String> row : rows.getResult()) {
companies.add(row.getColumns());
}
catch (Exception e){
logger.warn("Error retrieving companies", e);
}
}
}
What exactly should I test in the getCompanies method and how can I use Mockito to do it?
Your System Under Test is the Company Service. You want to test that, assuming all its dependencies/collaborators function properly, it functions properly.
The db object looks like the only dependency/collaborator you need to worry about within the getCompanies() method. Use Mockito to mock the call to db.query()
You could set up a test method like so:
#Test
public void testGetCompanies() {
/*** Arraign ***/
CompanyService cs = new CompanyService();
// Setup mock db
DB mockDb = mock(DB.class);
// Setup fake results for query
CompanyResult<Rows<String, String>> sampleResults = ... // build sample results here
// Have query on mock return fake results
when(db.query(/* match arguments */)).thenReturn(sampleResults);
// Tell your System Under Test to use the mock collaborator
cs.setDB(mockDb);
/*** Act ***/
CompanyResult<Rows<String, String>> results = cs.getCompanies();
/*** Assert ***/
... // Test that results and sampleResults are effectively the same
}
You could test how your code would work if the db query returned and empty result,null or if row has null values or unexpected values. You could mock the db class to return these values. You could also mock the db class to throw an exception to see how your code could react.

Refactor procedural method using OO principles

I have a method where I want to factor out some code into its own method
This is what I have:
public class TD0301AssignmentForm extends Form {
public TD0301AssignmentForm(TD0301AssignmentDAO dao, STKUser authenticatedUser) {
this.dao = dao;
this.authenticatedUser = authenticatedUser;
}
public Object insert(HttpServletRequest request) {
TD0301Assignment tdas = new TD0301Assignment();
TD0301Assignment tdas_orig = null;
Date dateNow = new Date();
try {
// Get the inuput from HTML form
tdas.setCalc_num(FormUtil.getFieldValue(request, FIELD_CALC_NUM));
processDate(request, tdas);
tdas.setCalc_dept(FormUtil.getFieldValue(request, FIELD_CALC_DEPT));
tdas.setYear_oi(Integer.toString(DateUtil.getIntYear(dateNow)));
processCalcSafetyRequirements(request, tdas);
...etc...
if (isSucces()) {
// Instantiate a base work flow instance!
WorkflowInstance wfi = new WorkflowInstance();
WorkflowInstanceDAO wfiDAO = new WorkflowInstanceDAO();
wfi.setWorkflow_class_id(tdas.getCalc_level());
wfi.setStarted_by(authenticatedUser.getBadge());
wfi.setStatus("0");
wfi.setLast_date(dateNow);
// Insert the WorkFlowInstance into the database, db sets returned sequence number into the wfi object.
wfiDAO.insert(wfi, authenticatedUser);
// Insert the TD0301Assignment into the db
tdas.setWorkflow_instance_id(wfi.getWorkflow_instance_id());
}
I'd like to remove the WorkflowInstance code out into its own method (still in this Class) like this:
if (isSucces()) {
insertWorkFlowInstance(request, tdas);
tdas.setWorkflow_instance_id(wfi.getWorkflow_instance_id());
but wfi is now marked by Eclipse as not available. Should I do something like this to fix the error so that I can still get the wfi.getWorkflow_instance_id() in the isSuccess block above? I know it removes the error, but I am trying to apply best practices.
public class TD0301AssignmentForm extends Form {
private WorkflowInstance wfi = new WorkflowInstance();
private WorkflowInstanceDAO wfiDAO = new WorkflowInstanceDAO();
Instance variables ("properties" or "fields") are not necessarily the way to go if they're not used throughout the entire class.
Variables should have the smallest scope possible--this makes code easier to reason about.
With some noise elided, and also guessing, it seems like the WorkflowInstance and WorkflowInstanceDao could be localized (names changed to match Java conventions):
public class TD0301AssignmentForm extends Form {
public Object insert(HttpServletRequest request) {
TD0301Assignment tdas = new TD0301Assignment();
try {
tdas.setCalcNum(FormUtil.getFieldValue(request, FIELD_CALC_NUM));
processDate(request, tdas);
tdas.setCalcDept(FormUtil.getFieldValue(request, FIELD_CALC_DEPT));
tdas.setYearOi(Integer.toString(DateUtil.getIntYear(dateNow)));
processCalcSafetyRequirements(request, tdas);
if (isSuccess()) {
WorkflowInstance wf = buildWorkflow(tdas);
tdas.setWorkflowInstanceId(wf.getId());
}
}
}
private buildWorkflow(TD0301Assignment tdas) {
WorkflowInstance wfi = new WorkflowInstance();
wfi.setWorkflowClassId(tdas.getCalcLevel());
wfi.setStartedBy(authenticatedUser.getBadge());
wfi.setStatus("0");
wfi.setLastDate(new Date());
WorkflowInstanceDao wfiDao = new WorkflowInstanceDao();
wfiDao.insert(wfi, authenticatedUser);
}
}
Whether or not this is appropriate depends on how/if the WorkflowInstance is used in the rest of the method snippet you show. The DAO is almost certainly able to be localized.
As methods become smaller and easier to think about, they become more testable.
For example, buildWorkflow is almost easy to test, except that the DAO is instantiated "manually". This means that testing the method will either (a) depend on having a working DAO layer, or (b) it must be mocked by a framework that can mock static utility methods (several can).
Without seeing all your code it's not easy to see exactlywhat you are trying to achieve. The reason eclipse is complaining is because it no longer has a wfi instance to play with because you've moved its local instance into your method, but creating another wfi instance is not likely to be your answer.
To get this working change the wfi to be class local and either use it's id directly or return wfi.getWorkflow_instance_id() from insertWorkFlowInstance() and then pass that value into tdas.setWorkflow_instance_id()

Categories