For testing purpose, I try to "fake" some objects. I want to do the following: I have an object, and want to add new methods, or overwrite some. Sadly, unlike in Java, its not possible to create "nameless" classes. Ok, I could do it by simply creating a new class, but I want to do it dinamicaly.
This is the class:
class Test
{
public function method1()
{
return 'oldmethod1';
}
public function method2()
{
return 'oldmethod2';
}
public static function staticmethod1()
{
return 'staticmethod1';
}
public static function staticmethod2()
{
return 'staticmethod2';
}
}
and now what I want to do:
$a = new Test();
$b = new CreateMockObjectFromObject($a);
$b->newmethod = function() { return 'newmethod'; };
$b->method2 = function() { return 'method2 is overwritten'; };
$b->staticmethod2 = function() { return 'staticmethod2 overwritten'; };
echo $b->method1().'<br>';
echo $b->method2().'<br>';
echo $b::staticmethod1().'<br>';
echo $b::staticmethod2().'<br>';
Here you can see my wishes: call a normal method, overwrite a method, call a static method, overwrite a method. The results: FAIL, SUCCESS, FAIL, FAIL.
I have a helper class:
class CreateMockObjectFromObject
{
private $sourceObj;
/**
* #return
*/
public function __call ($method, $args)
{
if (isset($this->sourceObj->$method))
{
return call_user_func_array($this->sourceObj->$method, $args);
}
if (isset($this->$method))
{
return call_user_func_array($this->$method, $args);
}
throw new Exception ($method.' NOT FOUND');
}
/**
* #return
*/
public static function __callStatic ($method, $args)
{
// I cant even imagine this...
}
/**
* #return CreateMockObjectFromObject
*/
public function __construct ($sourceObj)
{
$this->sourceObj = $sourceObj;
}
}
I cant even imagine what about static methods. How to write this helper class so that all mocking/faking can work? And I didnt even talk about "const"-s...
once again, I know it all could be done with extending, but I need to do in this way!
Mocking an entire class:
$mock = Mockery::mock('FQ\ClassName');
Mocking only certain methods:
$mock = Mockery::mock('FQ\ClassName[method1, method2]')
Here, method3, method4, ..., methodN will function exactly as they do in your class implementation.
Making an expectation:
$mock->shouldReceive('method1')
->once()
->andReturn('a value')
;
After you make an expectation, then you Act upon the system under test:
$return = $objectImTesting->performAction($mock);
Once you act, you should assert that any return value is what it should be, given the input you provided:
$this->assertEquals('a value', $return);
Taking the above example, the test will fail if any of the following conditions are true:
method1 on your mock object is never called
method1 on your mock object is called more than once
the return value of performAction is not the literal value 'a value'
Now, you've not reinvented the wheel and instead already gotten started writing tests. Also for free, you get everything that PHPUnit and Mockery provide you (how long do you think it would take you by yourself to be able to support Demeter Chains in your mocking framework?).
Don't get me wrong, by all means go ahead and develop your testing framework if all you're interested in is learning. However, in my opinion I would not want to trust a testing framework that I developed by myself to ensure the code that I write works. I'd much rather use something that's open source and has been around for awhile so that I can sleep easier at night.
More information:
PHPUnit Documentation
Mockery Documentation
Related
I have a simple Unit Test that is failing. Hopefully I can explain this in simple terms as I've been looking at it for hours and I see what the issue is, but I am not too familiar the underlying theory behind Mocks so I am a bit confused and cannot fix it. I will summarize the issue very quickly and then paste the code below.
Basically, in my test method called getAllValidModelsTest(), it uses a for loop to iterate thru enum values of object type DeviceModel. There are only 5: [EX3400_24P, EX4300_32F, EX4300_48MP, SRX_345, FAUX].
So inside the for loop, before the Assert statement (Junit), it makes a static method call to getDevice(deviceId) and it should from there return a Device object. The first line under the for loop in the getAllValidModelsTest() mocks the elementMock object to return the current model that is being iterated over in the DeviceModels[] array that was returned from the .values() call on the enums DeviceModel class.
So my issue is, when it jumps in the 2nd iteration in my for loop (counting from 1), the Assert fails , because the 0th element in the DeviceModel[] array is obviously EX4300_32F, but in the #Before setUp annotation it is being mocked to return EX3400_24P. But the weird thing is, under the for loop inside the getAllValidModelsTest() method, it is being overridden/mocked again to return to the current model that is being iterated through when .getModel is called on the elementMock object, so it should be returning the SAME value...
This is how the class SwitchDeviceFactoryTest.java is constructed (the class with the Unit Test):
#PowerMockIgnore({"javax.net.ssl.*"})
#RunWith(PowerMockRunner.class)
#PrepareForTest({DataGatewayFactory.class, SwitchConfig.class, RouterConfig.class})
public class SwitchDeviceFactoryTest {
String deviceId = "testdevice";
String ip = "1.1.1.1";
DataGateway dbMock = Mockito.mock(DataGateway.class);
SwitchConfig swConfigMock = PowerMockito.mock(SwitchConfig.class);
RouterConfig routerConfigMock = PowerMockito.mock(RouterConfig.class);
TransportDeviceSecretsInfo secrets = new TransportDeviceSecretsInfo();
TransportDeviceSecretsData secretsData = new TransportDeviceSecretsData("root","rootPw", "sshUser", "sshPass", "snmpAuthPass", "snmpPrivPass");
IElement elementMock = Mockito.mock(IElement.class);
ITransportDeviceSecretsCrud transportDeviceSecretsCrud = mock(ITransportDeviceSecretsCrud.class);
ISwitchConfigCrud switchConfigCrud = mock(ISwitchConfigCrud.class);
IRouterConfigCrud routerConfigCrud = mock(IRouterConfigCrud.class);
IElementCrud elementCrud = mock(IElementCrud.class);
This is my setUp method that runs before the test. The only variables that should be of importance are the elementMock object, specifically the one being mocked to return the EX3400_24P object:
#Before
public void setup() throws Exception {
secrets.setSecretsData(secretsData);
PowerMockito.mockStatic(DataGatewayFactory.class);
Mockito.when(DataGatewayFactory.getInstance()).thenReturn(dbMock);
Mockito.when(dbMock.getTransportDeviceSecretsCrud()).thenReturn(transportDeviceSecretsCrud);
Mockito.when(transportDeviceSecretsCrud.getServerSecretsInfo(anyString())).thenReturn(Optional.of(secrets));
Mockito.when(transportDeviceSecretsCrud.getReportedSecretsInfo(anyString())).thenReturn(Optional.of(secrets));
when(dbMock.getElementCrud()).thenReturn(elementCrud);
doReturn(Optional.of(elementMock)).when(elementCrud).getById(anyString());
Mockito.when(elementMock.getModel()).thenReturn(DeviceModel.EX3400_24P.getModel());
Mockito.when(elementMock.getType()).thenReturn(ElementType.SWITCH);
Mockito.when(dbMock.getSwitchConfigCrud()).thenReturn(switchConfigCrud);
Mockito.when(switchConfigCrud.get(anyString())).thenReturn(Optional.of(swConfigMock));
Mockito.when(swConfigMock.getIp()).thenReturn(ip);
Mockito.when(dbMock.getRouterConfigCrud()).thenReturn(routerConfigCrud);
Mockito.when(routerConfigCrud.get(anyString())).thenReturn(Optional.of(routerConfigMock));
Mockito.when(routerConfigMock.getIp()).thenReturn(ip);
And the test method:
#Test
public void getAllValidModelsTest() throws Exception {
for (DeviceModel model: DeviceModel.values()) {
when(elementMock.getModel()).thenReturn(model.getModel());
if (model == DeviceModel.SRX_345)
when(elementMock.getType()).thenReturn(ElementType.ROUTER);
else
when(elementMock.getType()).thenReturn(ElementType.SWITCH);
Device device = DeviceFactory.getDevice(deviceId);
assertEquals(model, device.getModel());
}
}
The thing that doesn't make sense, is I was refactoring code, and only changed 2 lines (the elementCrud and elementMock .doReturn and .when calls) and it works perfectly fine on the develop branch.
When I debug, I can see that on the 2nd iteration of the for loop, .getModel returns EX3400_24P object inside the static getDevice method, when it should be returning model.getModel() , which would be the 2nd object being iterated on in the .values() enum array of DeviceModels... so it should be EX4300_32F.
On the develop branch, this works perfectly.... It's as if the Mockito mock object forgets what it's suppose to do when it jumps inside the DeviceFactory class inside the getDevice method once its called in my getAllValidModelsTest() method (i.e. Device device = DeviceFactory.getDevice(deviceId);)
Here is the .getDevice method from the DeviceFactory class:
public static Device getDevice(String serialNumber) throws Exception {
IElement element = dataGateway.getElementCrud().getById(serialNumber).get();
DeviceModel model = DeviceModel.valueOfLabel(element.getModel()); // right here is where it returns the wrong model... it returns EX3400_24P on the 2nd iteration
log.info("Found device {} in database", serialNumber);
if (serialNumber.startsWith(FakeDevicePrefix.ATGTEST.toString()) || serialNumber.startsWith(FakeDevicePrefix.FAKE.toString())) {
log.info("Detected FAKE/ATG serial number. Using FAUX device.");
model = DeviceModel.FAUX;
}
switch (element.getType()) {
case SWITCH:
SwitchConfig config = dataGateway.getSwitchConfigCrud().get(serialNumber).get();
return getDevice(serialNumber, config.getIp(), model);
case ROUTER:
RouterConfig rconfig = dataGateway.getRouterConfigCrud().get(serialNumber).get();
return getDevice(serialNumber, rconfig.getIp(), DeviceModel.SRX_345);
case PTP:
default:
log.warn("Unsupported device type {}", element.getType().toString());
throw new Exception("Unsupported device type " + element.getType().toString());
}
}
I did indeed comment out/remove the piece of code that mocks it to return EX3400_24P in the setUp() method with #Before annotation , but the tests fails with a NULL POINTER EXCEPTION at this point.
How does the .getModel method know to return what I mocked it to return in the previous class (SwitchDeviceFactoryTest.java) before it jumps into the DeviceFactory.java class? How does it remember that if I'm not passing it in as a variable into the getDevice() method?
Do I need to use PowerMock or something because this is a static method? How does this change anything?
Please help!
Using Byte Buddy's advice API, is it possible to return from the instrumented method without actually executing it?
One use case would be to implement a cache and to return the cached value, if present, instead of computing the value again.
#Advice.OnMethodEnter
public static Object returnCachedValue(#Advice.Argument(0) String query) {
if (cache.containsKey(query)) {
// should "abort" method call
return cache.get(query);
}
}
I know that this code sample above just creates a local variable which I can get in a #Advice.OnMethodExit method. But is there a way to abort the method call on an explicit return? If yes, is this also possible for void methods?
No, this is not possible, a return value can only be set from exit advice. But it can be emulated by skipping the original method in case that a value already exists and by setting this value from the exit advice in case that the enter advice defines a value:
class MyAdvice {
#Advice.OnMethodEnter(skipOn = Advice.OnNonDefaultValue.class)
public static Object returnCachedValue(#Advice.Argument(0) String query) {
if (cache.containsKey(query)) {
return cache.get(query);
} else {
return null;
}
}
#Advice.OnMethodExit
public static void processCachedValue(
#Advice.Return(readOnly = false, typing = DYNAMIC) Object returned,
#Advice.Enter Object enter) {
if (enter != null) {
returned = enter;
} else {
cache.put(query, returned);
}
}
}
Of course, this does not work if the cached value is null. To avoid this, you could wrap the value in some instance to make sure that the enter value is never null. Doing so would also allow to use the above pattern to void methods.
This might look inconvenient to program but the idea of advice is that Byte Buddy can use the advice class as a template and inline the byte code without much work to avoid a runtime overhead.
knowing that Java is different from PHP, is there a way to reconstruct such a behaviour in Java?:
In PHP I can make something like this:
function a_function() {
static $var = "something";
// do something...
}
After I call this function the first time, all subsequent calls to this function would result in a $var not being reset to "something" at all (its value will be preserved across multiple calls to this function, i.e. if the value of $var will be changed while // do something... is executed, then after a subsequent call, the value of $var would be the last assigned value to $var).
I was wondering if there's a same/like method to do this in Java (declaring a static local variable in a method in Java is not possible, so I guess I can achieve this in some other ways). I thought of making a protected or private static field and use it across multiple calls to a method (here a possible way with a boolean):
public class A {
protected static boolean isSomething = "false";
public void aMethod() {
// do something... e.g. change the isSomething boolean
// if some condition occurs
}
}
What do you think about this approach and what would you do in such a case? Are there other ways to accomplish this?
Thanks for the attention!
Yes, you can do it the way you show in your second example.
Keep in mind that you will have problems with that pattern in multi-thread situations.
It is also not very elegant as a design choice. And depending on your situation its dangerous and error-prone.
Use with care (that is: don't do this unless you know you need it)
Usually you would want something like that to be thread-save and atomic.
You can do it with the volatile keyword if you also take care of synchronization.
public class A {
private static volatile boolean status = true;
public flipStatus() {
synchronized(this) {
status = !status;
}
}
}
Even better is the use of the Atomic classes from java.util.concurrent.atomic
import java.util.concurrent.atomic.*;
public class A {
private static final AtomicBoolean status = new AtomicBoolean(true);
public boolean flipStatusToTrue() { // return true if value has been flipped
return status.compareAndSet(false, true);
}
public boolean flipStatusToFalse() { // return true if value has been flipped
return status.compareAndSet(true, false);
}
}
I have a void method and I want to test it. How do I do that?
Here's the method:
public void updateCustomerTagCount() {
List<String> fileList = ImportTagJob.fetchData();
try {
for (String tag : fileList) {
Long tagNo = Long.parseLong(tag);
Customer customer = DatabaseInterface.getCustomer(tagNo);
customer.incrementNoOfTimesRecycled();
DatabaseInterface.UpdateCustomer(customer);
}
} catch(IllegalArgumentException ex) {
ex.printStackTrace();
}
}
when the method returns void, you can't test the method output. Instead, you must test what are the expected consequences of that method. For example:
public class Echo {
String x;
public static void main(String[] args){
testVoidMethod();
}
private static void testVoidMethod() {
Echo e = new Echo();
//x == null
e.voidMethod("xyz");
System.out.println("xyz".equals(e.x)); //true expected
}
private void voidMethod(String s) {
x = s;
}
}
It might not be always true, but basic concept of unit test is to check if function works as expected and properly handling errors when unexpected parameters/situation is given.
So basically unit test is against the functions that takes input parameters and return some output so we can write those unit test.
The code like yours, however, includes some other dependency (database call) and that's something you can't execute unless you write integration-test code or real database connection related one and actually that's not recommended for unit test.
So what you need to do might be introducing unit test framework, especially Mockto/Powermock or some other stuff that provides object mocking feature. With those test framework, you can simulate database operation or other function call that is going to be happening outside of your test unit code.
Also, about how do I test void function, there is nothing you can with Assert feature to compare output since it returns nothing as you mentioned.
But still, there is a way for unit test.
Just call updateCustomerTagCount() to make sure function works. Even with just calling the function, those unit test can raise your unit test coverage.
Of course for your case, you need to mock
ImportTagJob.fetchData();
and
DatabaseInterface.getCustomer(tagNo);
and have to.
Let mocked
ImportTagJob.fetchData();
throw empty list as well as non-empty list and check if your code works as you expected. Add exception handling if necessary. In your code, there are two condition depends on whether fieList are null or non-null, you need to test it.
Also, mock those objects and let them throw IllegalArgumentException where you expect it to be thrown, and write an unit test if the function throws a exception. In Junit, it should be like
#Test(expected = IllegalArgumentException.class)
public void updateCustomerTagCountTest(){
// mock the objects
xxxxx.updateCustomerTagCount();
}
That way, you can ensure that function will throw exception properly when it has to.
I know there are several question about void-method Unit-Testing, but my question is different.
I'm learning java, so my boss give me some tasks with different requirements on my tasks.
In my actual task, there is a requirement which says, the jUnit test must cover >60%. So I need to test a very simple method to reach this 60%. The method is the following:
public void updateGreen() {
// delete this outprint if the Power Manager works
System.out.println(onCommand + "-green");
// p = Runtime.getRuntime().exec(command + "-green");
// wait until the command is finished
// p.waitFor();
}
Because of intern problems, I can't execute the command with the Runtime task. So there is only a System.out in this method.
I've multiple methods like that, so tests for this method will cover over 10% of my whole code.
Is it useful to test such a method? When yes, how?
If there is a lot of such methods, the thing which you might want to test here is that updateScreen() uses the right string, "some-command-green" and that the System.out is being invoked. In order to do this you might want to extract System.out into an object field and mock it (i.e. with Mockito's spy()) to test the string that was provided to println.
I.e.
class MyClass{
PrintStream out = System.out;
public void updateGreen() { ... }
}
In test:
#Test
public void testUpdate(){
MyClass myClass = new MyClass();
myClass.out = Mockito.spy(new PrintStream(...));
// mock a call with an expected input
doNothing().when(myClass.out).println("expected command");
myClass.updateGreen();
// test that there was a call
Mockito.verify(myClass.out, Mockito.times(1)).println("expected command");
}
You could return true if the method ran successfully and false otherwise. It would be easy to test for this.
You could also test the output of this method, as described here:
Should we unit test console outputs?
But in my experience, it is much better to have methods return an optimistic or pessimistic value (true/false, 1/0/-1 etc) to indicate their status.
You can also write a getter method for the onCommand flag:
public string getFlag(){
// some logic here
return "green";
// otherwise default to no flags
return "";
}
You could test that onCommand + "-green" has been written to System.out by using the System Rules library.