Unit Testing: Verify that a method was called, without testing frameworks like Mockito or MockK - unit-testing

Not using testing frameworks like MockK or Mockito seems to be becoming more and more popular. I decided to try this approach. So far so good, returning fake data is simple. But how do I verify that a function (that does not return data) has been called?
Imagine having a calss like this:
class TestToaster: Toaster {
override fun showSuccessMessage(message: String) {
throw UnsupportedOperationException()
}
override fun showSuccessMessage(message: Int) {
throw UnsupportedOperationException()
}
override fun showErrorMessage(message: String) {
throw UnsupportedOperationException()
}
override fun showErrorMessage(message: Int) {
throw UnsupportedOperationException()
}
}
With MockK I would do
verify { toaster.showSuccessMessage() }
I do not want to reinvent a wheel so decided to ask. Finding anything on Google seems to be very difficult.
Since this is a thing, I assume the point would be to totally remove mocking libraries and everything can be done without them.

The old school way to do it before any appearance of the mocking library is to manually create an implementation that is just for testing . The test implementation will store how an method is called to some internal state such that the testing codes can verify if a method is called with expected parameters by checking the related state.
For example , a very simple Toaster implementation for testing can be :
public class MockToaster implements Toaster {
public String showSuccesMessageStr ;
public Integer showSuccesMessageInt;
public String showErrorMessageStr;
public Integer showErrorMessageInt;
public void showSuccessMessage(String msg){
this.showSuccesMessageStr = msg;
}
public void showSuccessMessage(Integer msg){
this.showSuccesMessageInt = msg;
}
public void showErrorMessage(String msg){
this.showErrorMessageStr = msg;
}
public void showErrorMessage(Integer msg){
this.showErrorMessageInt = msg;
}
}
Then in your test codes , you configure the object that you want to test to use MockToaster. To verify if it does really call showSuccessMessage("foo") , you can then assert if its showSuccesMessageStr equal to foo at the end of the test.

