Mock a "global" property in Kotlin - unit-testing

Is there a way to change the return value of the property accessor called when running unit tests? Like mocking the result of the property settingsState?
I am learning to create unit tests. What that class makes is to bring stored data into the program, this data is given in a visual form. In my test, I want to define what is going on there because the window won't open there.
// top-level declaration outside class
val settingsState: ApplicationSettingsState
get() = ServiceManager.getService(ApplicationSettingsState::class.java)

settingsState is not a variable. It is a property.
I am not aware of any way to mock global properties (or global functions). The way mocking works is to cleverly create an object that looks a bit like the object to mock from the outside, but behaves differently on the inside. In the absence of an object to mock, there is no way to mock.
That said, on the bytecode level there are no global variables, properties, whatever. They are all wrapped into classes, because the JVM likes it that way. Some clever bytecode manipulation might be able to achieve some effect – but not MockK.
You can mock the property if you have a mocked object. To paraphrase the documentation:
val mock = mockk(Bus())
every { mock getProperty "speed" } returns 33

Yes you can. You want to mock a property that is declared at top-level. Top-level code in a given Kotlin file MyFile.kt is actually compiled to be inside the class MyFileKt. Let's say we have following Kotlin file named MyFile.kt:
package foo
val bar = "some value"
If we compile this file to JVM bytecode and then decompile the result to Java, we get a Java class that looks similar to this:
package foo;
public final class MyFileKt {
private static final String bar = "some value";
public static final String getBar() {
return bar;
}
}
As you can see, our top-level val we defined earlier is actually translated to a static field with associated getter in the class MyFileKt.
TLDR; Getting back to our original problem, all you need to do is statically mock this field:
mockkStatic("foo.MyFileKt") // Fully-qualified name of the generated class
every { bar } returns "some other test value" // Define what our top-level property should return
The same also works for top-level functions, of course.

Related

How can I open class only to test class?

I'm mainly a Java developer and wonder about structure when writing unit test in kotlin,
Assuming there's no package-private in kotlin
private to restrict visibility to the file
internal to restrict visibility to the module
How can I open class only to test class ?
Must I write test inside kotlin class or open class to all module (internal)?
What's the kotlin way to open method for unit test only?
EDIT
Found similar question/request in kotlin discuss by #bentolor:
How am I supposed to do unit / whitebox testing properly? I want to write test code which tests class-internal functionality which I do not want to expose to other classes except my test class at all.
The package protected visibility is an excellent way to achieve this. Whereas Kotlin now requires me to make these methods effectively public and litter the visible API of my component all-over the project be able to test them.
In my view internal is more or less public as it has a much larger scope. Most projects have sth. around 1 - 5 “modules” in the Kotlin sense.
Really strongly asking/advocating for package-local visibility here.
Formally it is not possible to do this honestly on JVM, because class couldn't be open for subset of possible interiters.
However it can be partially done by the following trick:
open class SomeClass internal constructor(val configurableParameter: Int) {
companion object {
private const val defaultInput = 123
fun create() = SomeClass(defaultInput)
}
}
The constructor of this class can be called only from the same module (or from tests). And class is public, so anyone can use it. However from external modules you have only two ways of the class construction: companion object or reflection.
And finally you couldn't inherit from this class at any other modules, because constructor is internal.
For Android developers only, there's AndroidX VisibleForTesting annotation
Denotes that the class, method or field has its visibility relaxed, so that it is more widely visible than otherwise necessary to make code testable

Test a call in Kotlin object

I have a Kotlin class like this one
import org.slf4j.LoggerFactory.getLogger
object Doer {
val logger = LoggerFactory.getLogger(Doer::class.java)
fun doSomething() : Int {
logger.warn("Doing.")
return 123
}
}
I would like to have a unit test that verifies logger.warn call was made with relevant message. How do I do this in Kotlin?
One of my ideas was to use a getter for logger and stub that with Mockito, but do I need to declare such method explicitly given that Kotlin generates getters automatically? Method getLogger() is not visible in Doer class, but if I declare it explicitly it conflicts with generated one.
Are there better options to test this?
Second question is how to mock the object instance, since it is initialized by language which is out of my control?

Magento 2 : Proxy and Factory generation in unit tests scope

