Cannot mock final Kotlin class using Mockito 2 - java

I am unable to mock a Kotlin final class using Mockito 2. I am using Robolectric in addition.
This is my test code:
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
public class Test {
// more mocks
#Mock
MyKotlinLoader kotlinLoader;
#Before
public void setUp() {
MockitoAnnotations.initMocks(this);
}
}
The test fails when we try to initialise the mocks in the setUp() method.
In addition, I am using the following gradle dependencies in my code:
testCompile 'org.robolectric:robolectric:3.3.2'
testCompile 'org.robolectric:shadows-multidex:3.3.2'
testCompile 'org.robolectric:shadows-support-v4:3.3.2'
testCompile("org.powermock:powermock-api-mockito2:1.7.0") {
exclude module: 'hamcrest-core'
exclude module: 'objenesis'
}
testCompile 'junit:junit:4.12'
testCompile 'org.mockito:mockito-inline:2.8.9'
All other unit tests pass using this configuration but as soon as I try to mock the Kotlin class it throws the following error:
Mockito cannot mock/spy because :
- final class
Please note I am using Mockito version 2 and I am using the inline dependency which automatically enables the ability to mock final classes.

PowerMock implements its own MockMaker which leads to incompatibility with Mockito mock-maker-inline, even if PowerMock is just added as a dependency and not used. If two org.mockito.plugins.MockMaker exist in path then any only one can be used, which one is undetermined.
PowerMock can however delegate calls to another MockMaker, and for then tests are run without PowerMock. Since PowerMock 1.7.0 this can be configured with using the PowerMock Configuration.
The MockMaker can be configured by creating the file org/powermock/extensions/configuration.properties and setting:
mockito.mock-maker-class=mock-maker-inline
Example of using Mockito mock-maker-inline with PowerMock: https://github.com/powermock/powermock-examples-maven/tree/master/mockito2

Since Mockito 2.1.0 there is a possibility to mock final types, enums, and final methods. It was already mentioned in the comments to the original question.
To do this, you’ll need to create a folder (if dont exist) test/resources/mockito-extensions and add there file with the name org.mockito.plugins.MockMaker and this line:
mock-maker-inline
Links to documentation and tutorial

mock-maker-inline works as is pointed out in other answers. But It's really slow. You can use the all-open plugin to avoid this problem.
To do so you need:
Create an Annotation:
annotation class Mockable
Activate all-open in your build.gradle file:
dependencies {
classpath "org.jetbrains.kotlin:kotlin-allopen:$kotlin_version"
}
apply plugin: 'kotlin-allopen'
allOpen {
annotation('com.example.Mockable')
}
Annotate the classes that you want to mock:
#Mockable
class Foo {
fun calculateTheFoo(): Int {
sleep(1_000) // Difficult things here
return 1
}
}
If you want more information you can read my blog post where I explain this with more details: Mocking Kotlin classes with Mockito — the fast way

Try adding this below dependency to your build.gradle.
testImplementation 'org.mockito:mockito-inline:2.8.47'
Replace with your mockito version instead of 2.8.47. This will help you to avoid using powermock for the issue.
please look into the below link to know how this thing works.
How to mock a final class with mockito

You may use Powermock for this, for example:
import static org.powermock.api.mockito.PowerMockito.mock;
import static org.powermock.api.mockito.PowerMockito.spy;
import static org.powermock.api.mockito.PowerMockito.when;
#RunWith(RobolectricTestRunner.class)
#Config(constants = BuildConfig.class, sdk = 21)
#PowerMockIgnore({ "org.mockito.*", "org.robolectric.*", "android.*" })
#PrepareForTest({FinalClass1.class, FinalClass2.class})
public class Test {
#Rule
public PowerMockRule rule = new PowerMockRule();
... // your code here
}

Let us program to interfaces, not implementations. You can extract an interface, use it in your code, and mock it. For example, the following will not work:
import com.nhaarman.mockito_kotlin.mock
class MyFinalClass {...}
(snip)
private val MyFinalClass = mock()
So let us extract an interface:
class MyFinalClass : MyInterface {...}
(snip)
private val MyInterface = mock()

