Can Spock Mock a Java constructor - java

Trying to broaden the appeal of Spock at work and run into this issue. Actually trying to write Unit Tests for a Groovy class, but one that calls out to Java. A static method calls a private constructor. The code looks like:
private MyConfigurator(String zkConnectionString){
solrZkClient = new SolrZkClient(zkConnectionString, 30000, 30000,
new OnReconnect() {
#Override
public void command() { . . . }
});
}
"SolrZkClient" is from third party (Apache) Java library. Since it tries to connect to ZooKeeper, I would like to mock that out for this Unit Test (rather than running one internally as part of the unit test).
My test gets to the constructor without difficulty, but I can't get past that ctor:
def 'my test'() {
when:
MyConfigurator.staticMethodName('hostName:2181')
then:
// assertions
}
Is there anyway to do this?

Since the class under test is written in Groovy, you should be able to mock the constructor call by way of a global Groovy Mock/Stub/Spy (see Mocking Constructors in the Spock Reference Documentation). However, a better solution is to decouple the implementation of the MyConfigurator class, in order to make it more testable. For example, you could add a second constructor and/or static method that allows to pass an instance of SolrZkClient (or a base interface, if there is one). Then you can easily pass in a mock.

You can use GroovySpy for mocking constructors in Spock
For example:
def 'my test'() {
given:
def solrZkClient = GroovySpy(SolrZkClient.class,global: true);
when:
MyConfigurator.staticMethodName('hostName:2181')
then:
// assertions
}

def anySubscriber = GroovySpy(RealSubscriber, global: true)
1 * new RealSubscriber("Fred")
put that into def setup(){ } block or inside of given: label block

Related

Can an instance of a class be created for testing without calling the constructor?

Is it possible to instantiate a class with a complex constructor without mocking or calling its constructor? This would be useful because the unit test class shouldn't need to change every time a new dependency was added to the service.
Mocking the service solves the problem by creating a new implementation of the class, with empty method overrides, but this isn't an instance of the implementation. This is a problem because any time a method in the mocked service is called, Mockito has to be told to call the real method. Instead, an actual implementation of the service would be preferred.
For example:
class ComplexService {
private Service service1;
private Service service2;
private Service service3;
private Service service4;
private Service service5;
ComplexConstructor(Service service1, Service service2, Service service3, Service service4, Service service5) {
this.service1 = service1;
this.service2 = service2;
this.service3 = service3;
this.service4 = service4;
this.service5 = service5;
}
boolean methodToTest() {
return service1.get();
}
}
In the unit test class, is it possible to have an instantiation of a implementation without having to call its constructor?
public class ComplexConostructorTest {
private ComplexConstructor complexConstructor;
private Service serviceMock;
#Before
public void init() {
/*
Somehow create implementation of complexConstructor
without calling constructor
. . .
*/
// Mock dependency
ReflectionTestUtils.setField(complexConstructor,
"service1",
serviceMock = Mockito.mock(Service.class));
}
#Test
public void service1Test() {
when(serviceMock.get())
.thenReturn(true);
assertTrue(complexConstructor.methodToTest());
}
}
Edit
It is possible using reflection, I was hoping there was a built in way in JUnit or Mockito to achieve the same thing. Here is how to do it using reflection.
#Before
public void init() {
Constructor<?> constructor = ComplexConstructor.class.getConstructors()[0];
complexConstructor = (ComplexConstructor) constructor.newInstance(new Object[constructor.getParameterCount()]);
// Mock dependency
ReflectionTestUtils.setField(complexConstructor,
"service1",
serviceMock = Mockito.mock(Service.class));
}
This is not possible, as far as I know, but you can simply add a simpler Constructor to your class and use that for testing. On the other hand, if the test tests the object in a state that is different to what it will be in the app, I'm not sure how good such a test would be.
You can, but you probably don't want to:
ComplexConstructor partialMock =
Mockito.mock(ComplexConstructor.class, CALLS_REAL_METHODS);
This "partial mock" instance will not have its constructor or field initializers called, but all calls to the system under test will invoke the class's real behavior. (Technically the class will also have its equals and hashCode overridden for Mockito's purposes, and the class will be a generated subclass of ComplexConstructor instead of ComplexConstructor itself.)
In this way, you can be insulated from the constructor, but because you are suppressing an arbitrary subset of the class-under-test's behavior it is much more difficult to determine exactly what you're testing in order to be confident the system works because the test passes. That should be your main goal in testing, and it may be difficult to achieve that with partial mocks. Colleagues or collaborators may rightly observe that you shouldn't mock your system under test for exactly this reason.
Though personally I don't think it's wrong or unexpected for you to need to change your unit tests to supply mocks when needed, you could create a factory separate from your test that supplies testing instances of ComplexConstructor, or consider using a dependency injection framework that automatically supplies mocks to your system under test.
Looks like you are mixing up several terms and concepts. Let me help you understand them better.
This would be useful because the unit test class shouldn't need to change every time a new dependency was added to the service.
Your class has a number of dependencies, that are provided via the constructor. If you are writing unit test your aim is only to test this dependent class, all dependencies should be mocked. That's why it is called unit testing. This means that with every new dependency of your class the test should be updated by adding new mock and its mocked behaviour.
Mockito has to be told to call the real method. Instead, an actual implementation of the service would be preferred.
Consider integration tests, in this case, you can mock only some amount of dependencies while others will work as intended or "real" until you mock them of course. However, if you want just to avoid supporting tests then it is not the right approach.
Please don't try to hack your tested class from your test by reflection. This can lead to wrong test results, waste of time and overall disappointment :) Mocking libraries like PowerMock and JMockit provide any kind of hacks, like ones you've tried to implement yourself and are in general too powerful.
With Great Power Comes Great Responsibility
You can create helper methods as part of your test code, for example some factory methods with descriptive names that construct the object. For example, make_default_ComplexService and more the like, just as needed by your tests. The tests could then use these methods, and if the constructor changes, you will in many cases only have to update the helper methods and not all of the tests. This approach is generic enough to also separate your test from drastic changes like turning the "constructor with parameters" approach into the "non-argument constructor with lots of setters" approach.
This approach will reduce maintenance effort for your tests, you will still use the original constructor (because it is called by the factory) rather than some fake constructor, and your test code might even be more readable than with the direct constructor's calls if the factory methods' names are well chosen.
You could add a parameterless constructor to your class and create setter for all your fields.
class ComplexService {
private Service service1;
private Service service2;
private Service service3;
private Service service4;
private Service service5;
public ComplexService(){
super();
}
...
public void setService1(Service service1){
this.service1 = service1;
}
//for other fields too
...
}
In your test you call it like:
myService = new ComplexService()
myService.setService1(service1);
...

