Can't mock Path extension function - mockk

This test fails. I got the package-class string from the decompiled class.
#Test
fun `path does not exist`() {
mockkStatic("kotlin.io.path.PathsKt__PathUtilsKt")
val nonExistentPath: Path = mockk(relaxed = true)
every { nonExistentPath.exists() } returns false
assertThat(nonExistentPath.exists()).isFalse
}

As a workaround don't call the extension function, but a JVM function via File:
every { nonExistentPath.toFile().exists() } returns false
and use
Path.toFile().exists()
in the production code.

Related

What is the purpose of getTonightFromDatabase() in the Android Kotlin Room codelabs?

I am trying to understand codelab 6.2 Coroutines and Room in Android Kotlin Fundamentals. Class SleepTrackerViewModel includes (with comments added by me):
private var tonight = MutableLiveData<SleepNight?>()
private suspend fun getTonightFromDatabase(): SleepNight? {
var night = database.getTonight() // this gets the most recent night
// Return null if this night has been completed (its end time has been set).
if (night?.endTimeMilli != night?.startTimeMilli) {
night = null
}
return night
}
fun onStartTracking() {
viewModelScope.launch {
val newNight = SleepNight()
insert(newNight)
tonight.value = getTonightFromDatabase()
}
}
fun onStopTracking() {
viewModelScope.launch {
val oldNight = tonight.value ?: return#launch
oldNight.endTimeMilli = System.currentTimeMillis()
update(oldNight)
}
}
I don't understand why the method getTonightFromDatabase(), which is called only from onStartTracking(), is needed. It seems the last statement in onStartTracking() could be replaced by:
tonight.value = newNight
I also don't understand why the conditional in getTonightFromDatabase() is needed.
One of the reasons is that the nightId in the SleepNight data class is auto generated by SQL.
If the code would do tonight.value = newNight then the nightId wouldn't be the same than the one in the database. That would cause the update call in onStopTracking to end (update) the wrong night.
Note too that the method getTonightFromDatabase is called from a later version of SleepNightViewModel:
private var tonight = MutableLiveData<SleepNight?>()
init {
initializeTonight()
}
private fun initializeTonight() {
viewModelScope.launch {
tonight.value = getTonightFromDatabase()
}
}
When the application restarts, getTonightFromDatabase is called to set the instance variable tonight (which would more accurately be called latestNight). If the most recent night was complete, the completeness check would ensure that null is returned, prevent the entry from being modified.

Recursively filter and map a list of properties

I'm using Kotlin reflection to check if attributes that have a certain annotation are null.
Given the following example:
data class DataClass(
#SomeRandomAnnotation
val otherAnnotated: String?,
val inner: InnerClass
)
data class AnotherDataClass(
#SomeRandomAnnotation
val annotatedProperty: String?,
val dataClass: DataClass
) {
fun checkCreditAnalysisConstrain() {
print(checkConstrain(this))
}
}
And the function that checks it:
fun checkConstrain(parentClass: Any): List<String> {
val filter = parentClass::class.memberProperties.filter {
if (memberIsDataClass(it)) checkConstrain(getMemberPropertyInstance(parentClass, it))
hasAnnotation(it) && propertyIsNull(it, parentClass)
}
return filter.map { formatResult(parentClass, it) }
}
The idea is that the function is going to iterate through the attributes of my classes checking if they have the annotation and checking if the value is null.
If the property is a data class, the code evaluates the properties of the childs, recursively.
After that, I map the results, transforming the KProperty's into a simple String that is human readable, containing the class name and the attribute name.
The problem is that the above code does not work as expected. The properties returned are only the properties from the first-level class.
If, instead of doing a filter, I just run a forEach and print the result, I get the expected attributes. So I'm pretty sure it's related to the recurring inside a filter.
Do you see any way of doing this in a more functional way? I'm just concerned I won't need a "temp" list and add values to the list and reset it afterwards.
Your function recursively calls itself, but does nothing with the returned list of that recursive call. That's why you only get results for the top-level class.
Also, in my opinion, you shouldn't rely on side effects happening from your filter call. It probably works, but the function's documentation does not provide a guarantee that it will be called exactly once per item in the collection. So there should be a separate for-loop to do the recursive calls, and the result should be added onto existing results.
fun checkConstrain(parent: Any): List<String> {
val memberProperties = parent::class.memberProperties
var result = memberProperties
.filter { hasAnnotation(it) && propertyIsNull(it, parent) }
.map { formatResult(parent, it) }
memberProperties.filter { memberIsDataClass(it) }
.mapNotNull { getMemberPropertyInstance(parent, it) }
.forEach { result += checkConstrain(it) }
return result
}
You didn't provide code for several of the functions you used. This is what I used for them:
val KProperty<*>.returnTypeClass get() = this.returnType.classifier as? KClass<*>
fun <T> memberIsDataClass(member: KProperty<T>) = member.returnTypeClass?.isData == true
fun <T> getMemberPropertyInstance(parent: Any, property: KProperty<T>) = property.getter.call(parent)
fun <T> hasAnnotation(property: KProperty<T>) = property.annotations.firstOrNull { it.annotationClass == SomeRandomAnnotation::class } != null
fun <T> propertyIsNull(property: KProperty<T>, parent: Any) = getMemberPropertyInstance(parent, property) == null
fun formatResult(parent: Any, property: KProperty<*>) = "$parent's property(${property.name}) is annotated with SomeRandomAnnotation and is null."

