I was trying to follow multiple tutorials about the subject but I cannot understand how they support DI. To my basic understanding, DI should be supported by supplying extended/implemented classes object to the test, so the test will be able to be executed with multiple variations of the objects.
For example:
#Test
public void myTest(Base baseObj){
assertThis(baseObj);
assertThat(baseObj);
}
class Base {
//data
//methods
}
class Class1 extends Base{}
class Class2 extends Base{}
class Class3 extends Base{}
There should be a way to supply objects of the derived classes to the test. Am I wrong till here?
I couldn't understand from the explanations, how TestInfo class for example (or the other classes) helps me with that?
Can you enlighten me please?
You can do it like this:
public class Test1 {
#ParameterizedTest
#MethodSource("myTest_Arguments")
public void myTest(Base baseObj){
System.out.println(baseObj);
}
static Stream<Arguments> myTest_Arguments() {
return Stream.of(
Arguments.of(new Class1()),
Arguments.of(new Class2()),
Arguments.of(new Class3()));
}
}
The entire code is:
package com.example.demo;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
class Base {}
class Class1 extends Base{}
class Class2 extends Base{}
class Class3 extends Base{}
public class Test1 {
#ParameterizedTest
#MethodSource("myTest_Arguments")
public void myTest(Base baseObj){
System.out.println(baseObj);
}
static Stream<Arguments> myTest_Arguments() {
return Stream.of(Arguments.of(new Class1()),Arguments.of(new Class2()),Arguments.of(new Class3()));
}
}
and the dependencies used are:
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-params</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<version>5.9.0</version>
<scope>test</scope>
</dependency>
Related
Spring AOP is a proxy-based AOP framework. This means that to implement aspects to the target objects, it'll create proxies of that object. This is achieved using either of two ways:
JDK dynamic proxy – the preferred way for Spring AOP. Whenever the targeted object implements even one interface, then JDK dynamic proxy will be used
CGLIB proxy – if the target object doesn't implement an interface, then CGLIB proxy can be used
source
And yet, CGLIB is always used... This applies both to the standard Spring delivery (for example, using the #Transactional annotation) and using its written aspect
public interface MyService {
void methodFirst();
void methodSecond();
}
#Service
public class MyServiceImpl implements MyService {
#Override
#AnnotationCustom
public void methodFirst() {
System.out.println("methodFirst()");
methodSecond();
}
#Override
#AnnotationCustom
public void methodSecond() {
System.out.println("methodSecond()");
System.out.println();
}
#SpringBootApplication
public class AopTransactionalSpringApplication {
public static void main(String[] args) {
SpringApplication.run(AopTransactionalSpringApplication.class, args);
}
#Aspect
#Component
public class AspectCustom {
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("#annotation(aop.transactional.spring.aop.annotation.AnnotationCustom)")
public void annotatedMethodCustom() {}
#Before("annotatedMethodCustom() && publicMethod()")
public void printSomeMessage() {
System.out.println(" AspectCustom");
}
}
#SpringBootApplication
public class AopTransactionalSpringApplication {
public static void main(String[] args) {
SpringApplication.run(AopTransactionalSpringApplication.class, args);
}
test
#SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public abstract class AopTransactionalSpringApplicationTests {
}
class MyServiceTest extends AopTransactionalSpringApplicationTests {
#Autowired
private MyService myService;
#Test
void methodFirst() {
myService.methodFirst();
System.out.println();
}
}
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.source-target.version>11</java.source-target.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
The documentation says that if we use the interface, then JDK will be used, and if the target class does not implement the interface, then inheritance will be used and then CGLIB will create a proxy, inheriting from the target class . But in debug mode, I actually observe something else .
Why is that?
Maybe I'm doing something wrong, please point out the mistakes in my understanding of this issue.
I'm trying to spy my service class but I'm getting below exception, can you please help what I'm doing wrong here:
I tried to create Spy object using below code but is not working as expected
def myService = Spy(MyService)
MyInterface.groovy
interface MyInterface<T> {
public String welcome(T t);
}
MyService.groovy
#Service
class MyService implements MyInterface<WelcomeMessage> {
#Override
String welcome(WelcomeMessage welcomeMessage) {
try {
// Business logic
} catch (ex) {
// Catch Exception
}
}
}
import spock.lang.Specification
class myServiceTest extends Specification {
def "testWelcome"() {
setup: "create mock object"
def myService = Spy(MyService)
and: " and object with mock data"
when: "invoke welcomeMessage"
then: "Expecting no exception is thrown"
}
}
Exception:
java.lang.IllegalArgumentException
at net.sf.cglib.proxy.BridgeMethodResolver.resolveAll(BridgeMethodResolver.java:61)
at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:911)
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:498)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
at org.spockframework.mock.runtime.ProxyBasedMockFactory$CglibMockFactory.createMock(ProxyBasedMockFactory.java:154)
at org.spockframework.mock.runtime.ProxyBasedMockFactory.create(ProxyBasedMockFactory.java:68)
at org.spockframework.mock.runtime.JavaMockFactory.createInternal(JavaMockFactory.java:59)
at org.spockframework.mock.runtime.JavaMockFactory.create(JavaMockFactory.java:40)
at org.spockframework.mock.runtime.CompositeMockFactory.create(CompositeMockFactory.java:44)
at org.spockframework.lang.SpecInternals.createMock(SpecInternals.java:51)
at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:296)
at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:286)
at org.spockframework.lang.SpecInternals.SpyImpl(SpecInternals.java:169)
Thanks for your support
Could you provide your versions of spring, spock and cglib?
For these ones I could not reproduce the described issue:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.spockframework</groupId>
<artifactId>spock-core</artifactId>
<version>1.2-groovy-2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.10</version>
<scope>test</scope>
</dependency>
The code I have is almost the same (instead WelcomeMessage I use String):
MyInterface.groovy
interface MyInterface<T> {
String welcome(T t);
}
MyService.groovy
import org.springframework.stereotype.Service
#Service
class MyService implements MyInterface<String> {
#Override
String welcome(String welcomeMessage) {
return welcomeMessage
}
}
MyServiceTest.groovy
import spock.lang.Specification
class MyServiceTest extends Specification {
def "Welcome"() {
setup: "create mock object"
def myService = Spy(MyService)
when: "invoke welcomeMessage"
def actual = myService.welcome("any")
then:
actual == "any"
}
}
I have the following class that extends an Abstract class
public class MyConverter extends AbstractConverter<A, B> {
#Override
public B convert(A source) {
// implementation here
}
}
AbstractConverter is from org.modelmapper, it's declaration is the following:
public abstract class AbstractConverter<S, D> implements Converter<S, D> {
...
}
Now, from a Groovy file that uses Spock I want to mock my class:
class ATestOverThere extends Specification {
def myConverter = Mock(MyConverter) // THIS THROWS THE EXCEPTION
...
}
But I'm getting the folling exception when initializing the mock.
java.lang.IllegalArgumentException
at net.sf.cglib.proxy.BridgeMethodResolver.resolveAll(BridgeMethodResolver.java:61)
at net.sf.cglib.proxy.Enhancer.emitMethods(Enhancer.java:911)
at net.sf.cglib.proxy.Enhancer.generateClass(Enhancer.java:498)
at net.sf.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:216)
at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
at net.sf.cglib.proxy.Enhancer.createClass(Enhancer.java:317)
at org.spockframework.mock.runtime.ProxyBasedMockFactory$CglibMockFactory.createMock(ProxyBasedMockFactory.java:154)
at org.spockframework.mock.runtime.ProxyBasedMockFactory.create(ProxyBasedMockFactory.java:68)
at org.spockframework.mock.runtime.JavaMockFactory.createInternal(JavaMockFactory.java:59)
at org.spockframework.mock.runtime.JavaMockFactory.create(JavaMockFactory.java:40)
at org.spockframework.mock.runtime.CompositeMockFactory.create(CompositeMockFactory.java:44)
at org.spockframework.lang.SpecInternals.createMock(SpecInternals.java:51)
at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:296)
at org.spockframework.lang.SpecInternals.createMockImpl(SpecInternals.java:286)
at org.spockframework.lang.SpecInternals.MockImpl(SpecInternals.java:105)
at com.tuenti.services.argentina.business.products.sva.SvaManagerSpec.$spock_initializeFields(SvaManagerSpec.groovy:14)
Seems that I'm not able to mock a class that extends an abstract class with Spock, can I?
With Objenesis in addition to CGLIB on the classpath, as Michael Easter correctly said, your example works. No need to use ByteBuddy, though.
See also the Spock manual.
<!-- (...) -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>3.2.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.objenesis</groupId>
<artifactId>objenesis</artifactId>
<version>2.5.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.0.0</version>
<scope>compile</scope>
</dependency>
<!-- (...) -->
package de.scrum_master.stackoverflow;
import org.modelmapper.AbstractConverter;
public class MyConverter extends AbstractConverter<Integer, String> {
#Override
protected String convert(Integer source) {
return source.toString();
}
}
package de.scrum_master.stackoverflow
import spock.lang.Specification
class MyConverterTest extends Specification {
def "test"() {
given:
def myConverter = Mock(MyConverter) {
convert(_) >>> ["one", "two"] >> { callRealMethod() }
}
expect:
myConverter.convert(11) == "one"
myConverter.convert(22) == "two"
myConverter.convert(11) == "11"
myConverter.convert(22) == "22"
}
}
I have a java class - very typical of the usual singleton - like this :
PLEASE NOTE : I have left out the "if null" logic here, for the sake of brevity, because that isn't what I am having trouble with, and I don't want to crowd the question.
public class MySingleton
{
ObjectMapper mapper;
private MySingleton()
{
new MySingleton(new ObjectMapper())
}
private MySingleton(ObjectMapper mapper)
{
this.mapper = mapper;
}
private static final class Lazy
{
static final MySingleton INSTANCE = new MySingleton();
}
public static MySingleton getInstance()
{
return Lazy.INSTANCE;
}
}
Now - that is great - and that works - but what if I am trying to test this in a unit test...
I want to mock the mapper - so I can do :
ObjectMapper mockObjectMapper = mock(ObjectMapper.class)
But, then when I need to somehow call the constructor of "MySingleton" in order to test it...
How do I do that - given that from my test class, I know it will say "MySingleton(arguments here) has private access in MySingleton"?
Singletons are the enemies of testable code. The links given in the answer to that question are an excellent argumentation on what's evil with singletons as well as why and how to avoid them.
You can use PowerMock to inject your test ObjectMapper instance using ConstructorMocking.
http://benkiefer.com/blog/2013/04/23/powermockito-constructor-mocking/
I had to modify your example singleton so that the constructors were chained correctly.
public class MySingleton {
ObjectMapper mapper;
private MySingleton()
{
//This does not work.
//new MySingleton(new ObjectMapper());
this(new ObjectMapper());
}
private MySingleton(ObjectMapper mapper)
{
this.mapper = mapper;
}
private static final class Lazy
{
static final MySingleton INSTANCE = new MySingleton();
}
public static MySingleton getInstance()
{
return Lazy.INSTANCE;
}
}
I also stubbed the ObjectMapper Class.
public class ObjectMapper {
//Empty Sample uses default CTR
}
I was able to test this as follows using the instructions from the link previously listed:
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class)
#PrepareForTest(MySingleton.class)
public class MySingletonTest {
#Test
public void testSingletonCtr() throws Exception {
ObjectMapper mapper = new ObjectMapper();
PowerMockito.whenNew(ObjectMapper.class).withNoArguments().thenReturn(mapper);
Assert.assertEquals(MySingleton.getInstance().mapper, mapper);
}
}
I am doing this in a maven project. I needed the following dependencies added to my test scope:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4-rule</artifactId>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
I do tend to agree that Singletons tend to cause problems long-term for code maintenance and scalability. If you have capacity to look for alternative approaches to your problem it may benefit you to do so. If not, then I believe that the PowerMock utility will provide you the capability you're looking for.
Best of Luck.
I am having a problem with JUnit test using Powermock. I want to mock final class object looking like this - created via builder:
public final class Request implements Serializable {
private static final long serialVersionUID = 1L;
public static ReferenceStep builder() {
return new Builder();
}
I need to test the method that uses this object (Request) but don't need to test the Request itself. So I wrote a test:
import junit.framework.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.Rule;
import java.util.List;
import java.util.ArrayList;
import java.util.Collections;
import org.mockito.Mock;
import org.junit.runner.RunWith;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
#RunWith(PowerMockRunner.class) // <-this leads to error
#PrepareForTest( { Request.class })
public class Test1 {
private static final String acc1 = "acc1";
private static final String acc2 = "acc2";
private aService aService;
private tService tService;
#Before
public void setupTestConditionsBeforeEachTest() throws Exception {
Factory Factory = (Factory) Class.forName(
"example.FactoryImpl").newInstance();
aService = bankFactory.getAccountService();
tService = bankFactory.getTransferService();
Factory.setupInitialData();
}
#Test
public void doTransfer() throws Exception {
aService.createAccount(CASH_ACCOUNT_1,100);
aService.createAccount(CASH_ACCOUNT_2,0);
TransactionLeg leg1 = new TransactionLeg(CASH_ACCOUNT_1,MoneyUtils.toMoney("10.00", "EUR"));
TransactionLeg leg2 = new TransactionLeg(CASH_ACCOUNT_1,MoneyUtils.toMoney("-10.00", "EUR"));
//used later for Request
List<TransactionLeg> legs = new ArrayList<TransactionLeg>();
legs.add(leg1);
legs.add(leg2);
Request tested = PowerMockito.mock(Request.class);
PowerMockito.when(tested.getLegs()).thenReturn(legs);
PowerMockito.when(tested.getTransactionRef()).thenReturn("First transaction");
PowerMockito.when(tested.getTransactionType()).thenReturn("Type1");
tService.transferFunds(tested);
}
}
}
So the method to be tested is transferFunds. I only need Request class to pass as parameter to this function. Ok I run this test and get an error:
Tests in error:
doTransfer(...Test1): Error creating bean with name 'exceptionTranslation' defined in class path resource [.../config/PersistenceJPAConfig.class]: Initialization of bean failed; nested exception is org.springframework.beans.factory.BeanCrea
tionException: Error creating bean with name 'entityManagerFactoryBean' defined in class path resource [.../config/PersistenceJPAConfig.class]: Invocation of init method failed; nested exception is javax.persistence.PersistenceException: [PersistenceUnit: banktest_pu] Unable to
build EntityManagerFactory
Using Maven. pom looks like this:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.9</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-core</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.5</version>
<scope>test</scope>
</dependency>
It seems that the problem is in Spring configuration. But I don't know where to dig in the problem. Alternatively, is there any way to avoid using mock? The class Transfer is made using builder() so I don't see how to put test values in it(and I also cannot change any part of the code)
Thanks in advance!