Spock bug with testing private method?

Can somebody tell me if this is a bug or intended behavior.
I know in Spock I can test private methods:
def "test with private"() {
given:
FileContentValidator fileContentValidator = new FileContentValidator(1)
when:
fileContentValidator.validateCustomerSiteId("") // this is a private method
then:
true // succeeds
}
But when I try the same thing using a Spock Spy, it fails:
def "test with private on spy"() {
given:
FileContentValidator fileContentValidator = Spy(FileContentValidator, constructorArgs: [1])
when:
fileContentValidator.validateCustomerSiteId("") // this is a private method
then:
true // does not get here
}
I get an exception:
groovy.lang.MissingMethodException: No signature of method: com.shoppertrak.device.management.web.validator.ophour.FileContentValidator$$EnhancerByCGLIB$$7ff6a42.validateCustomerSiteId() is applicable for argument types: (java.lang.String) values: []
I think this is due to how the cglib works. When testing an existing concrete class, Spock doesn't get involved in the byte code, so you're taking advantage of a flaw in Groovy that give you access to private methods. When you spy or mock the same class, the Spock/cglib manipulation steps in and changes the resulting byte code. The end product is a method that is truly private, thus you can't access it.
There are probably hacks you can use to get around it, but you're probably better off adding something like a CustomerSiteIdValidator class with a public validateCustomerSiteId() that gets injected into your FileContentValidator class. Then you can easily mock it and isolate responsibilities.
Someone suggested adding the ability to Spy private methods, but the ticket was closed as Won't Fix
https://github.com/spockframework/spock/issues/403

Cannot Mockito spy class in Spock test?

I'm trying to spy a Java class in a Spock test. I've not had a problem with Mockito in Spock/Groovy before.
When I try to do the following:
def service = spy(Service.class)
I get the following error:
org.mockito.exceptions.base.MockitoException:
Cannot mock/spy class java.lang.Class
Mockito cannot mock/spy following:
- final classes
- anonymous classes
- primitive types
When I do mock(Service.class) though, it works fine.
I have confirmed that the class is not final, anonymous, or primitive.
Any ideas? I have a random generator in the class (ak) so I need to spy not mock.
Thank you
Mockito doesn't spy on Classes (or Mocks), it spies on (regular) Objects. Thus, instead of
def service = spy(Service.class)
you have to write
def service = spy(new Service())
(or whichever constructor is appropriate for your scenario).
Just an extra to Ray's answer, you can also spy on already created instances. The only thing is that all methods you called before spy() was called, cannot be verified. Like:
ClassToSpy spiedInstance;
#Before
public void before () {
spiedInstance = new ClassToSpy();
}
#Test
public void testWillFail() {
spiedInstance.method();
spiedInstance = spy(spiedInstance);
verify(spiedInstance).method();
}
#Test
public void testWillPass() {
spiedInstance = spy(spiedInstance);
spiedInstance.method();
verify(spiedInstance).method();
}
The error you would get from testWillFail would be similar to:
Wanted but not invoked:
classToSpy.method();
-> at your.package.testWillFail(TestClass.java:line)
Actually, there were zero interactions with this mock.
Due to the fact that, after spied, you did not call this method.

