How to create Spock Spy with private property - java

I have following Java class:
public class FooServiceImpl {
private BarService barService;
public String generateFoo() {
String barValue = barService.generateBar();
return customFoo() + barValue;
}
public String customFoo() {
return "abc";
}
}
And here is exemplary Spock test method:
def "generate foo bar"() {
setup:
def barService = Mock(BarService) {
generateBar() >> "Bar"
}
FooServiceImpl spyFooService =
Spy(FooServiceImpl, constructorArgs: [[barService: barService]])
spyFooService.customFoo() >> "foo"
when:
def fooValue = spyFooService.generateFoo()
then:
fooValue == "fooBar"
}
I try to create a Spy object for FooServiceImpl class but I get following error:
org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack:
No such property: barService for class:
com.foo.FooServiceImpl$$EnhancerByCGL`
I can't add a constructor to FooServiceImpl or setter for BarService, so I want to use map constructor. Is this possible?
Note: according to this issue it should work

The easiest solution in your case would be to make this field protected instead of private. When you create a spy object from a class, CGLIB is involved and it creates as subclass from the class you are trying to create spy from - com.foo.FooServiceImpl$$EnhancerByCGL in your case. The thing is that the field you are trying to modify is a private field and according to regular subclassing strategy in Java, private field does not get inherited in child class. That is why field barService does not exist in spy object
ATTENTION: IntelliJ's debugger may tell you that barService is present in this spyFromService instance, however this is IDE's bug - if you list all available fields from spyFromService.class.fields or spyFromService.class.declaredFields you wont find barService field here.
Another problem is that when CGLIB gets involved in object creation process it also gets involved if it comes to invoking methods. This is why adding dynamic fields to a class or instance via Groovy's metaprogramming features wont work. Otherwise you would be able to do things like:
spyFromService.metaClass.barService = barService
or
spyFromService.class.metaClass.barService = barService
Alternatively you could get rid of spy object and use a real instance in your test. Then
FooServiceImpl spyFromService = new FooServiceImpl()
spyFromService.#barService = barService
will work. However you won't be able to stub existing customFoo() method and you will have to rely on what its real implementation returns.

Related

Mockito.mock is not working, using Mockito.when causes NullPointerException

I have used Mockito very long time. With Mockito 4.7.0 version (and also for example with the version 3.12.4) the code:
A a = mock(A.class);
when(a.doX()).thenReturn("X");
causes the java.lang.NullPointerException in the second line.
When I print the content of the variable a
System.out.println(a);
I got also "java.lang.NullPointerException".
For any other class the Mockito.mock and Mockito.when are working perfectly and if I print a content of some other class instance b I got "Mock for B, hashcode: some hash code"
Do you know what could be problem? My example is simplified and I can't show the actual classes.
I don't have your code, but I tried to be the more generic as I can.
I have test like the one below and it works great.
Hope to been helpful.
#ExtendWith(MockitoExtension.class)
class MyTest {
#Mock
private AClass aclass;
#Mock
private BClass bclass;
#Mock
private CClass cclass;
#Test
void testExecute() {
when(aclass.getAbc(any(String.class), any(Date.class), anyString()))
.thenAnswer(i -> getAbc(i.getArgument(0), i.getArgument(1), i.getArgument(2)));
when(bclass.isOk())
.thenAnswer(i -> false);
when(cclass.getCdf(anyString()))
.thenAnswer(i -> getCdf());
...
...
...
}
private List<ABC> getAbc(final String myString, final Date now, final String x) {
...
...
...
}
private CDF getCdf() {
...
...
...
}
}
As you can see, for a simple answer I write the answer directly, for a more complex one, I create a private method with the same name and use it as answer.
Your code will still call the real method, unless A is an interface. I assume it is an implementation (a class) in your code.
A a = mock(A.class);
when(a.doX()).thenReturn("X");
// ^^^^^^^-- this is calling the real method
If you must use the class and cannot switch to an interface, you have do use Mockito.doReturn instead of Mockito.when:
A a = mock(A.class);
doReturn("X").when(a).doX();
But it is probably better to extract an interface and create mocks from the interface, not by subclassing your real class.

Mockito: Mocking different classes method in a specific class scope

I'd like to do this mocking with Mockito
MyServiceClass
(this isn't the actual code, just a fake example with a similar intent)
public String getClassObjects() {
OtherClassObject otherclass = OtherClassObject.createOtherClassObject();
String id = otherclass.getParentObject().getId();
return id;
}
So essentially I want to mock ".getId()" but only in the context of this class "MyServiceClass" if I call the same method of "getId()" in a different class I want to be able to mock a different return.
This will return "3" in every method call for the OtherClassObject
new MockUp<MyServiceClass>() {
#Mock
public String getId(){
return "3";
}
};
Is there a way to isolate method calls for a class object within the scope of a specific class?
Plain Mockito is unable to mock static calls, so you need PowerMock here. To achieve desired you should return different values from the mocked object like this
// from your example it's not clear the returned type from getParentObject method.
// I'll call it ParentObj during this example. Replace with actual type.
ParentObj poOne = mock(ParentObj.class);
when(poOne.getId()).thenReturn("3");
ParentObj poTwo = mock(ParentObj.class);
when(poTwo.getId()).thenReturn("10");
...
OtherClassObject otherClassObjectMock = mock(OtherClassObject.class);
// return all your stubbed instances in order
when(otherClassObjectMock.getParentObject()).thenReturn(poOne, poTwo);
PowerMockito.mockStatic(OtherClassObject.class);
when(OtherClassObject.createOtherClassObject()).thenReturn(otherClassObjectMock);
Thus, you can customize your mocks per needs, specifying desired return value, or propagating call to actual (real) method.
Don't forget to use annotations #RunWith(PowerMockRunner.class) and #PrepareForTest(OtherClassObject.class) on class level to activate the magic of PowerMock.
An alternative idea is to get rid of static call inside your getClassObjects method and pass factory using constructor, so you can easily mock it, setting mocked object only for single class.
Hope it helps!

Mocking helper class with Mockito

I have a plain helper class with public methods which I am using in the service level class. When I am writing test for the service class and trying to mock this helper class for one of the method it is going inside the methods and running every line. Since code inside this method is more complex I want to mock helper class with method(s) so that I don't have to take care of every detail inside helper class method.
Service Class
class HistoryServiceImpl implements CaseHistory {
#Override
public List<CaseHistoryDto> getCaseHistory(Individual member, Individual provider) {
MemberUtil memberUtil = new MemberUtil();
List<CaseHistoryDto> caseHistoryDtoList = new ArrayList<CaseHistoryDto>();
List<CaseHistory> caseHistoryList = caseDetailDao.fetchCaseHistory(member.getId(), provider.getId());
for(CaseHistory caseHistory : caseHistoryList) {
CaseHistoryDto caseHistoryDto = new CaseHistoryDto();
caseHistoryDto.setMemberInfo(memberUtil.getMemberInfo(member, caseHistory.getCreateDate()));
caseHistoryDtoList.add(caseHistoryDto);
}
return caseHistoryDtoList;
}
}
Test Class
Class HistoryServiceTest {
#Mock MemberUtil memberUtil;
#InjectMocks private HistoryServiceImpl historyServiceImpl = new HistoryServiceImpl();
#Test
public void testGetCaseHistory() {
//why this line going inside real method and executing all lines?
when(memberUtil.getMemberInfo(any(Individual.class), any(Date.class))).thenReturn(member);
}
}
The reason that your test case is running all the lines in the "real" method, is because your mock object is never being used anywhere.
As written, you cannot mock MemberUtil in your HistoryServiceImpl, because you are manually instantiating it in the getCaseHistory() method. You need to make getCaseHistory() get its MemberUtil from somewhere else, so that you can inject your mock version in your test class.
The simplest solution would be to define your MemberUtil as a member variable, so that the #InjectMocks annotation can override the default value:
class HistoryServiceImpl implements CaseHistory {
MemberUtil memberUtil = new MemberUtil();
#Override
public List<CaseHistoryDto> getCaseHistory(Individual member, Individual provider) {
...
}
}
Alternately you could have HistoryServiceImpl accept an externally provided MemberUtil, either in its constructor or via a setter method. You can then easily pass in a mocked version in your test class.
Generally, utility classes are stateless, so another possible solution would be to convert MemberUtil to make all of its methods static. Then you can use something like PowerMock to mock your static methods.

How should I use EasyMock's #Mock annotation (new in version 3.2)?

It looks like EasyMock version 3.2 now supports using annotations to setup mock objects. I am new to EasyMock (and Java in general) and am trying to understand how to use this. Do these annotations do something new or just provide an alternative way to do things? The documentation says:
Since EasyMock 3.2, it is now possible to create mocks using annotations. This is a nice
and shorter way to create your mocks and inject them to the tested class.
Here is the example above, now using annotations: ...
Then there is a listing that shows use of the #TestSubject and #Mock annotations, but I don't understand how it works. It seems as if it magically sets the private field of the class under test to the mock object. In most of my cases, I just want to make mock objects that return pre-defined values for use in JUnit test cases (don't currently care about verifying which ones were called, how many times they were called, etc). For example, for some tests I want to create a fake HttpServletRequest object like this:
public class SomeTest {
// Construct mock object for typical HTTP request for the URL below
private static final String REQUEST_URL = "http://www.example.com/path/to/file?query=1&b=2#some-fragment";
private static final Map<String, String> requestHeaderMap;
static {
Map<String, String> requestHeaders = new LinkedHashMap<String, String>();
requestHeaders.put("host", "www.example.com");
// ... (add any other desired headers here) ...
requestHeaderMap = Collections.unmodifiableMap(requestHeaders);
}
private HttpServletRequest httpServletRequest;
// ...
#Before
public void setUp() throws Exception {
httpServletRequest = createNiceMock(HttpServletRequest.class);
expect(httpServletRequest.getRequestURI()).andReturn(REQUEST_URL).anyTimes();
expect(httpServletRequest.getHeaderNames()).andReturn(Collections.enumeration(requestHeaderMap.keySet())).anyTimes();
capturedString = new Capture<String>();
expect(httpServletRequest.getHeader(capture(capturedString))).andAnswer(new IAnswer<String>() {
public String answer() throws Throwable {
String headerName = capturedString.getValue().toLowerCase();
if (requestHeaderMap.containsKey(headerName))
return requestHeaderMap.get(headerName);
else
return "";
}
}).anyTimes();
replay(httpServletRequest);
// ...
}
#Test
public void someMethod_givenAnHttpServletRequest_shouldDoSomething() {
// ...
}
}
Could I change the above code to use annotations? If so, should I? Under what circumstances?
I thought perhaps putting the #Mock annotation above an instance variable declaration would automatically take care of the createNiceMock(...) part, but this does not seem to work, so I suspect that I am misunderstanding something.
Examining their source code, they are using reflection to inject anything with an #Mock into a field of the #TestSubject. Their javadoc for the method
public static void injectMocks(final Object obj)
in EasyMockSupport.java says:
Inject a mock to every fields annotated with {#link Mock} on the class passed in parameter. Then, inject these mocks to the fields of every class annotated with TestSubject.
The rules are
Static and final fields are ignored
If a mock can be assigned to a field, do it. The same mock an be assigned more than once
If no mock can be assigned to a field, skip it silently
If two mocks can be assigned to the same field, return an error
Fields are searched recursively on the superclasses
Note: If the parameter extends EasyMockSupport, the mocks will be created using it to allow replayAll/verifyAll to work afterwards
#param obj the object on which to inject mocks
#since 3.2
public static void injectMocks(final Object obj) {
...
}
For you to use the #Mock annotation, you would need a #TestSubject that has an HttpServletRequest field for EasyMock to set the #Mock on (via reflection). The annotations are provided to make it a little easier to wire up a test, it let's you skip the createMock, and then calling the settter yourself.

Java: How do I mock a method of a field when that field isn't exposed?

I'm using Java 6, JUnit 4.8.1, and writing a console application. My application has a member field that isn't exposed …
public class MyApp {
...
private OpportunitiesService m_oppsSvc;
private void initServices() {
…
m_oppsSvc = new OpportunitiesServiceImpl(…);
}
...
}
I want to mock a behavior such that whenever one method from my service is called, (e.g. m_oppsSvc.getResults()), the same result is always returned. How do I do that? There's no setter method for the field. I'm currently working with Mockito 1.8.4. Is it possible to do this with Mockito or some other mock framework?
This is what you want:
#RunWith(MockitoJUnitRunner.class)
public class MyAppTest {
#Mock private OpportunitiesService mocked_m_oppsSvc;
#InjectMocks MyApp myApp;
#Test public void when_MyApp_uses_OpportunititesService_then_verify_something() {
// given
given( mocked_m_oppsSvc.whatever()).willReturn(...);
// when
myApp.isUsingTheOpportunitiesService(...);
// then
verify...
assertThat...
}
}
Using: Mockito 1.9.0, BDD style, FEST-Assert AssertJ.
Hope that helps :)
Given that you're already using mockito, why not just use reflection:
#RunWith(MockitoJUnitRunner.class)
public class MyApp {
#Mock
private OpportunitiesService m_oppsSvc;
private MyApp myApp;
#Before
public void before() throws Exception {
myApp = new MyApp();
Field f = MyApp.class.getDeclaredField("m_oppsSvc");
f.setAccessible(true);
f.set(myApp, m_oppsSvc);
}
}
It's a bit ugly, but it will do the trick. Note that this may not be the most efficient way to do it with Mockito, but it will work.
There's also Powermock which should allow you to do this as well using the Whitebox class. I won't get into the whole details of Powermock but here's the call to inject the private field value, which should be a mock object:
Whitebox.setInternalState(myApp, "m_oppsSvc", m_oppsSvc);
You should consider attempts to mock a private field a smell. That is, a sign that either what you're trying to do is either incorrect or that your code is currently structured incorrectly. You should only need to mock public methods or injected dependencies
In the code you've given you should consider injecting OpportunitiesService as follows:
public class MyApp {
...
private OpportunitiesService m_oppsSvc;
public MyApp(OpportunitiesService oppsSvc) {
this.m_oppsSvc = oppsSvc;
}
...
}
In your test you can then inject a mock as follows:
OpportunitiesService mockOpportunitiesService =
Mockito.mock(OpportunitiesService.class);
Mockit.when(mockOpportunitiesService.someMethod()).thenReturn(someValue);
MyApp app = new MyApp(mockOpportunitiesService);
You can easily do it with JMockit:
public class MyAppTest
{
#Tested MyApp myApp;
#Test
public testSomething(final #Capturing OpportunitiesService mockService)
{
new NonStrictExpectations() {{
mockService.getResults(); result = asList("a", "b", "C");
// record other expectations, if needed
}};
myApp.whateverMethodIWantToTest();
new Verifications() {{
mockService.doSomething(anyInt);
// verify other expectations, if needed
}};
}
}
Even though the implementation class OpportunitiesServiceImpl isn't mentioned in test code, its instances (any number of them) will still get properly mocked.
Generally you should use dependency injection and pass the mock object (of type OppportunitiesServiceImpl) in via the constructor, a separate setter or directly to the method (getResults). You might need to extract an interface for OpportunitiesServiceImpl first.
Usually, this is solved through the use of dependency injection. In regular (production) mode, your dependency injection container (e.g. Spring or Guice) will inject an instance of OpportunitiesService into MyApp through your constructor or through a setter.
Then, when you're testing you can "inject" a mock instance manually using the same setter or constructor argument.
Instead of doing
m_oppsSvc = new OpportunitiesServiceImpl(…);
Try Passing OpportunitesService in through MyApp's constructor

Categories

Resources