A lot of people seem to be suggesting the very straight forward solution for this, which totally makes sense. I decided to go a bit fancy and achieve this syntax:
verify(toaster = toaster, times = 1).showErrorMessage(any<String>()).
I created simple Matchers:
inline fun <reified T> anyObject(): T {
return T::class.constructors.first().call()
}
inline fun <reified T> anyPrimitive(): T {
return when (T::class) {
Int::class -> Int.MIN_VALUE as T
Long::class -> Long.MIN_VALUE as T
Byte::class -> Byte.MIN_VALUE as T
Short::class -> Short.MIN_VALUE as T
Float::class -> Float.MIN_VALUE as T
Double::class -> Double.MIN_VALUE as T
Char::class -> Char.MIN_VALUE as T
String:: class -> "io.readian.readian.matchers.strings" as T
Boolean::class -> false as T
else -> {
throw IllegalArgumentException("Not a primitive type ${T::class}")
}
}
}
Added a map to store call count for each method to my TestToaster where the key is the name of the function and value is the count:
private var callCount: MutableMap<String, Int> = mutableMapOf()
Whenever a function gets called I increase current call count value for a method. I get current method name through reflection
val key = object {}.javaClass.enclosingMethod?.name + param::class.simpleName
addCall(key)
In oder to achieve the "fancy" syntax, I created inner subcalss for TestToaster and a verify function:
fun verify(toaster: Toaster , times: Int = 1): Toaster {
return TestToaster.InnerToaster(toaster, times)
}
That function sends current toaster instance to the inner subclass to create new instance and returns it. When I call a method of the subclass in my above syntax, the check happens. If the check passes, nothing happens and test is passed, if conditions not met - and exception is thrown.
To make it more general and extendable I created this interface:
interface TestCallVerifiable {
var callCount: MutableMap<String, Int>
val callParams: MutableMap<String, CallParam>
fun addCall(key: String, vararg param: Any) {
val currentCountValue = callCount.getOrDefault(key, 0)
callCount[key] = currentCountValue + 1
callParams[key] = CallParam(param.toMutableList())
}
abstract class InnerTestVerifiable(
private val outer: TestCallVerifiable,
private val times: Int = 1,
) {
protected val params: CallParam = CallParam(mutableListOf())
protected fun check(functionName: String) {
val actualTimes = getActualCallCount(functionName)
if (actualTimes != times) {
throw IllegalStateException(
"$functionName expected to be called $times, but actual was $actualTimes"
)
}
val callParams = outer.callParams.getOrDefault(functionName, CallParam(mutableListOf()))
val result = mutableListOf<Boolean>()
callParams.values.forEachIndexed { index, item ->
val actualParam = params.values[index]
if (item == params.values[index] || (item != actualParam && isAnyParams(actualParam))) {
result.add(true)
}
}
if (params.values.isNotEmpty() && !result.all { it } || result.isEmpty()) {
throw IllegalStateException(
"$functionName expected to be called with ${callParams.values}, but actual was with ${params.values}"
)
}
}
private fun isAnyParams(vararg param: Any): Boolean {
param.forEach {
if (it.isAnyPrimitive()) return true
}
return false
}
private fun getActualCallCount(functionName: String): Int {
return outer.callCount.getOrDefault(functionName, 0)
}
}
data class CallParam(val values: MutableList<Any> = mutableListOf())
}
Here is the complete class:
open class TestToaster : TestCallVerifiable, Toaster {
override var callCount: MutableMap<String, Int> = mutableMapOf()
override val callParams: MutableMap<String, TestCallVerifiable.CallParam> = mutableMapOf()
override fun showSuccessMessage(message: String) {
val key = object {}.javaClass.enclosingMethod?.name + message::class.simpleName
addCall(key, message)
}
override fun showSuccessMessage(message: Int) {
val key = object {}.javaClass.enclosingMethod?.name + message::class.simpleName
addCall(key, message)
}
override fun showErrorMessage(message: String) {
val key = object {}.javaClass.enclosingMethod?.name + message::class.simpleName
addCall(key, message)
}
override fun showErrorMessage(message: Int) {
val key = object {}.javaClass.enclosingMethod?.name + message::class.simpleName
addCall(key, message)
}
private class InnerToaster(
verifiable: TestCallVerifiable,
times: Int,
) : TestCallVerifiable.InnerTestVerifiable(
outer = verifiable,
times = times,
), Toaster {
override fun showSuccessMessage(message: String) {
params.values.add(message)
val functionName = object {}.javaClass.enclosingMethod?.name + message::class.simpleName
check(functionName)
}
override fun showSuccessMessage(message: Int) {
params.values.add(message)
val functionName = object {}.javaClass.enclosingMethod?.name + message::class.simpleName
check(functionName)
}
override fun showErrorMessage(message: String) {
params.values.add(message)
val functionName = object {}.javaClass.enclosingMethod?.name + message::class.simpleName
check(functionName)
}
override fun showErrorMessage(message: Int) {
params.values.add(message)
val functionName = object {}.javaClass.enclosingMethod?.name + message::class.simpleName
check(functionName)
}
}
companion object {
fun verify(toaster: Toaster, times: Int = 1): Toaster {
return InnerToaster(toaster as TestCallVerifiable, times)
}
}
}
I have not tested this extensively and it will evolve with time, but so far it works well for me.
I also wrote an article about this on Medium: https://sermilion.medium.com/unit-testing-verify-that-a-method-was-called-without-testing-frameworks-like-mockito-or-mockk-433ef8e1aff4

Related

JustMock Arranging a method that returns an object whose value needs to be propagated into the SUT