Because in kotlin all classes are final by default.
You should also consider adding open to the class declaration.
Example: open class MyClasss{}

Related

How cglib fixing the mock issue in spock

I am having below class
class TestClientSpec extends Specification {
TestClient testClient
RestTemplate restTemplate
def setup() {
restTemplate = Mock()
testClient = new TestClient(restTemplate)
}
def 'successful'() {
given:
def url = 'https://test123'
when:
testClient.getValue(url)
then:
1 * restTemplate.getForEntity(url, Test.class)
}
}
when I try to run this test got below error
Cannot create mock for class org.springframework.web.client.RestTemplate. Mocking of non-interface types requires a code generation library. Please put an up-to-date version of byte-buddy or cglib-nodep on the class path.
org.spockframework.mock.CannotCreateMockException: Cannot create mock for class org.springframework.web.client.RestTemplate. Mocking of non-interface types requires a code generation library. Please put an up-to-date version of byte-buddy or cglib-nodep on the class path.
After that I have added cglib dependnecy and it's working fine
implementation group: 'cglib', name: 'cglib-nodep', version: '3.3.0'
But not sure why it's throws the above error and how cglib fixing this?
Spock uses a bytecode generator lib, either cglib or byte-buddy, to generate classes at runtime in order to be able to mock classes.
The dependencies on these libs are optional, so that you can choose whether to use them or not, as you might as well use some dedicated mock libraries for all your mocking needs (e.g. PowerMock).
How adding a bytecode lib to the classpath can fix this?
Well, by enabling the necessary code in Spock... in case of Cglib, CglibMockFactory.
Spock seems to find out which lib is available to use for mocking at MockInstantiator... if you know Java reflection, doing that kind of thing is not hard, just do something like Class.forName("org.objenesis.Objenesis") and if that doesn't throw ClassNotFoundException you can use that.

Mock Abstract Class in EasyMock

I am using EasyMock 2.4 and can't upgrade to latest version due to dependencies.
I need to mock abstract class but not able to do it with createMock method.
It throws an error that class is not an interface.
Can anyone help me in solving this problem?
There is an abstract class called ClassA (I can't modify this class):
public abstract class ClassA {
}
There is another MyTest class which mocks ClassA:
public class MyTest {
private ClassA mockClassA;
#Before
public void setup() {
mockClassA = createMock(ClassA.class); //Line number: 28
}
}
while running this it throws below exception at createMock call:
java.lang.IllegalArgumentException: ClassA is not an interface
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:590)
at java.lang.reflect.Proxy$ProxyClassFactory.apply(Proxy.java:557)
at java.lang.reflect.WeakCache$Factory.get(WeakCache.java:230)
at java.lang.reflect.WeakCache.get(WeakCache.java:127)
at java.lang.reflect.Proxy.getProxyClass0(Proxy.java:419)
at java.lang.reflect.Proxy.newProxyInstance(Proxy.java:719)
at org.easymock.internal.JavaProxyFactory.createProxy(JavaProxyFactory.java:13)
at org.easymock.internal.MocksControl.createMock(MocksControl.java:40)
at org.easymock.EasyMock.createMock(EasyMock.java:60)
at mypackage.MyTest.setup(MyTest.java:28)
EasyMock prior to v3.0 was using Java proxies mechanism to create mocks. This mechanism is capable only of creating proxies for interfaces, so there is no way you can mock a class (abstract class) with easy mock without upgrading to v3.0 at least.
You have the following options:
Upgrade EasyMock to v3.0+ (what prevents your from?)
Use other mocking library in parallel with EasyMock (e.g. Mockito)
Create your own subclass of ClassA in test and override methods there for testing. But this one is clearly a workaround that may not provide you with enough flexibility.
Actually what do you expect from your mock? (E.g. to stub some method calls, or to do some method call verifications, other...)
EasyMock Class extension worked to create mock object for class or interface. I used import static org.easymock.classextension.EasyMock.*; instead of import static org.easymock.EasyMock.*;
Yes. Prior to EasyMock 3, you need the class extension to mock classes. However, latest EasyMock versions have not much dependencies (only Objenesis in fact).
Which one is blocking you?

