CGLIB throws IllegalArgumentException when trying to use a Spock Spy - java

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"
}
}

Related

How to implement dependency injection with JUnit 5?

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>

Mocking a class that extends an abstract class in Spock

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"
}
}

Spring 4 TestNG +#Autowired

Have a class in the package com.conf
#Configuration
public class WebConfTest {
#Autowired
private Environment environment;
}
and unit test into com.service
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = { WebConfTest.class })
public class DMSServiceImplTest {
#Autowired
WebConfTest webConfTest;
#Test
public void testConnect() throws Exception {
}
}
test dependency :
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${springframework.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>${testng.version}</version>
<scope>test</scope>
</dependency>
In the IDEA navigation between beans work. But WebConfTest== null if I run test.
What is wrong?
Thanks.
#RunWith is for junit runner.
If you want to run tests with TestNG, you need to extends AbstractTestNGSpringContextTests.
http://docs.spring.io/spring/docs/current/spring-framework-reference/html/integration-testing.html#testcontext-support-classes-testng

JMock expecting void method to be invoked

I am using JMock to test the following method in a ProcessingTest class:
public void handle(Process process) {
processor.handleProcess(process);
}
I have mocked out the processor and process classes. For my test for this particular method, my JMock expectations are as follows:
checking( new Expectations() {
{
oneOf( ProcessingTest.this.processor ).handleProcess(
ProcessingTest.this.process );
}
} );
This is causing the following error:
unexpected invocation ...
no expectations specified
....
I assume that there is something incorrect in the expectations, what should they be?
I have tried to expect that the method in invoked atLeast one time, but this seems to be an issue for void methods.
I have no idea where is your problem exactly as you have not provided enough code. This is how to do this with JMock:
import org.jmock.Expectations;
import org.jmock.integration.junit4.JUnitRuleMockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Rule;
import org.junit.Test;
public class ProcessingTest {
#Rule
public final JUnitRuleMockery mockery = new JUnitRuleMockery() {{
setImposteriser(ClassImposteriser.INSTANCE);
}};
// just some dummy object, we will be comparing reference only
private final Process process = mockery.mock(Process.class);
private final Processor processor = mockery.mock(Processor.class);
private final Processing processing = new Processing(processor);
#Test
public void test() {
mockery.checking(new Expectations() {{
oneOf(ProcessingTest.this.processor).handleProcess(ProcessingTest.this.process);
}});
processing.handle(process);
}
}
public class Processing {
private final Processor processor;
public Processing(Processor processor) {
this.processor = processor;
}
public void handle(Process process) {
processor.handleProcess(process);
}
}
public interface Processor {
void handleProcess(Process process);
}
You need these dependencies:
<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock-legacy</artifactId>
<version>2.6.0</version>
</dependency>
<dependency>
<groupId>org.jmock</groupId>
<artifactId>jmock-junit4</artifactId>
<version>2.6.0</version>
</dependency>

Mockito throws NotAMockException using spring mvc injection

Hy guys,
I am trying to make a test using mockito on a web application that uses spring mvc.
When it executes this line "Mockito.reset(notificacaoRepositoryMock);" it throws "org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class com.sun.proxy.$Proxy40"
I saw that in am example and it worked, I can't find what I am doing wrong here.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestContext.class, WebAppConfig.class})
#WebAppConfiguration
public class NotificacaoControllerTest {
private MockMvc mockMvc;
#Autowired
private NotificacaoRepository notificacaoRepositoryMock;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
// *** Error here ***
Mockito.reset(notificacaoRepositoryMock);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
My TestContext is:
#Configuration
public class TestContext {
#Bean
public MessageSource messageSource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename("i18n/messages");
messageSource.setUseCodeAsDefaultMessage(true);
return messageSource;
}
#Bean
public NotificacaoRepository notificacaoRepository() {
return Mockito.mock(NotificacaoRepository.class);
}
}
The class I want mock is a CrudRepository interface
public interface NotificacaoRepository extends CrudRepository<Notificacao, Long> {
}
and, I think, the relevant part of my pom.xml is (spring versions and mockito)
<properties>
<hibernate.version>4.2.0.Final</hibernate.version>
<mysql.connector.version>5.1.21</mysql.connector.version>
<spring.version>4.1.6.RELEASE</spring.version>
<spring.data.version>1.8.0.RELEASE</spring.data.version>
</properties>
...
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>${spring.data.version}</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>1.9.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>3.2.3.RELEASE</version>
<scope>test</scope>
</dependency>
UPDATE
#jfcorugedo i tryed exactly what you said but I keep receiving the same error. My test context is just
#Configuration
public class TestContext {
#Bean
#Primary
public NotificacaoRepository notificacaoRepository() {
return Mockito.mock(NotificacaoRepository.class);
}
}
and my test class now is:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebAppConfig.class})
#WebAppConfiguration
#Import({TestContext.class})
public class NotificacaoControllerTest {
#Autowired
NotificacaoRepository notificacaoRepositoryMock;
#Before
public void setUp() {
Mockito.reset(notificacaoRepositoryMock); // Error >> org.mockito.exceptions.misusing.NotAMockException: Argument should be a mock, but is: class com.sun.proxy.$Proxy55
this.mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
This is because Spring create a proxy around your bean.
Try to not inject mock using Spring
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {TestContext.class, WebAppConfig.class})
#WebAppConfiguration
public class NotificacaoControllerTest {
private MockMvc mockMvc;
private NotificacaoRepository notificacaoRepositoryMock;
#Autowired
private WebApplicationContext webApplicationContext;
#Before
public void setUp() {
notificacaoRepositoryMock = Mockito.mock(NotificacaoRepository.class);
Mockito.reset(notificacaoRepositoryMock);
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
}
As far as I know, you are trying to replace the spring bean with a mock version.
In that case you have to annotate the method that produces your mock with the annotation #Primary, so Spring could choose the mock object by default in each autowired field (of course if it doesn't have any qualifier).
If you're trying to use a Spring context in your test that has some real beans and other mock beans, you have to follow this steps:
Create a #Configuration class in your test folder that inject a mock instance of the beans you want to mock
Import this configuration class in your test
For instance:
Configuration class that injects the mock
import static org.mockito.Mockito.mock;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import xxx.xxx.xxx.NotificacaoRepository;
#Configuration
public class MockConfigurer {
#Bean
#Primary
public NotificacaoRepository registerMock() {
return mock(NotificacaoRepository.class);
}
}
Test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = {WebAppConfig.class})
#WebAppConfiguration
#Import({MockConfigurer.class})
public class NotificacaoControllerTest {
//Now the instance injected here should be a mock object
#Autowired
private NotificacaoRepository notificacaoRepositoryMock;
...
}

Categories

Resources