Declare Fields in groovy class Spock framework Unit test Case - java

I am make a Test Class for testing Employee Service , Everything is working fine , Except when i use a Field in other function it gets null.
// field
Employee employee;
// this will be assigned to return of below
employee= employeeService.create(emp);
// this(employee) gets null
Now what is want is to use
employeeService.remove(employee.getId);
for delete test function ,
Below is my code . Kindly provide some suggestion
I am new to groovy.
package services.employee
import spock.lang.Specification
#ContextConfiguration(loader = SpringApplicationContextLoader.class,classes = Application.class)
class EmployeeSpec extends Specification{
#Autowired
EmployeeService employeeService;
Employee response = null;
def "Check if Employee exists"(){
setup:
long empid = 43;
when:
empid > 0
then:
employeeService.getEmployee(empid);
}
def "Find all Employee"(){
setup:
when:
def res = employeeService.getAllEmployees();
then:
res.size()>0;
}
def "Insert a New Employee"(){
setup:
Employee employee = new Employee();
employee.setName("Ajit Singh");
employee.setCity("Delhi");
employee.setAge(34);
when:
response = employeeService.createEmployee(employee);
then:
response.getName().equals("Ajit Singh");
}
def "Updating an Employee"(){
}
def "delete an Employee"(){
setup:
if (response.equals(null))
println("Object is null");
when:
employeeService.removeEmployee(response.empID)
then:
def res = employeeService.find(response.empID);
res == null;
}
}