Using PowerMock with Cucumber

I've written a JUnit test that uses Mockito and PowerMock to mock some classes. I'm trying to convert it a Cucumber test, but the static PowerMock features don't work.
Extracts of the two relevant Cucumber classes:
Runner
#RunWith(Cucumber.class)
public class JWTValidatorBDDTest {
}
Steps Class
public class JWTValidatorCukeTest {
String tokenValue;
JWTValidator jwtValidator;
MockHttpServletRequest mockRequest;
#Before
public void before() throws IOException {
this.mockRequest = new MockHttpServletRequest();
PowerMockito.mockStatic(JWTAuthConnectionManager.class);
BDDMockito.given(JWTAuthConnectionManager.postToken(anyString(), anyString(), anyString())).willReturn(200);
Mockito.doReturn(200).when(JWTAuthConnectionManager.postToken(anyString(), anyString(), anyString()));
}
#Given("^a JWT token with the value (.*)")
public void a_JWT_token_with_the_value_(String token) {
this.jwtValidator = new JWTValidator("https://test.7uj67hgfh.com/openam", "Authorization", "Bearer");
this.tokenValue = token;
}
Whilst this code works within the JUnit test, it fails here - it enters the JWTAuthConnectionManager.postToken() method that should be mocked and then fails by executing code within there. I've tried adding the lines:
#RunWith(PowerMockRunner.class)
#PrepareForTest(JWTAuthConnectionManager.class)
to both of the above classes (although of course I can't use RunWith in the Runner class as it already has one RunWith annotation), but this doesn't change anything.
How do I get PowerMock to work within Cucumber?
Seems like it is possible now with #PowerMockRunnerDelegate annotation. I use #RunWith(PowerMockRunner.class) and #PowerMockRunnerDelegate(Cucumber.class) and it's working. Taken an advise from here: https://medium.com/#WZNote/how-to-make-spock-and-powermock-work-together-a1889e9c5692
Since version 1.6.0 PowerMock has support for delegating the test execution to another JUnit runner without using a JUnit Rule. This leaves the actual test-execution to another runner of your choice. For example tests can delegate to “SpringJUnit4ClassRunner”, “Parameterized” or the “Enclosed” runner.
There are also options of using #Rule: PowerMockRule rule = new PowerMockRule(); instead of #RunWith(PowerMockRunner.class) (so Runner can be something else) - but the comment by Stefan Birkner suggests that Cucumber runner should support rules to use this and I am not sure if it does (now).
Hope it helps someone.
You can't use the PowerMockRunner because a test can only have one runner (in your case Cucumber). But AFAIK you can use the PowerMockRule instead of the PowerMockRunner.

Mockito - NullpointerException when stubbing Method

So I started writing tests for our Java-Spring-project.
What I use is JUnit and Mockito. It's said, that when I use the when()...thenReturn() option I can mock services, without simulating them or so. So what I want to do is, to set:
when(classIwantToTest.object.get().methodWhichReturnsAList(input))thenReturn(ListcreatedInsideTheTestClass)
But no matter which when-clause I do, I always get a NullpointerException, which of course makes sense, because input is null.
Also when I try to mock another method from an object:
when(object.method()).thenReturn(true)
There I also get a Nullpointer, because the method needs a variable, which isn't set.
But I want to use when()..thenReturn() to get around creating this variable and so on. I just want to make sure, that if any class calls this method, then no matter what, just return true or the list above.
Is it a basically misunderstanding from my side, or is there something else wrong?
Code:
public class classIWantToTest implements classIWantToTestFacade{
#Autowired
private SomeService myService;
#Override
public Optional<OutputData> getInformations(final InputData inputData) {
final Optional<OutputData> data = myService.getListWithData(inputData);
if (data.isPresent()) {
final List<ItemData> allData = data.get().getItemDatas();
//do something with the data and allData
return data;
}
return Optional.absent();
}
}
And here is my test class:
public class Test {
private InputData inputdata;
private ClassUnderTest classUnderTest;
final List<ItemData> allData = new ArrayList<ItemData>();
#Mock
private DeliveryItemData item1;
#Mock
private DeliveryItemData item2;
#Mock
private SomeService myService;
#Before
public void setUp() throws Exception {
classUnderTest = new ClassUnderTest();
myService = mock(myService.class);
classUnderTest.setService(myService);
item1 = mock(DeliveryItemData.class);
item2 = mock(DeliveryItemData.class);
}
#Test
public void test_sort() {
createData();
when(myService.getListWithData(inputdata).get().getItemDatas());
when(item1.hasSomething()).thenReturn(true);
when(item2.hasSomething()).thenReturn(false);
}
public void createData() {
item1.setSomeValue("val");
item2.setSomeOtherValue("test");
item2.setSomeValue("val");
item2.setSomeOtherValue("value");
allData.add(item1);
allData.add(item2);
}
I had this issue and my problem was that I was calling my method with any() instead of anyInt(). So I had:
doAnswer(...).with(myMockObject).thisFuncTakesAnInt(any())
and I had to change it to:
doAnswer(...).with(myMockObject).thisFuncTakesAnInt(anyInt())
I have no idea why that produced a NullPointerException. Maybe this will help the next poor soul.
The default return value of methods you haven't stubbed yet is false for boolean methods, an empty collection or map for methods returning collections or maps and null otherwise.
This also applies to method calls within when(...). In you're example when(myService.getListWithData(inputData).get()) will cause a NullPointerException because myService.getListWithData(inputData) is null - it has not been stubbed before.
One option is create mocks for all intermediate return values and stub them before use. For example:
ListWithData listWithData = mock(ListWithData.class);
when(listWithData.get()).thenReturn(item1);
when(myService.getListWithData()).thenReturn(listWithData);
Or alternatively, you can specify a different default answer when creating a mock, to make methods return a new mock instead of null: RETURNS_DEEP_STUBS
SomeService myService = mock(SomeService.class, Mockito.RETURNS_DEEP_STUBS);
when(myService.getListWithData().get()).thenReturn(item1);
You should read the Javadoc of Mockito.RETURNS_DEEP_STUBS which explains this in more detail and also has some warnings about its usage.
I hope this helps. Just note that your example code seems to have more issues, such as missing assert or verify statements and calling setters on mocks (which does not have any effect).
I had the same problem and my issue was simply that I had not annotated the class properly using #RunWith. In your example, make sure that you have:
#RunWith(MockitoJUnitRunner.class)
public class Test {
...
Once I did that, the NullPointerExceptions disappeared.
For future readers, another cause for NPE when using mocks is forgetting to initialize the mocks like so:
#Mock
SomeMock someMock;
#InjectMocks
SomeService someService;
#Before
public void setup(){
MockitoAnnotations.initMocks(this); //without this you will get NPE
}
#Test
public void someTest(){
Mockito.when(someMock.someMethod()).thenReturn("some result");
// ...
}
Also make sure you are using JUnit for all annotations.
I once accidently created a test with #Test from testNG so the #Before didn't work with it (in testNG the annotation is #BeforeTest)
For me the reason I was getting NPE is that I was using Mockito.any() when mocking primitives. I found that by switching to using the correct variant from mockito gets rid of the errors.
For example, to mock a function that takes a primitive long as parameter, instead of using any(), you should be more specific and replace that with any(Long.class) or Mockito.anyLong().
Hope that helps someone.
As this is the closest I found to the issue I had, it's the first result that comes up and I didn't find an appropriate answer, I'll post the solution here for any future poor souls:
any() doesn't work where mocked class method uses a primitive parameter.
public Boolean getResult(String identifier, boolean switch)
The above will produce the same exact issue as OP.
Solution, just wrap it:
public Boolean getResult(String identifier, Boolean switch)
The latter solves the NPE.
keep in mind if you choose this approach, now you might want to include a nullcheck for Boolean in production code (credit: brought up by Ridcully)
Make sure you initialize your mocks.
JUnit4 use #Before
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
JUnit5 use #BeforeEach
#BeforeEach
public void setup() {
MockitoAnnotations.initMocks(this);
}
For JUnit5 check, you are using proper imports also.
import org.junit.runner.RunWith
import org.mockito.junit.MockitoJUnitRunner;
#RunWith(MockitoJUnitRunner.class)
Corner case:
If you're using Scala and you try to create an any matcher on a value class, you'll get an unhelpful NPE.
So given case class ValueClass(value: Int) extends AnyVal, what you want to do is ValueClass(anyInt) instead of any[ValueClass]
when(mock.someMethod(ValueClass(anyInt))).thenAnswer {
...
val v = ValueClass(invocation.getArguments()(0).asInstanceOf[Int])
...
}
This other SO question is more specifically about that, but you'd miss it when you don't know the issue is with value classes.
For JUnit 5 the test class has to be annotated with:
#ExtendWith(MockitoExtension.class)
imports:
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.junit.jupiter.MockitoExtension;
My issue was fixed with this addition.
you need to initialize MockitoAnnotations.initMocks(this) method has to called to initialize annotated fields.
#Before public void initMocks() {
MockitoAnnotations.initMocks(this);
}
for more details see Doc
Check that the method signature is not declared as final
This one catches out a lot of people who work on codebases which are subjected to Checkstyle and have internalised the need to mark members as final.
i.e. in the OP's example:
object.method()
Make sure that method() is not declared as final:
public final Object method() {
}
Mockito cannot mock a final method and this will come up as a wrapped NPE:
Suppressed: org.mockito.exceptions.misusing.InvalidUseOfMatchersException:
Buried deep in the error message is the following:
Also, this error might show up because you use argument matchers with methods that cannot be mocked.
Following methods *cannot* be stubbed/verified: final/private/equals()/hashCode().
Mocking methods declared on non-public parent classes is not supported.
None of the above answers helped me. I was struggling to understand why code works in Java but not in Kotlin.
Then I figured it out from this thread.
You have to make class and member functions open, otherwise NPE was being thrown.
After making function open tests started to pass.
You might as well consider using compiler's "all-open" plugin:
Kotlin has classes and their members final by default, which makes it inconvenient to use frameworks and libraries such as Spring AOP that require classes to be open. The all-open compiler plugin adapts Kotlin to the requirements of those frameworks and makes classes annotated with a specific annotation and their members open without the explicit open keyword.
For me, it was because I was stubbing the mock in the #BeforeAll method.
MockitoExtension does not have a callback for #BeforeAll.
public class MockitoExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver
I moved the stubbing inside the test method it worked!!
In my case, it was the wrong import for when().
I used import static reactor.core.publisher.Mono.when by accident.
In my case, Intellij created Test with org.junit.jupiter.api.Test (Junit5) instead of import org.junit.Test of (Junit4) which caused all beans to be null apparently.
also, make sure the class and test method is public
In my case, my Mockito annotation didn't match the JUnit Version.
When using #ExtendWith(MockitoExtension.class) make sure you're using JUnit 5: import org.junit.jupiter.api.Test;
When using #RunWith(MockitoJUnitRunner.class) make sure you're using JUnit 4: import org.junit.Test;
In my case, I missed add first
PowerMockito.spy(ClassWhichNeedToBeStaticMocked.class);
so this can be helpful to somebody who see such error
java.lang.NullPointerException
at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.addAnswersForStubbing(PowerMockitoStubberImpl.java:67)
at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:42)
at org.powermock.api.mockito.internal.expectation.PowerMockitoStubberImpl.when(PowerMockitoStubberImpl.java:112)
#RunWith(MockitoJUnitRunner.class) //(OR) PowerMockRunner.class
#PrepareForTest({UpdateUtil.class,Log.class,SharedPreferences.class,SharedPreferences.Editor.class})
public class InstallationTest extends TestCase{
#Mock
Context mockContext;
#Mock
SharedPreferences mSharedPreferences;
#Mock
SharedPreferences.Editor mSharedPreferenceEdtor;
#Before
public void setUp() throws Exception
{
// mockContext = Mockito.mock(Context.class);
// mSharedPreferences = Mockito.mock(SharedPreferences.class);
// mSharedPreferenceEdtor = Mockito.mock(SharedPreferences.Editor.class);
when(mockContext.getSharedPreferences(Mockito.anyString(),Mockito.anyInt())).thenReturn(mSharedPreferences);
when(mSharedPreferences.edit()).thenReturn(mSharedPreferenceEdtor);
when(mSharedPreferenceEdtor.remove(Mockito.anyString())).thenReturn(mSharedPreferenceEdtor);
when(mSharedPreferenceEdtor.putString(Mockito.anyString(),Mockito.anyString())).thenReturn(mSharedPreferenceEdtor);
}
#Test
public void deletePreferencesTest() throws Exception {
}
}
All the above commented codes are not required
{ mockContext = Mockito.mock(Context.class); },
if you use #Mock Annotation to Context mockContext;
#Mock
Context mockContext;
But it will work if you use #RunWith(MockitoJUnitRunner.class) only. As per Mockito you can create mock object by either using #Mock or Mockito.mock(Context.class); ,
I got NullpointerException because of using #RunWith(PowerMockRunner.class), instead of that I changed to #RunWith(MockitoJUnitRunner.class) it works fine
Well in my case it was because of wrong annotation usage. I was using junit 4 for testing and used #BeforeEach instead of #Before while initializing.
Changed it to #Before and it works like charm.
This is where google took me when I had the same NullPointerException with Junit 5, but was correctly using #ExtendWith(MockitoExtension.class) in my maven project.
Turns out I hadn't included the maven-surefire-plugin in my pom.xml and that meant the #ExtendWith wasn't actually doing anything!
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
...
I was using wrong annotation/import of Mock, so my object was not getting formed.
I was using org.evosuite.shaded.org.mockito.Mock and I switched back to org.mockito.Mock. Then as if by magic, it started working for me.
Ed Webb's answer helped in my case. And instead, you can also try add
#Rule public Mocks mocks = new Mocks(this);
if you #RunWith(JUnit4.class).
None of these answers worked for me. This answer doesn't solve OP's issue but since this post is the only one that shows up on googling this issue, I'm sharing my answer here.
I came across this issue while writing unit tests for Android. The issue was that the activity that I was testing extended AppCompatActivity instead of Activity. To fix this, I was able to just replace AppCompatActivity with Activity since I didn't really need it. This might not be a viable solution for everyone, but hopefully knowing the root cause will help someone.
When using JUnit 5 or above. You have to inject the class annotated with #Mock
in an #BeforeEach setup.
In my case it was due to wrong import of the #Test annotation
Make sure you are using the following import
import org.junit.jupiter.api.Test;
Annotate the test class with: #ExtendWith(MockitoExtension.class).
In my case a tested method called another method as a parameter:
Mockito.`when`(repository.getItems(prefs.getUser().id)).thenReturn(listOf())`
While repository is mocked, prefs.getUser().id) will throw NPE. So, first we should mock a parameter, for instance,
Mockito.`when`(prefs.getUser()).thenReturn(User(id = 1, name = "user"))`
Also we should mock prefs. I didn't check it and changed a library, sorry.
I was trying to mock a "final" method, which apparently was the problem.
The right way to handle this would be to use an interface and mock that interface however I couldn't control the library where the "final" method was.
Mockito 2 can handle mocking final method. Add a text file to the project's src/test/resources/mockito-extensions directory named org.mockito.plugins.MockMaker and add a single line of text:
mock-maker-inline
After that, mocking the final method should work just fine.
Check which version of Junit you are using. In the Maven/Gradle build tool, if you set to use testRuntimeOnly 'junit5',then it might not take #RunWith since it is not available and it is replaced with #ExtendWith in Junit5.
This doesnt answer the OP's original query, but its here to try help others with Mockito null pointer exceptions (NPE).
My NPE was happening as I did not explicitly set the class under tests' dependencies to be the classes I had mocked. So the class under test was unable to find its required dependencies, creating the NPE. I tend to not mock the class under test (i.e. use new keyword), to ensure im getting my native class behaviour for testing.
Im still using Junit 4 for reasons outside my control. Worked example;
ClassUnderTest
public class ClassUnderTest {
private DependantClassOne dependantClassOne;
private DependantClassTwo dependantClassTwo;
// remaining class, including setters
}
Test Class
#RunWith(MockitoJUnitRunner.class)
public class Test {
private ClassUnderTest classUnderTest;
private DependantClassOne dependantClassOne;
private DependantClassTwo dependantClassTwo;
#Before
public void setup() {
dependantClassOne = mock(DependantClassOne.class);
dependantClassTwo = mock(DependantClassTwo.class);
classUnderTest = new ClassUnderTest();
classUnderTest.setDependantClassOne(dependantClassOne); //added to prevent NPE
classUnderTest.setDependantClassTwo(dependantClassTwo); //added to prevent NPE
}
// tests
}

Declaring Jmockit mock parameters on #BeforeMethod of TestNG

I've been testing my code behavior using TestNG and JMockit for a while now and I have had no specific issue with their combination. Today I came across a situation where I needed to mock one of my internal dependencies, in the so called, type wide manner and I did not need to keep that mock around since none of the test cases dealt with it directly while they counted on the mocked version functionality. So, naturally, I put the mocking logic in my #BeforeMethod. Here is a sample:
public class SampleTest
{
#Mocked
#Cascading
private InnerDependency dependency;
#BeforeMethod
public void beforeMethod()
{
new NonStrictExpectations()
{
{
dependency.getOutputStream((String)any);
result = new Delegate<OutputStream>()
{
public OutputStream getOutputStream(String url)
{
return null;
}
};
}
};
}
#Test
public void testNormalOperation()
{
// The test whose desired behavior depends on dependency being mocked out
// ..
}
}
But, since my tests do not care about the mocked dependency explicitly, I'm not willing to declare it as a test class field, unlike what is done above. To my knowledge of JMockit The only options remaining would be:
Declare dependency as a local mock field:
new NonStrictExpectations()
{
#Cascading
private InnerDependency dependency;
{
//...
}
}
Declare dependency as an input argument for beforeMethod(), similar to what is done for normal #Test methods:
#BeforeMethod
public void beforeMethod(#Mocked #Cascading final InnerDependency dependency)
{
// ...
}
I see that JMockit 1.6+ would not like the first option and warns with WARNING: Local mock field "dependency" should be moved to the test class or converted to a parameter of the test method. Hence, to keep everyone happy, I'm ruling this option out.
But for the second option, TestNG (currently 6.8.6) throws exception when running the test saying java.lang.IllegalArgumentException: wrong number of arguments. I don't see this behavior with normal #Test cases passed with #Mocked parameters. Even playing with #Parameter and #Optional will not help (and should not have!).
So, is there any way I could make this work without declaring the unneccessary test class mock field, or am I missing something here?
Thanks
Only test methods (annotated with #Test in JUnit or TestNG) support mock parameters, so the only choice here is to declare a mock field at the test class level.
Even if not used in any test method, I think it's better than having it declared in a setup method (using #Before, #BeforeMethod, etc.). If it were to be possible, the mock would still have to apply to all tests, because of the nature of setup methods; having a mock field of the test class makes it clear what the scope of the mock is.
Dynamic partial mocking is one more technique to specify #Mocked dependencies locally. However, it has it's limitations (see comments below).

Categories