I'm trying to create an Unit Test for a Magento 2 (version 2.2.0) Class that have a Proxy Class injected in constructor.
According to the Magento documentation, Proxies are generated code, like Factories.
However, in Unit Test scope (code generation is in dev/tests/unit/tmp/generated directory), Proxies class are not generated. Only Factories is generated.
Is there any reason why Proxy Class is not generated in Unit Test scope ?
Hypothesis : according to the documentation, the Proxy injection should be in di.xml configuration file
<type name="FastLoading">
<arguments>
<argument name="slowLoading" xsi:type="object">SlowLoading\Proxy</argument>
</arguments>
</type>
instead of injecting directly in constructor :
class FastLoading
{
protected $slowLoading;
public function __construct(
SlowLoading\Proxy $slowLoading
){
$this->slowLoading = slowLoading;
}
}
So, inject a Proxy Class directly in constructor is a bad practice ?
Another question for Factory generation in unit test scope, assuming the following Factory generated :
// dev/tests/unit/tmp/generated/code/Magento/Framework/Api/SearchCriteriaBuilderFactory.php
namespace Magento\Framework\Api;
class SearchCriteriaBuilderFactory
{
public function create(array $data = [])
{
}
}
What is the reason that the create() method generated is empty and so return null in unit test scope ?
Thanks.
Firstly, assumption that proxy class should be configured in xml instead of injecting it in constructor is wrong because you are not able to configure arguments that are not already expected by constructor. Therefore both are complementary.
You can configure the class constructor arguments in your di.xml in the argument node. The object manager injects these arguments into the class during creation. The name of the argument configured in the XML file must correspond to the name of the parameter in the constructor in the configured class.
Secondly, in production you are using different Object Manager with different configuration of dependencies. When you are creating factory with developer/production object manager at some stage there is injected dynamic factory object responsible for generation of factory (see pic below)
Respectively, for developer mode dynamic class is
vendor/magento/framework/ObjectManager/Factory/Dynamic/Developer.php and for
production mode
vendor/magento/framework/ObjectManager/Factory/Dynamic/Production.php
In case of ObjectManager for UT it is a more or less a wrapper for mockery with some additional utilities and therefore it will not generate any real class. Actually, ::getObject() will throw an exception on 161 line of code. As you can see there it is because there is nothing more but some reflection magic.
Regarding the proxy question, in light of ad.1, you solution is not even possible plus, the proxy class is not generated for the same reasons as with factory.
A little bit more from UT perspective, I can't imagine situation when you would need any of the autogenerated class. All dependencies should be mocked and generated class will never be tested directly. Either, for factory or proxy, create mock like:
$mockSearachCriteriaBuilder = $this->getMockBuilder(Magento\Framework\Api\SearchCriteriaBuilderFactory::class)->disableOriginalConstructor()->setMethods([set_your_methods_stubs]->getMock()
And then inject it as a dependency in constructor of class under test eg.
$this->om = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
$this->om->getObject('your\class\name', ['searchCriteriaBuilder' => $this->mockSearchCriteriaBuilder];
This is just an example but it shows that even if your question was interesting, the problem does not exist because the real solution lies in a totally different approach.
Update:
No, presence of class is not required for a mock, the type is what is all about so if mock can act like a given type then source class do not have to exist.
class FactoryTest extends \PHPUnit_Framework_TestCase
{
public function testSomething()
{
$mock = $this->getMock(UnknownClass::class, ['test']);
$mock->expects($this->once())->method('test')->willReturn(true);
$this->assertTrue($mock->test());
}
}
The test above will pass but there is nothing like UnknownClass. Also, there is no createMock method. Unit Test is all about isolation. If test requires anything more than class under test then it violate this principle. And here is where mockery comes in handy.

How to test a method that calls another class' method?

I'm fairly new to unit testing. I've recently encountered a problem where I test a method that does one thing only - it calls a method of an object that's part of the class. The class that this object represents has it's own unit tests.
I understand that this method may change in time, and when it does the test should inform me about if the expected result it. But what can I test in such a method?
My code:
public class MyClassToBeTested
{
private CustomType myObject;
private const myParameter = 2;
(...)
public string MyProperty
{
get
{
return myObject.DoYourStuff(myParameter);
}
}
}
This sounds like you need to capture the call to the underlying object and inspect it (or at least verify that the call has been made). I would mock this object and inject a reference to it (Inversion of Control).
By injecting the object you can provide the real object at deploy time, and the mock during testing.
If something is dependent on something else. i.e. method calls another method, then you should mock it, or simulate its behaviour.

How do I mock static property Groovy?

I saw a lot of examples how to mock a static method in a class.
I want to mock a static property of the class, that's initialized in its definition.
Something like:
class MyClass {
private static log = LogFactory.getLog(this)
}
I want to mock log property, in order that in my test it should be null, and it should not initialize itself.
Make static getter method :
static def getLog() {
return log
}
Use metaClass.staticto mock method:
Myclass.metaClass.static.getLog = {
return "what you need"
}
You are referring to 'log' as a property, but it's actually not. Assuming that MyClass is a Groovy class, then a property is declared without a private or public modifier, like this:
class MyClass {
static log = LogFactory.getLog(this)
}
What you have is a private static variable with an initializer. In this case, no property access methods are generated, and the variable is not considered a property.
Because it's a private variable with an initializer, and you state that you don't want to change the class, you cannot stop the initializer from running. So the best thing to do here is to mock out the getLog() call to return null. A simple but crude approach would be:
LogFactory.metaClass.static.getLog = {
return null
}
but this will kill all log lookup in the whole application. This could be OK for a simple unit test, but a more specific solution is to use Groovy mocks (MockFor and StubFor), which will allow you to mock the getLog call in a specific block of code. You can find out more here
As a matter of interest, why do you not want the log variable initialized?