While Ivans answer above works, the proper way to do this in Spock is to use the #Shared annotation:
#Shared Employee response = null
(see https://spockframework.github.io/spock/docs/1.1-rc-1/all_in_one.html#_fields)
To cite the docs why this is the proper way to do it:
Static fields should only be used for constants. Otherwise shared fields are preferable, because their semantics with respect to sharing are more well-defined.

Yes, you can use #Shared tag, variable declared with that tag is likely global variable in java or C.
I don't know what your 'createEmployee' function is returning, but that's not a problem in groovy as you can use 'def' type instead of all types.
So, you just add #Shared def response = null

Related

Groovy Spock - mocked method not returning desired value

I have a simple Java class with single method so far which fetches data from database and then does some updates, it looks like this:
#Slf4j
#Service
public class PaymentServiceImpl implements PaymentService {
private final PaymentsMapper paymentsMapper;
private final MyProperties myProperties;
public PaymentServiceImpl(PaymentsMapper paymentsMapper,
MyProperties myProperties) {
this.paymentsMapper = paymentsMapper;
this.myProperties = myProperties;
}
#Override
#Transactional
public void doSomething() {
List<String> ids = paymentsMapper.getPaymentIds(
myProperties.getPayments().getOperator(),
myProperties.getPayments().getPeriod().getDuration().getSeconds());
long updated = 0;
for (String id : ids ) {
updated += paymentsMapper.updatedPaymentsWithId(id);
}
}
}
For the record, MyProperties class is a #ConfigurationProperties class that fetches properties from the application.properties, it looks like this:
#Data
#Configuration("myProperties")
#ConfigurationProperties(prefix = "my")
#PropertySource("classpath:application.properties")
public class MyProperties {
private Payments payments;
#Getter
#Setter
public static class Payments {
private String operator;
private Period period;
#Getter #Setter
public static class Period{
private Duration duration;
}
}
}
Now I am trying to write a simple test for such method, I have come up with this:
class PaymentServiceImplTest extends Specification {
#Shared
PaymentsMapper paymentsMapper = Mock(PaymentsMapper)
#Shared
MyProperties properties = new MyProperties()
#Shared
PaymentServiceImpl paymentService = new PaymentServiceImpl(paymentsMapper, properties)
def setupSpec() {
properties.setPayments(new MyProperties.Payments())
properties.getPayments().setOperator('OP1')
properties.getPayments().setPeriod(new MyProperties.Payments.Period())
properties.getPayments().getPeriod().setDuration(Duration.ofSeconds(3600))
}
def 'update pending acceptation payment ids'() {
given:
paymentsMapper.getPaymentIds(_ as String, _ as long) >> Arrays.asList('1', '2', '3')
when:
paymentService.doSomething()
then:
3 * paymentsMapper.updatedPaymentsWithId(_ as String)
}
}
but trying to run the test I am getting a Null Pointer Exception:
java.lang.NullPointerException
at com.example.PaymentServiceImpl.doSomething(PaymentServiceImpl.java:33)
at com.example.service.PaymentServiceImplTest.update pending acceptation payment ids(PaymentServiceImplTest.groovy:33)
Could someone tell me why is that? Why am I getting NPE there?
My pom.xml dependencies for Spock are as follows:
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-spring</artifactId>
<scope>test</scope>
</dependency>
You have several problems here:
You use #Shared variables. You should only do that when absolutely necessary, e.g. if you need to instantiate expensive objects like DB connections. Otherwise, there is danger that context from feature method A bleeds over into B, because the shared object was modified. Then features suddenly become sensitive to execution order, which they should not be.
Your mock is also shared, but you are trying to specify stubbed results and interactions from within a feature method. This is not going to work as you might expect, if you do that in multiple feature methods. In that case, you should create a new instance for each feature, which also means that a shared variable does not make sense anymore. The only case in which it might make sense is if the exact same mock instance is used without any change in all feature methods. But then an interaction like 3 * mock.doSomething() would continue counting across features. Also, a mock is always cheap, so why make a mock shared in the first place?
Interaction paymentsMapper.getPaymentIds(_ as String, _ as long) will not match in your case, hence the null default return value. The reason is the second parameter's runtime type in Groovy is Long. So you need to change the parameter list to any of (_ as String, _ as Long) or simpler (_ as String, _) (_, _), (*_), depending on how specific your match needs to be.
So you could do either of the following:
Do not use #Shared for your fields and rename setupSpec to setup. Very simple, the specification will not run noticeably slower because of this.
If you insist in shared variables, make sure to either set up the two mock interactions only once in the setupSpec method or inline in the mock definition, i.e. something like
#Shared
PaymentsMapper paymentsMapper = Mock(PaymentsMapper) {
getPaymentIds(_ as String, _ as Long) >> Arrays.asList('1', '2', '3')
3 * updatedPaymentsWithId(_ as String)
}
// ...
def 'update pending acceptation payment ids'() {
expect:
paymentService.doSomething()
}
But then the mock interactions are outside of the feature method, which might make readers wonder what the feature method actually does. So, technically this works, but an easily readable test looks different IMO.
You also might have the idea to instantiate and configure the shared mock in each feature method. But then the one-time assignment to #Shared PaymentServiceImpl paymentService would use another instance or null. Uh-oh! Do you see the problem with shared mocks? I guess, it is not worth your premature optimisation, because this is what I believe it is.

Creating annotations with byte-buddy but my code can't find the annotations

Currently I'm trying to generate my classes for my Tests via this:
AnnotationDescription entity = AnnotationDescription.Builder.ofType(Entity.class) //
.build();
AnnotationDescription table = AnnotationDescription.Builder.ofType(Table.class) //
.define("name", "FIRST_TABLE") //
.build();
// #Column ( name = "X1", length = 20 )
AnnotationDescription nameAnnotationColumn = AnnotationDescription.Builder.ofType(Column.class) //
.define("name", "X1") //
.define("length", 20) //
.build();
// #Column ( name = "X1", length = 20 )
AnnotationDescription nameAnnotationKeyColumn = AnnotationDescription.Builder.ofType(Column.class) //
.define("name", "ADR_EXTERN_KEY") //
.define("precision", 15) //
.build();
Class<? extends Serializable> subclass = new ByteBuddy().subclass(Serializable.class) //
.annotateType(entity, table) //
.defineField("name", String.class, Visibility.PRIVATE).annotateField(nameAnnotationColumn) //
.defineMethod("getName", String.class, Visibility.PUBLIC).intercept(FieldAccessor.ofField("name")) //
.defineField("key", BigDecimal.class, Visibility.PRIVATE).annotateField(nameAnnotationKeyColumn) //
.defineMethod("getKey", BigDecimal.class, Visibility.PUBLIC).intercept(FieldAccessor.ofField("key")) //
.make() //
.load(getClass().getClassLoader()) //
.getLoaded();
But my own code is trying to identify a class which should have an annotation like #Entity and #Table(name = "XXX"). So the call to .annotatypeType(..) seemed to be either doing something I don't understand or I just create the annotations in a wrong way..
The code which tries to identify if the classes contain the #Entity and #Table annotation is in general like this:
public class X {
private Class<? extends Serializable> givenClass;
...
if (hasAnnotation(this.givenClass.getClass().getAnnotations(), Entity.class)) {
....
}
where the method looks like this:
private boolean hasAnnotation(Annotation[] annotations, Object annotationToBeExistent) {
boolean result = false;
for (int i = 0; i < annotations.length; i++) {
if (annotations[i].annotationType().equals(annotationToBeExistent)) {
result = true;
}
}
return result;
If I create a class manually and implement is as usual the code finds the annotations...So I think my mistake must be somewhere in using byte-buddy ?
Update:
After thinking about it and the comment I found out that my creation was wrong it must look like this instead:
Serializable subclass = new ByteBuddy().subclass(Object.class) //
.implement(Serializable.class)
.annotateType(entity, table) //
But my code will not see the created annotations?
Update 2:
So after your comments/suggestions (which helped a lot; Thanks for the support) I have drilled down my problem to the following code snippet: I assume my usage of AnnotationDescription or creating the annotation is not correct cause the assert will result in false.
AnnotationDescription entity = AnnotationDescription.Builder.ofType(Entity.class) //
.build();
Serializable subclass = new ByteBuddy().subclass(Object.class) //
.implement(Serializable.class)
.annotateType(entity) //
.make() //
.load(this.getClass().getClassLoader()) //
.getLoaded();
assertThat(subclass.getClass().isAnnotationPresent(Entity.class)).isTrue();
Your API use is correct and I could even validate the working by invoking:
subclass.isAnnotationPresent(Entity.class)
on your resulting class. I assume that there is a problem in your scanning logic. Some tools scan classes by introspecting class files from jars directly, maybe this is the reason that you cannot locate the class?
As for your update, are you expecting for Byte Buddy to return an instance? Byte Buddy only generates classes and Class types implement the Serializable interface which is why your code compiles to begin with:
AnnotationDescription entity = AnnotationDescription.Builder
.ofType(Entity.class)
.build();
Class<?> subclass = new ByteBuddy().subclass(Object.class)
.implement(Serializable.class)
.annotateType(entity)
.make()
.load(this.getClass().getClassLoader())
.getLoaded();
Now, the resulting class should carry your expected annotation. You can use reflection to create an instance that implements the Serializable interface.

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).

Replace field values of a nested object dynamically

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.

Spring + Scala + anonymous block or class

I don't know why but the class below doesn't work if instance variable text is private, but if I leave out private, it works.
Debugging the test in section "setField" I could see that the instance variable name should be "text" but it becomes "com$test$SimpleTest$$text"
package com.test
import org.testng.annotations.Test
import org.springframework.test.util.ReflectionTestUtils
class SimpleTest {
private var text = ""
#Test
def testValueOfX(): Unit = {
val simpleTest = new SimpleTest
ReflectionTestUtils.setField(simpleTest,"text", "abc")
println(
Option[String](null)
.map(v => v + " 123")
.getOrElse {
simpleTest.text + " 321"
})
}
}
I believe that the problem someway be the "getOrElse" because if I leave out too, it works.
Scala compiler has a right to compile your private field into an any working java code, as it doesn't affect interoperability (if you don't do any tricks). Spring's setField actually do such trick, as it makes your private field accessible (setAccessible(true) inside). Public fields are always compiling as is to give you appropriate interface from Java.
Use http://docs.scala-lang.org/overviews/reflection/environment-universes-mirrors.html to work with Scala reflection. Also this article may be helpful.
Here is explanation why scalac uses another name for private field.
P.S. The reason why removing .getOrElse(text) make it work is because you don't use text anywhere but inside this piece of code.
This class is only to show the problem, actually it is very different of real class. So I changed the strategy to receive an instance by #Autowired a method instead #Autowired a field.
package com.test
import org.testng.annotations.Test
import org.springframework.test.util.ReflectionTestUtils
class SimpleTest {
// item 1
private var text = ""
#Test
def testValueOfX(): Unit = {
val simpleTest = new SimpleTest
ReflectionTestUtils.invokeSetterMethod(simpleTest, "text", "abc")
println(
Option[String](null)
.map(v => v + " 123")
.getOrElse {
simpleTest.text + " 321"
})
}
// item 2
def setText(text: String): Unit = {
this.text = text
}
}
I'm not using #Autowired in this sample but in the real class I am. So if you need to get an instance follow instructions below.
Item 1 -> if I put on #Autowired it doesn't work because like said dk14 Scala compiler has a right to compile your private field into an any working java code. So, the compiler change the field's name when it compiles the class
Item 2 -> I put on #Autowired in the setter method, it works.

Categories