I feel like I have to be missing something that is obvious, or I am overcomplication what I am doing. I am attempting to test a method that contains several other methods. One method is passed an object to write data to a database, in which the ID will be updated. This ID is then set to a local variable and used in other methods and the return. I can't get my Assert.AreEqual to work because the ID out is always 0 when I expect it to be 12. I have not had a lot of experience with UnitTesting and less with JuskMock. I assume I am doing something wrong.
This simplified pseudo code demonstrates my issue.
public class MyObj: IMyObject
{
public int ID { get; set; }
public string Name {get;set;}
}
public int Query(string Name)
{
int ID = 0;
ID = _setID.FindPerson(Name);
if(ID = 0)
{
IMyObject myObj = new MyObj(0, Name);
_setID.WritePerson(myObj);
ID = myObj.ID;
}
_setID.WriteSomethingElse(ID)
return ID;
}
public delegate void SetIDDelegate<T1, T2>(T1 arg1, T2 arg2);
[TestMethod]
public void TestQuery_ReturnID()
{
IMyObject UTobj = new MyObj {
ID = 12,
msg = string.Empty
};
Mock.Arrange(() => _mockSetID.WritePerson(
Arg.IsAny<IMyObject>(),
))
.DoInstead(new SetIDDelegate<IMyObject, string>
((IMyObject a, string b) =>
{
a = UTobj;
}
)).MustBeCalled();
int IDout = _objProcessObj.Query();
Mock.Assert(_mockSetID);
Assert.AreEqual(UTobj.ID, IDout);
}
I was able to figure out my issue with my UT. I needed to update the object in the delegate, not replace it.
.DoInstead(new SetIDDelegate<IMyObject, string>
((IMyObject a, string b) =>
{
a.ID = UTobj.ID;
}

Accessing retrofit 2 data outside on response?

I am working on two apps, in one of my app "A" i applied retrofit 2.
This was the method i used to retrieve data.
But here in on Response the data retrieved in response body can be set to activity variables and can be used outside this method without getting null values.
public void fetch_information() {
ApiInterface = ApiClient.getApiClient().create(Api.class);
Call<List<City>> call = ApiInterface.GetCities();
call.enqueue(new Callback<List<City>>() {
#Override
public void onResponse(Call<List<City>> call, Response<List<City>> response) {
citylist = new ArrayList<City>();
citylist = response.body();
cities = new String[citylist.size()];
citiesid = new String[citylist.size()];
for (int i = 0; i < citylist.size(); i++) {
cities[i] = citylist.get(i).getCityName();
citiesid[i] = citylist.get(i).getCityId();
}
city_adapter = new ArrayAdapter<String>(Pay_Payment_X.this, android.R.layout.simple_list_item_1, cities);
city_adapter.setDropDownViewResource(R.layout.spinner_dropdown_layout);
City_Spinner.setAdapter(city_adapter);
}
#Override
public void onFailure(Call<List<City>> call, Throwable t) {
Toast.makeText(getApplicationContext(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
after applying this method and on debugging this method i will retain values of varaibles "cities" and "citiesid"out side onResponse.
But applying retrofit 2 similarly on another app "B", i did the same thing for retrieving data on different URL.
ApiUtil.getServiceClass().getAllPost().enqueue(new Callback<List<ApiObject>>() {
#Override
public void onResponse(Call<List<ApiObject>> call, Response<List<ApiObject>> response) {
if (response.isSuccessful()) {
List<ApiObject> postList = response.body();
try {
for (int i = 0; i < postList.size(); i++) {
String Name = postList.get(i).getGamesName();
mGamesName.add(Name);
}
} catch (Exception e) {
}
Log.d(TAG, "Returned count " + postList.size());
NewAdapter adapter = new NewAdapter(getApplicationContext(), postList);
recyclerView.setAdapter(adapter);
}
}
#Override
public void onFailure(Call<List<ApiObject>> call, Throwable t) {
//showErrorMessage();
Log.d(TAG, "error loading from API");
}
});
the data is retrievable inside onResponse but outside it shows null.
So here variables are not retaining values.
Why is this happening?
the only thing came to mind is retrieving data can take time while your code lines are being read and finding null values as data has not been received yet.
Also to mention in app "A" the data retrieved is huge but in app "B" only 3 objects with string values.But still in app"A" data is retrievable.
In app 2 did this for resolving my issue.
public void doRequest( final ApiCallback callback){
ApiUtil.getServiceClass().getAllPost().enqueue(new Callback<List<ApiObject>>() {
#Override
public void onResponse(Call<List<ApiObject>> call, Response<List<ApiObject>> response) {
if (response.isSuccessful()) {
List<ApiObject> postList = response.body();
callback.onSuccess(postList);
// apobject =response.body();
if(response.isSuccessful()) {
try {
for (int i = 0; i < postList.size(); i++) {
String Name = postList.get(i).getGamesName().toString();
mGamesName.add(Name);
}
} catch (Exception e) {
}
}
Log.d(TAG, "Returned count " + postList.size());
NewAdapter adapter = new NewAdapter(getApplicationContext(), postList);
recyclerView.setAdapter(adapter);
}
}
#Override
public void onFailure(Call<List<ApiObject>> call, Throwable t) {
//showErrorMessage();
Log.d(TAG, "error loading from API");
}
});
}
pass an interface
public interface ApiCallback{
void onSuccess(List<ApiObject> result);
}
and in on Create view of activity i called this
doRequest(new ApiCallback(){
#Override
public void onSuccess(List<ApiObject> result){
//here i can set variable values
}
});
the only thing came to mind is retrieving data can take time while your code lines are being read and finding null values as data has not been received yet.
That's entirely correct. Your call is finishing after you check the values. I'm going to go on a limb here and say that it's just a coincidence that it works on one app and not in the other (if they are actually doing it the same way)
When you call callback.onSuccess(postList); doesn't seem to be right either, because you haven't checked yet for success. This means that response.body() might be null and response.errorBody() will contain the body of the error.
If you'd move callback.onSuccess inside the if this would be fixed:
if(response.isSuccessful()) {
callback.onSuccess(response.body());
try {
for (int i = 0; i < postList.size(); i++) {
String Name = postList.get(i).getGamesName().toString();
mGamesName.add(Name);
}
} catch (Exception e) {
}
Last but not least, inside the onSuccess method is when you can use your global variables. Maybe it's better to stop using global variables and just use the callback parameters.

unit test - mockito testing to check if another method is invoked

Summary: I am trying to test if a method is invoked once I call one method.
What this class does is, displays information of wrong spelled words and provides u with buttons to 'ignore' or 'ignore all' or 'add to dictionary', etc.
Over here 'ignore' is a JButton declared above.
I am trying to write one test for this method ->
public class SpellCheckerDialog extends JDialog implements ActionListener {
...
..
public void actionPerformed( ActionEvent ev ) {
Object source = ev.getSource();
if( source == ignore ) {
searchNext();
}
}
...
}
Here is what it is invoking, I am testing to see if this method is being invoked or not.
...
//inside same class
public boolean searchNext() {
String wordStr;
while( true ) {
wordStr = tok.nextInvalidWord();
if( wordStr == null ) {
dispose();
String title = SpellChecker.getApplicationName();
if(title == null){
title = this.getTitle();
}
SpellChecker.getMessageHandler().handleInformation( getParent(), title, Utils.getResource( "msgFinish" ) );
return false;
}
if( ignoreWords.contains( wordStr ) ) {
continue;
}
String changeTo = changeWords.get( wordStr );
if( changeTo != null ) {
replaceWord( wordStr, changeTo );
continue;
}
break;
}
word.setText( wordStr );
notFound.setText( wordStr );
List<Suggestion> list = dictionary.searchSuggestions( wordStr );
boolean needCapitalization = tok.isFirstWordInSentence() && Utils.isFirstCapitalized( wordStr );
Vector<String> suggestionsVector = new Vector<String>();
for( int i = 0; i < list.size() && i < options.getSuggestionsLimitDialog(); i++ ) {
Suggestion sugestion = list.get( i );
String newWord = sugestion.getWord();
if( needCapitalization ) {
newWord = Utils.getCapitalized( newWord );
}
if( i == 0 )
word.setText( newWord );
suggestionsVector.add( newWord );
}
suggestionsList.setListData( suggestionsVector );
addToDic.setEnabled( true );
return true;
}
What I have tried until now, tried using Mockito and calling the verify method, but this code snippet seems to not working or have lots of dependencies that I am struggling to get around.
Inside my TestClass, I have this - >
Dialog fr = Mockito.mock(Dialog.class);
SpellCheckerDialog sD = new SpellCheckerDialog(fr);
sD.searchNext();
Mockito.verify(sD, Mockito.times(1)).thenReturn(searchNext());
I don't know if I should be making a stub for my (ActionEvent ev) or ...
Verifications must be done on the mocks created by Mockito, because the framework can't possibly know what happens with objects that it does not manage. This being said, your searchNext() method is part of your class under test so you probably want to spy on it just like in the example below:
public class SpyTest {
class MyClass {
public void callDoSomething(){
doSomething();
}
public void doSomething(){
// whatever
}
}
#Test
public void shouldSpyAndVerifyMethodCall(){
MyClass objectUnderTest = new MyClass();
MyClass spy = Mockito.spy(objectUnderTest);
spy.callDoSomething();
Mockito.verify(spy, Mockito.times(1)).doSomething();
}
}
My advice is to go through the Mockito documentation and examples from the link above, as they're pretty straight-forward and should give you a good starting point.
EDIT as per your comment:
public class SpyTest {
class MyClass {
private JButton myButtton;
public void actionPerformed(ActionEvent event){
if(event.getSource() == myButtton) {
searchNext();
}
}
public void searchNext(){
// whatever
}
}
#Mock // define a mock for the button to "hack" the source check
private JButton mockButton;
#InjectMocks // inject the mock in our object under test
private MyClass objectUnderTest;
#Test
public void shouldSpyAndVerifyMethodCall(){
// spy on our object so we can query various interactions
MyClass spy = spy(objectUnderTest);
// event mock
ActionEvent mockEvent = mock(ActionEvent.class);
// "hack" the source check
when(mockEvent.getSource()).thenReturn(mockButton);
// call main logic
spy.actionPerformed(mockEvent);
// verify interactions
verify(spy).searchNext(); // times(1) not needed because it's the implicit/default setting, see David's comment
}
}

How to break execution of method in Groovy?

I have the code below and strange things hapened so I println something. When I passed to method String which matches the regex - printed :
---------->1
---------->2
How is this possible? How to return different return things?
def getInfo(String nameOfFile) {
def matcher = nameOfFile =~ /(\w+)_(\w+)_(\w+)/
if (matcher.matches()) {
matcher.each { fullName, CN, FN, PN->
println "---------->1"
return [nameOfFile, CN, FN, PN]
}
}
println "----------->2"
return [nameOfFile]
}
This just returns from the Closure, not the enclosing method
return [nameOfFile, CN, FN, PN]
Depending on what it is you're trying to do (you don't give any example inputs), you probably want findResult instead of each:
def getInfo( String nameOfFile ) {
def matcher = nameOfFile =~ /(\w+)_(\w+)_(\w+)/
if (matcher.matches()) {
matcher.findResult { fullName, CN, FN, PN->
[nameOfFile, CN, FN, PN]
}
}
else {
[nameOfFile]
}
}
Here is a very crude approximation of the Groovy code in Java (using 1.6):
import java.util.*;
import java.util.regex.*;
interface ClosureBlock {
Object each(String... args);
}
public class Runner {
public List<String> getInfo(String nameOfFile) {
List<String> results = new ArrayList<String>();
Pattern regex = Pattern.compile("(\\w+)_(\\w+)_(\\w+)");
Matcher matcher = regex.matcher(nameOfFile);
if (matcher.matches()) {
String fullName = matcher.group(0);
String CN = matcher.group(1);
String FN = matcher.group(2);
String PN = matcher.group(3);
ClosureBlock closure = new ClosureBlock() {
public Object each(String... args) {
List<String> results = new ArrayList<String>();
String fullName = args[0];
String CN = args[1];
String FN = args[2];
String PN = args[3];
results.add(fullName);
results.add(CN);
results.add(FN);
results.add(PN);
// this return does not exit getInfo()
System.out.println("----------->1");
return results;
}
};
// each returns an Object but we didn't assign it
closure.each(fullName, CN, FN, PN);
}
System.out.println("----------->2");
results.add(nameOfFile);
return results;
}
public static void main(String... args) {
Runner quick = new Runner();
List<String> list = quick.getInfo(args[0]);
System.out.println(list);
}
}
The key points are:
Runner defines an anonymous inner class with an each method. Calling return in the each method does not exit the getInfo method
Even though the each method returns an Object, we don't assign it to anything so the List<String> in getInfo is unaffected.
The Java is ugly and complex. The Groovy code is much more elegant, but regarding your question, the Java code illustrates the idea.

How to mock obj.&method in groovy/grails

Code below. I would like to use the obj.&method thing to pass around a reference to it. However, when trying to test that, mocking it doesn't work. Is there something in the test I can do to make it work?
The result of running the test is it throws the exception "should not get here".
import grails.test.mixin.TestFor
#TestFor(SomeController)
class SomeControllerTest {
void testSomething() {
def control = mockFor(SomethingElse)
control.demand.someMethod(1) { int num, String str, Map another, List param ->
println 'worked'
}
controller.obj = control.createMock()
controller.underTest()
control.verify()
}
}
class SomeController {
SomethingElse obj
void underTest() {
otherCall(obj.&someMethod) // **
}
void otherCall(toRun) {
String result = toRun(1, 'blah', null, null) // ** doesn't call mock here
}
}
class SomethingElse {
String someMethod(int num, String str, Map another, List param) {
throw new RuntimeException('should not get here')
}
}
Yes, don't mock SomethingElse. Use ExpandoMetaClass instead.
void testSomething() {
SomethingElse.metaClass.someMethod = {int num, String str, Map another,
List param ->
println 'worked'
}
controller.obj = new SomethingElse()
controller.underTest()
}
with the price of loosing the mock control.
A roundabout way would be to mock otherCall() as well
void testSomething() {
def control = mockFor(SomethingElse)
control.demand.someMethod(1) { int num, String str, Map another,
List param ->
println 'worked'
}
def obj = control.createMock()
controller.metaClass.otherCall = {Closure clos ->
delegate.obj.someMethod(1, 'blah', null, null)
}
controller.obj = obj
controller.underTest()
control.verify()
}
This way you can verify the mock control. But I am still skeptical about using mock objects and MethodClosure together.