Unit test fails because the service is offline

I have a unit test that fails because it indirectly calls a method which is dependent upon a service. But when unit tests are run, the service is offline. I tried using Mockito for mocking the behavior of this service dependent method, but the problem is that this method is a static method in a final class, so Mockito does not work in this case.
I also tried using PowerMock with Mockito, but again as the method is not called directly from the unit test, it does not work. This is the skeleton of my unit test:
#RunWith(PowerMockRunner.class)
#PrepareForTest(FinalClassWithStaticMethod.class)
public class MyObjTestCase {
#Test public void myRandomTest() throws Exception {
PowerMockito.mockStatic(FinalClassWithStaticMethod.class);
MyObj returnObj = new MyObj();
// setup fields for returnObj
...
...
PowerMockito.when(FinalClassWithStaticMethod.staticMethod((AnotherObj)anyObject())).thenReturn(returnObj);
AnotherObj obj = new AnotherObj();
// setup fields for obj...
MyObj mockedObj = FinalClassWithStaticMethod.staticMethod(obj); // This returns the mocked value.
// TestUtil.staticMethod calls another class' method which calls FinalClassWithStaticMethod.staticMethod.
MyObj myObj = TestUtil.staticMethod(obj); // This does not return mocked value.
}
}
My questions:
Are the unit tests even meant for such scenarios?
Is there a way by which I can get this unit test to work without modifying the final class? In case I do have to modify the existing classes, what is the correct way of doing it by minimal effects on the dependent code? Although it is a specific scenario, links to examples that exhibit such refactoring will be great help.
In your case, you should ideally be mocking the service object. The mocking will only work if the method that is calling the service is using the mocked object. If the client for the service can be created and passed to the class calling the service directly from the unit test (setter/constructor) when the object is created in the unit test, then you don't have to make too many changes.
Sample code:
class CallsService {
public CallsService(final ServiceClient client) {
... }
public someMethod() {
client.callService();
}
}
In unit test:
void test() {
ServiceClient mockedClient = mock(ServiceClient.class);
// Setup mocks to return as required
CallsService caller = new CallsService(mockedClient);
}
This way the caller will use the mockedClient in the unit test, and in the actual program it can get a client to the real service.

Mockito: Mocking "Blackbox" Dependencies

