How to key off a parameter to a stubbed method using Mockito - java

Greetings.
I am mocking a search engine for testing in my web app. This search engine returns xml documents with different schemas. The schema depends on a parameter known as a collection set. Returning different schemas based on collection sets is the part that's difficult to mock, because specifying the collection set is basically a setup method, and a void one at that. This search engine is an external jar file so I can't modify the API. I have to work with what's been provided. Here's an example:
Engine engine = factory.getEngine();
Search search = engine.getSearch();
search.addCollectionSet(someCollectionSet);
SearchResult result = search.getSearchResult();
Document[] documents = result.getAllDocuments();
Then for each document, I can get the xml by calling:
document.getDocumentText();
When I'm using my mock objects, getDocumentText() returns an xml string, created by a generator, that conforms to the schema. What I want to do is use a different type of generator depending on which collection set was provided in step 3 in the first code snippet above. I've been trying to do something like this:
doAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
if (args == "foo") {
SearchResult result = getMockSearchResult();
when(search.getSearchResult()).thenReturn(result);
}
}
}).when(search.addCollectionSet(anyString()));
But this results in lots of red highlighting :)
Basically, my goal is to key off of addCollectionSet(someCollectionSet) so that when it's called, I can do some kind of switch off of the parameter and ensure that a different generator is used. Does anyone know how I can accomplish something like this? Or is there maybe some form of Dependency Injection that could be used to conditionally wire up my generator?
Thanks!
Update
I've changed my factory object so that it never returns the engine, but rather, the Search and Find objects from that engine, so now I can do something like this:
Search search = factory.getSearch(collectionSet);
So what I'd like to do is something like this:
when(factory.getSearch(anyString()).thenAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
switch(args[0]) {
case fooSet: return fooSearch; break;
case barSet: return barSearch; break;
In other words, I still want to key off the string that was passed in to getSearch in a switch statement. Admittedly, I could do something more like felix has suggested below, but I'd rather have all my cases wrapped in a switch. Can someone provide an example of how this could be done? Thanks!
Update
I've seen that you can capture the arguments that are passed into mocked calls, but these captured arguments are used for later assertions. I haven't seen a way that I can key off these arguments so that a call to my mock will return different values depending on the arguments. It seems like there has to be a way to do this, I just don't have enough experience with Mockito to figure it out. But surely someone does!

I would recommend wrapping the call to the legacy code into your own object.
So you end up with your own method along these lines:
class SearchEngineWrapper {
public String getSearchResult(String collection){
Engine engine = factory.getEngine();
Search search = engine.getSearch();
search.addCollectionSet(someCollectionSet);
SearchResult result = search.getSearchResult();
...
return document.getDocumentText();
}
}
Now you can mock out this method. The method also nicely documents your intent. Also you could test the actual implementation in an integration test.
when(searchEngineWrapper.getSearchResult("abc").thenReturn("foo");
when(searchEngineWrapper.getSearchResult("xyz").thenReturn("bar");

Related

Mocking Map parameter in Spock Framework Java test

I am having difficulty in getting a test to work using Spock Framework in a Java project. I have the following:
- Person.class
- Attribute.class
I have a service class which is being mocked in my test. The method I am trying to mock has the following signature:
serviceClass.call(Map<Person, List<Attribute>> map)
When I mock it purely using wildcards like:
serviceClass.call(_) >> MockReturnObject
Everything works as expected. The call in the service class will return the MockReturnObject.
However, for my specific case I need to specify the Person object I am passing in and assign it a specific MockReturnObject. Like:
serviceClass.call([(PersonA):_)]) >> MockReturnObjectA
or
def listWildcard = _
serviceClass.call(Map.of(PersonA, listWildcard)) >> MockReturnObjectA
Neither of these approaches work and the call ends up returning null instead of MockReturnObjectA (I assume it is because it is failing to match the arguments). I am unfortunately not too experienced with Spock and my attempt to search through documentation on handling Maps in this scenario has not come up fruitful. I would appreciate anyone able to offer any guidance.
I don't think it makes a difference but PersonA is passed in to an entry method in the serviceClass in a list like;
List<Attribute> list = getAttributeList()
entryClass.process(List<Person> personList) {
personList.forEach(person -> serviceClass.call(Map.of(person, list))
}
So in my tests, the "when" is:
entryClass.process([PersonA, PersonB, PersonC])
With all 3 being Mock(Person.class) with their own behaviours.
When you use an object as parameter Spock will use Groovy equality to compare the argument. However, if it is too complicated to construct the reference object, you can use code argument constraints instead to programmatically check the actual argument.
serviceClass.call({ it[PersonA] == attributeList }) >> MockReturnObject
As you've shared very little code this is the best example I can give.

Use a generic type to pass a specific class

I'm very new to programming language. My question might not even make sense. My environment is using java and trying to implement both ios and android apps in the same automation testing framework.
So, the idea is that any test script should be able to run on both the apps. Ex: one signin test script should be run for both ios and android.
I've decided to use interface and class implementation approach. The problem I'm facing is with test data. My company doesn't want to use excel. They want to use json for test data.
Here's my problem, look at the following line of code:
ValidBuy goodBuy = JsonFileReader.loadDaTa(TestBase.DATA_PATH, "good-buy.json", ValidBuy.class);
As you can see I have a class "ValidBuy" that has all the getters for a particular json file. I have another class "JsonFileReader" which takes the json filePath, fileName, and a class as an input and returns the data for that class name that I passed in. For this example I've passed ValidBuy.class
So, when I run a positive test, I'm passing "goodBuy" variable which is of type "ValidBuy". The problem starts here.
The test case is now specified with the data from goodBuy because it's type is "ValidBuy" and I'm passing goodBuy as a parameter.
Look at one of my extracted methods:
private void enterBuyInfo(ValidBuy goodBuy) {
itemPage = nativeApp.getItemPage(goodBuy);
itemPage.setItemName(goodBuy.getItemName());
itemPage.setItemSize(goodBuy.getItemSize());
itemPage.setItemDigitSSN(goodBuy.getSsn());
itemPage.clickContinue();
}
You can see those getters I'm using are coming from ValidBuy class.
If I run this test with the data for a badBuy:
InvalidBuy badBuy = JsonFileReader.loadDaTa(TestBase.DATA_PATH, "bad-buy.json", InvalidBuy.class);
It fails because now I have to change "ValidBuy" class with "InvalidBuy" class. Since, changing the parameter in the extracted method in every run is not possible, how can I make it more generic?
I want something like this:
TestData data = JsonFileReader.loadDaTa(RESOURCES_PATH, "good-client.json", InvalidBuy.class);
Here, TestData is generic. It could either be a class or interface (I don't know if that's possible) and the return type will be specified by whichever class I pass into the loadData() method. In this case InvalidBuy.class
The extracted method should look like this:
private void enterBuyInfo(TestData data) {
itemPage = nativeApp.getItemPage(data);
itemPage.setItemName(data.getItemName());
itemPage.setItemSize(data.getItemSize());
itemPage.setItemDigitSSN(data.getSsn());
itemPage.clickContinue();
}
If I can do this, I can use those extracted methods to create more tests.
I know I wrote a lot. I've only tried to make it as clear as possible. If it doesn't make any sense, just disregard it.
Any suggestions, ideas, code samples will be highly appreciated.
Firstly let me see if I understand your question. I think you are saying that loadData may return a value of type ValidBuy or InvalidBuy and you want to pass into it the class that you want returned. You then want to know how to use an interface that might represent either of these classes in your test methods so you can test various return values (both valid and invalid). You use the term "generic" in your question but I'm guessing you don't mean to use it in the specific way it's used in Java.
If I've understood your question correctly, then here's an answer:
Passing the class you wish to have returned into a method is an unusual usage and almost certainly not ideal. Better OOD would be to extract the common methods for all objects returned from loadData into an interface.
So:
interface Buy {
String getItemName();
boolean isValid();
}
class ValidBuy implements Buy {
#Override
public boolean isValid() {
return true;
}
...
}
class InvalidBuy implements Buy {
#Override
public boolean isValid() {
return false;
}
...
}
class JsonFileReader {
Buy loadData(Path path) {
...
}
}
Then your tests can look like:
#Test
void testValidBuy() {
assertTrue(reader.loadData(validPath).isvalid());
}
#Test
void testInvalidBuy() {
assertFalse(reader.loadData(invalidPath).isValid());
}
I realise I've simplified it a bit but hopefully you get the idea.

Java Cucumber storing value returned by step

i'm writing a cucumber test and i come up with some difficulty:
I have a step which creates dto and saves it using save client which returns dto back again i would need to use that returned dto for other step but don't know how to make it.
Here's how it looks in code :
commonExpenseCreationSteps.java
#Given("^new \"([^\"]*)\" expense with type \"([^"]*)\"$")
public ExpenseDTO newExpense(String description, String expenseType) throws Throwable {
ExpenseDTO expenseDTO = new ExpenseDTO();
expenseDTO.setDefaultPurpose(description);
expenseDTO.setExpenseType(expenseType);
return expenseSaveClient.save(expenseDTO);
}
expenseTransactionsSendSteps.java
#Given("^send expense for Approval$")
public void sendExpenseForApproval() throws InterruptedException {
expenseTransactionSendClient.sendToApproval(expenseDTO);
}
How it would be possible to store value returned by one Step and use it in other one in this case i return ExpenseDTO in newExpense method but i need to use it in sendExpenseForApproval but don't know how to do it !?
Create expenseDTO object outside of your glue code, probably in your stepdef class constructor.
ExpenseDTO expenseDTO = new ExpenseDTO();
The way to share state between steps in the same class is to use instance variables. Set the value in one step and use that value in a later step.
The way to share state between steps with two or more step classes is to use dependency injection.
I wrote a blog post that describes how it can be done using PicoContainer.

mockito - how to check an instance inside a method

I am new to Mockito, I am trying to verify the attributes of an object which gets created inside a method.
pseudo code below:
class A{
...
public String methodToTest(){
Parameter params = new Parameter(); //param is basically like a hashmap
params.add("action", "submit");
return process(params);
}
...
public String process(Parameter params){
//do some work based on params
return "done";
}
}
I want to test 2 things:
when I called methodToTest, process() method is called
process() method is called with the correct params containing action "submit"
I was able to verify that process() is eventually called easily using Mockito.verify().
However trying to check that params contains action "submit" is very difficult so far.
I have tried the following but it doesn't work :(
BaseMatcher<Parameter> paramIsCorrect = new BaseMatcher<Parameter>(){
#Overrides
public boolean matches(Object param){
return ("submit".equals((Parameter)param.get("action")));
}
//#Overrides description but do nothing
}
A mockA = mock(A);
A realA = new A();
realA.methodToTest();
verify(mockA).process(argThat(paramIsCorrect))
Any suggestion ?
If you have got verify() to work, presumably it is just a case of using an argument matcher to check the contains of params.
http://docs.mockito.googlecode.com/hg/org/mockito/Mockito.html#3
The example given in the above docs is verify(mockedList).get(anyInt()). You can also say verify(mockedList).get(argThat(myCustomMatcher)).
As an aside, it sounds like you are mocking the class under test. I've found that this usually means I haven't thought clearly about either my class or my test or both. In your example, you should be able to test that methodToTest() returns the right result irrespective of whether or not it calls process() because it returns a String. The mockito folk have lots of good documentation about this sort thing, particularly the "monkey island" blog: http://monkeyisland.pl/.
Just pass Parameter in as a constructor argument to a constructor of the class A, then use a mocked instance/implementation of Parameter in your test and verify on the mock. That is how it is normally done - you separate your classes and compose them using constructor injection, that enables you to pass in mocks for testing purposes (it also allows rewiring the application and exchanging some commons a lot easier).
If you need to create Parameter on every function invocation you should use a factory that creates Parameter instances and pass that in. Then you can verify on the factory as well as the object created by the factory.

Best way to exclude the if's, which in result calls multiple methods

I have a form made in JSP, here I have multiple buttons - "Approve, Reject, Save, Cancel".
For every submit, the control goes to a single Controller(Servlet), and there I am handling this submit as ::
String methodName = (String) request.getParameter("methodName");
if(methodName.trim.toLower().equals("approve"))
{
approve_Claim(parameters);
}
else if(methodName.trim.toLower().equals("reject"))
{
reject_Claim(parameters);
}
else if(methodName.trim.toLower().equals("save"))
{
save_Claim(parameters);
}
else if(methodName.trim.toLower().equals("cancel"))
{
cancel_Claim(parameters);
}
Is there a way to remove these multiple if's,
please suggest
It looks like you basically want a mapping from methodName.trim().toLower() to some sort of "claim action" which takes parameters. Create an interface like this:
interface ClaimAction
{
void execute(Parameters parameters);
}
Then implement this with classes of CancelClaimAction, ApproveClaimAction etc. Next create a Map<String, ClaimAction> mapping "approve" to an ApproveClaimAction etc. Then:
String actionName = methodName.trim().toLower(Locale.US));
ClaimAction action = CLAIM_ACTION_MAPPING.get(actionName);
if (action != null)
{
action.execute(parameters);
}
else
{
// Error case, presumably.
}
You could use enums to do this, but I'd expect each of these classes to have enough work to do that it's worth separating them out and testing each one individually.
Some alternatives coming in my minds are with switch statement:
Pass integers instead of strings in parameter, parse them to int in servlet and then use switch case on this integers.
If you are using Java 7 then use switch case over string.
In java 6 take Enum of this strings and then switch over them - for older versions, upto java 5, (as Enum are introduced in java 5, mentioned by Jon skeet)
The comparison about wich action was triggered has to be done somewhere. You can remove this logic from this place by using maybe something like this
public void handleRequest(HttpServletRequest request, String action){}
but as i said , the validation has to come somewhere in your code....
Well first suggestion will be that you can re-think on the approach itself. Lots of MVC frameworks are out there (like struts, struts2, spring MVC, play framework) which does all the handling part for you keeping you actual code very simple and clean.
Assuming you have some specific purpose to use your own implementation I would suggest you can use java reflection for this.
String methodName = (String) request.getParameter("methodName");
this.getClass().getMethod(methodName).invoke(this);
What you need to do here, is keep the methodNames same as the acutal java methods.
To pass the parameters, you can do it this way -
String methodName = (String) request.getParameter("methodName");
this.getClass().getMethod(methodName, Paramter.class).invoke(this, parameters);

Categories