Using chained partial mocks on an interface used in an artisan command

I'm trying to unittest an artisan command in Laravel 5.3. The command calls on functions in a class that is provided to the command constructor as an interface. That interface calls on functions in another class. This is the general setup.
class MyCommand
{
public function __construct(MyRepositoryInterface $interface)
{
...
$this->interface = $interface;
...
}
public function fire()
{
$this->interface->useTheSecondClass();
}
}
class MyRepository implements MyRepositoryInterface
{
public function __construct(MySecondRepositoryInterface $second)
{
...
$this->second = $second;
...
}
public function useTheSecondClass()
{
$response = $this->second->getSomeValue();
}
}
class MySecondRepository implements MySecondRepositoryInterface
{
/**
* #return Some\External\Client
*/
public function getExternalClient()
{
....
return $external_client;
}
public function getSomeValue()
{
$client = $this->getExternalClient();
$something = $client->doSomething();
Event::fire('some event based on $something`);
return $something;
}
}
I'm attempting to mock the variable returned in MySecondRepository -> getExternalClient() so that I can fake an external API call and use that faked data to test both the MySecondRepository -> getSomeValue() and MyRepository -> useTheSecondClass() functionalities as called from the MyCommand class as such.
public function testMyCommand()
{
$external_client_mock = Mockery::mock("Some\External\Client");
$external_client_mock->shouldReceive("doSomething")
->andReturn("some values");
$second_repository_mock = Mockery::mock("MySecondRepositoryInterface")
->makePartial();
$second_repository_mock->shouldReceive("getExternalClient")
->andReturn($external_client_mock);
$resource = new MyRepository($second_repository_mock);
$this->app->instance("MyRepositoryInterface", $resource);
$class = App::make(MyCommand::class);
$class->fire();
...
}
I have used this exact same mock chain successfully to test the $resource variable directly (e.g., testing $resource->useTheSecondClass() directly, not through MyCommand), but in this situation, while $second_repository_mock->getExternalClient() is mocking correctly, the test is still expecting there to be a mocked expectation for $second_repository_mock->getSomeValue(). Since $second_repository_mock is set to a partial mock, I don't understand why it's still looking for all functions to be mocked.
If I remove the $external_client_mock part and fully mock $second_repository_mock my tests directly related to the artisan command work, however I'd like to test that the event triggered in getSomeValue() is dealt with properly from the artisan command, which I can't do if I can't use the partial mock.
Does anyone have any insight on why this isn't working?
You are trying to mock an interface which makes no sense. Interfaces have no concrete implementations to use. This results in the faulty code. Just mock your repositories and it will work.
EDIT
Just ran the tests with the following refactor and they went to green:
public function testMyCommand() // phpcs:ignore
{
$external_client_mock = \Mockery::mock("App\Client");
$external_client_mock->shouldReceive("doSomething")
->andReturn("some values");
$second_repository_mock = \Mockery::mock("App\MySecondRepository[getExternalClient]")
->shouldIgnoreMissing();
$second_repository_mock->shouldReceive("getExternalClient")
->andReturn($external_client_mock);
$resource = new MyRepository($second_repository_mock);
$this->app->instance("App\MyRepositoryInterface", $resource);
$class = \App::make(\App\MyClass::class);
$class->fire();
}
The only major difference is that you had App\MyCommand in the second to last line and not App\MyClass.

Mocking an extension function with Mockito

How can I test an extension function with Mockito? It doesn't seem to work nicely.
This is my extension function
fun <T> CrudRepository<T, String>.findOneById(id: String): T? {
val o = findById(id)
return if (o.isPresent) o.get() else null
}
And this is what I'm trying to test
#Test
fun getIslandById() {
//given
BDDMockito.given(islandRepository.findOneById("islandId1"))
.willReturn(IslandEntity(tileList, "1", "islandId1")) //findOneById is my extension function
//when
val island = islandService.getIslandById("islandId1")
//then
Assertions.assertThat(island?.id).isEqualTo("islandId1")
}
But the preceeding test throws the following error
org.mockito.exceptions.misusing.WrongTypeOfReturnValue:
IslandEntity cannot be returned by findById()
findById() should return Optional
Any ideas?
Instance extension functions can be mocked like this with a little help of mockito-kotlin:
data class Bar(thing: Int)
class Foo {
fun Bar.bla(anotherThing: Int): Int { ... }
}
val bar = Bar(thing = 1)
val foo = mock<Foo>()
with(foo) {
whenever(any<Bar>().bla(any()).doReturn(3)
}
verify(foo).apply {
bar.bla(anotherThing = 2)
}
As I said in a comment above, in the bytecode extension functions are nothing more than static functions which accept receiver as a first argument. Therefore you can't mock an extension function with a Mockito since it is not able to mock static functions.
What you can do, in case that findById(id) is implemented by Repository and not another extension function, is next:
Mock return value of findById(id) instead.
Take a look at the sample code below:
#Test
fun getIslandById() {
//given
BDDMockito.given(islandRepository.findById("islandId1"))
.willReturn(Optional.of(IslandEntity(tileList, "1", "islandId1"))) //mock findById function
//when
val island = islandService.getIslandById("islandId1")
//then
Assertions.assertThat(island?.id).isEqualTo("islandId1")
}
This way you are indirectly mocking your extension by providing it a mocked value that you want it to operate on.
Note: Error that you posted above says that your findById(id) should return an optional. So, wrap your return value of findById(id) function with an optional by calling Optional.of(result).

NSubstitute if-else condition

Good day! I need your help, I have next tests:
[SetUp]
public void SetUp()
{
controller = Substitute.For<IApplicationController>();
view = Substitute.For<ICamerasView>();
presenter = new CamerasPresenter(controller, view);
argument = InitializeDevicesList();
presenter.Run(argument);
}
private List<string> InitializeDevicesList()
{
List<string> devicesList = new List<string>();
Device device = new Device();
devicesList.Add(device.Name);
return devicesList;
}
[Test]
public void RunIfDeviceListIsNotEmpty()
{
view.DidNotReceive().SetUIOnNoConnectedDevices();
view.Received().FillCamerasListView(argument);
view.Received().Show();
}
which actually tests next code
public override void Run(List<string> argument)
{
connectedCameras = argument;
if(connectedCameras.Count == 0)
{
SetUIOnNoConnectedDevices();
}
else
{
FillCamerasListView();
}
View.Show();
}
And my issue is that FillCamerasListView method isn't calling in test. But as it expected it called in Run method in this case. So, I can't imagine what is the problem, so I will be very appreciated for your help. Thanks for your time!
This example passes. The problem appears to be something in your example that is changing the argument passed to FillCamerasListView as discussed in the comments.
A few options:
Modify the code to match the test's expectation. i.e. pass the argument given to Run on to FillCamerasListView.
Use view.ReceivedWithAnyArgs().FillCamerasListView(null) to assert a call was made without worry about the specifics of the argument passed.
Use view.Received().FillCamerasListView(Arg.Is<List<string>>(x => Matches(x, argument)), where Matches is your own code which determines whether the argument given is correct based on the argument passed to Run.

Resources