So I have been asked to read up on mocking and BDD for our development team and play around with mocks so as to improve a handful of our existing unit tests (as an experiment).
I have ultimately chosen to go with Mockito for a number of reasons (some outside the scope of my control), but namely because it supports both stubbing and mocking for instances when mocking would not be appropriate.
I have spent all day learning about Mockito, mocking (in general) and BDD. And now I am ready to dig in and start augmenting our unit tests.
So we have a class called WebAdaptor that has a run() method:
public class WebAdaptor {
private Subscriber subscriber;
public void run() {
subscriber = new Subscriber();
subscriber.init();
}
}
Please note: I do not have a way to modify this code (for reasons outside the scope of this question!). Thus I do not have the ability to add a setter method for Subscriber, and thus it can be thought of as an unreachable "blackbox" inside of my WebAdaptor.
I want to write a unit test which incorporates a Mockito mock, and uses that mock to verify that executing WebAdaptor::run() causes Subscriber::init() to be called.
So here's what I've got so far (inside WebAdaptorUnitTest):
#Test
public void runShouldInvokeSubscriberInit() {
// Given
Subscriber mockSubscriber = mock(Subscriber.class);
WebAdaptor adaptor = new WebAdaptor();
// When
adaptor.run();
// Then
verify(mockSubscriber).init();
}
When I run this test, the actual Subscriber::init() method gets executed (I can tell from the console output and seeing files being generated on my local system), not the mockSubscriber, which shouldn't do (or return) anything.
I have checked and re-checked: init is public, is neither static or final, and it returns void. According to the docs, Mockito should have no problem mocking this object.
So it got me thinking: do I need to explictly associate the mockSubscriber with the adaptor? If this is a case, then ordinarily, the following would normally fix it:
adaptor.setSubscriber(mockSubscriber);
But since I cannot add any such setter (please read my note above), I'm at a loss as to how I could force such an association. So, several very-closely-related questions:
Can anyone confirm that I've set the test up correctly (using the Mockito API)?
Is my suspicion about the missing setter correct? (Do I need to associate these objects via a setter?)
If my above suspicion is true, and I can't modify WebAdaptor, are there any circumventions at my dispose?
Thanks in advance!
You need to inject the mock into the class which you are testing. You do not need access to Subscriber. The way mockito and other mocking frameworks help is that you do not need access to objects which you are interacting with. You do however need a way to get mock objects into the class you are testing.
public class WebAdaptor {
public WebAdaptor(Subscriber subscriber) { /* Added a new constructor */
this.subscriber = subscriber;
}
private Subscriber subscriber;
public void run() {
subscriber.init();
}
}
Now you can verify your interactions on the mock, rather than on the real object.
#Test
public void runShouldInvokeSubscriberInit() {
// Given
Subscriber mockSubscriber = mock(Subscriber.class);
WebAdaptor adaptor = new WebAdaptor(mockSubscriber); // Use the new constructor
// When
adaptor.run();
// Then
verify(mockSubscriber).init();
}
If adding the Subscriber to the constructor is not the correct approach, you could also consider using a factory to allow WebAdaptor to instantiate new Subscriber objects from a factory which you control. You could then mock the factory to provider mock Subscribers.
If you don't want to change the production code and still be able to mock the functionality of the Subscriber class you should have a look at PowerMock. It works fine together with Mockito and allows you to mock the creation of new objects.
Subscriber mockSubscriber = mock(Subscriber.class);
whenNew(Subscriber.class).withNoArguments().thenReturn(mockSubscriber);
Further details are explained in the documentation for the PowerMock framework.
There is a way to inject your mock into the class under test without making any modifications to the code. This can be done using the Mockito WhiteBox. This is a very good feature that can be used to inject the dependencies of your Class Under Test from your tests. Following is a simple example on how it works,
#Mock
Subscriber mockSubscriber;
WebAdaptor cut = new WebAdaptor();
#Before
public void setup(){
//sets the internal state of the field in the class under test even if it is private
MockitoAnnotations.initMocks(this);
//Now the whitebox functionality injects the dependent object - mockSubscriber
//into the object which depends on it - cut
Whitebox.setInternalState(cut, "subscriber", mockSubscriber);
}
#Test
public void runShouldInvokeSubscriberInit() {
cut.run();
verify(mockSubscriber).init();
}
Hope this helps :-)
You could have used PowerMock to mock the constructor call without changing the original code:
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)
#PrepareForTest(WebAdaptor.class)
public class WebAdaptorTest {
#Test
public void testRunCallsSubscriberInit() {
final Subscriber subscriber = mock(Subscriber.class);
whenNew(Subscriber.class).withNoArguments().thenReturn(subscriber);
new WebAdaptor().run();
verify(subscriber).init();
}
}
You cannot mock the Subscriber using Mockito in your current implementation.
The problem you have is that the Subscriber is constructed and then immediately accessed, Mockito has no ability to replace (or spy) the Subscriber instance after creation but before the init method is called.
public void run() {
subscriber = new Subscriber();
// Mockito would need to jump in here
subscriber.init();
}
David V's answer solves this by adding the Subscriber to the constructor. An alternative that retains the hidden Subscriber construction would be to instantiate the Subscriber in a WebAdapter no-arg constructor and then use reflection to replace that instance before calling the run method.
Your WebAdapter would look like this,
public class WebAdaptor {
private Subscriber subscriber;
public WebAdaptor() {
subscriber = new Subscriber();
}
public void run() {
subscriber.init();
}
}
And you could use ReflectionTestUtils from Springframework's test module to inject dependencies into that private field.
#Test
public void runShouldInvokeSubscriberInit() {
// Given
Subscriber mockSubscriber = mock(Subscriber.class);
WebAdaptor adaptor = new WebAdaptor();
ReflectionTestUtils.setField( adaptor "subscriber", mockSubscriber );
// When
adaptor.run(); // This will call mockSubscriber.init()
// Then
verify(mockSubscriber).init();
}
ReflectionTestUtils is really just a wrapper about Java's reflection, the same could be achieved manually (and much more verbosely) without the Spring dependency.
Mockito's WhiteBox (as Bala suggests) would work here in place of ReflectionTestUtils, it is contained within Mockito's internal package so I shy away from it, YMMV.

Categories

Resources