Android - Kotlin - JUnit 5: How to test private methods? - android

General software organization question:
I have a Kotlin class file (under src/) with some 10 methods, only 1 of which should be visible outside the class (the rest should be private).
I also have a dozen JUnit test methods for testing the abovementioned methods, and I put all those in a different class file (under src/test/).
But the test methods cannot see the methods they're supposed to test, so I had to remove the private modifiers from the source code :(
What's a man to do?
JUnit 5, Kotlin for Android, on IntelliJ, if that matters.
Thank you

What's a man to do?
Check this question and answer.
Either a) test the private details indirectly by testing the public interface (which goes through the private methods) or b) Use #VisibleForTesting annotation to make a private function public for testing but clearly flagged as something that should not be used otherwise.

Related

Package Level Protection in Kotlin [duplicate]

In Java, we have the package protected (default) modifier for classes, which allows us to have many classes in a single package but exposes only a few and keeps the logic encapsulated.
With Kotlin this doesn't seem to be the case. If I want a few classes to be visible to each other but no further, I have to use a private modifier which limits visibility to a single file.
So if you want 10 classes in a package but only one of them to be public, you'd have to have one huge file with all the classes in it (and private all over the place).
Is this normal practice or there is a way to achieve some similar modularity in Kotlin?
I don't understand: if they have the notion of a package, why did they get rid of package protected access?
Update: We might have package protected visibility after all
see the discussion here
Update: If you read through the discussion and still think this is a must-have feature for the language, please vote here
Kotlin, compared to Java, seems to rely on packages model to a lesser degree (e.g. directories structure is not bound to packages). Instead, Kotlin offers internal visibility, which is designed for modular project architecture. Using it, you can encapsulate a part of your code inside a separate module.
So, on top level declarations you can use
private to restrict visibility to the file
internal to restrict visibility to the module
At this point, there is no other option for visibility restriction.
As a workaround for me on android I've created #PackagePrivate annotation and lint checks to control access. Here you can find the project.
Lint checks are obviously not that strict as compiler checks and some setup needed to fail the build on errors. But android studio picks up lint checks automatically and shows error immediately while typing. Unfortunately I don't know a way to exclude annotated members from autocomplete.
Also, as lint is a purely compile-time tool, no checks at runtime performed.
As #hotkeys points out, you can use the internal keyword in a module or you can put all classes that would otherwise belong in a package inside a single file, but sticking several classes in a file may be a questionable design decision.
For me, the package visibility is helpful for its documenting value. I want to know what public interface some package is presenting to the rest of the project, hide factory implementation classes and so on.
So even if it's possible to access package-private classes and methods in Java, I still choose to use the package modifier.
For this I created a project with a single annotation:
package com.mycompany.libraries.kotlinannotations;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Documented
#Retention(SOURCE)
#Target({ TYPE, METHOD, CONSTRUCTOR })
/**
* Use in Kotlin code for documentation purposes.
*
* Whenever a Kotlin class or method is intended to be accesible at package level only.
*
*/
public #interface PackagePrivate {
}
Then I can use this annotation in any Kotlin project.
The second step, which I haven't done yet, is creating a PMD rule to enforce this with maven (or any other build tool for that matter) and also be able to see violations of the rule in my IDE with the pmd plugin.
There no is full Kotlin support in pmd at this moment but it seems to be expected at some point.
A near-replacement for package private visibility is available using the opt-in requirements feature (credit to pdvrieze on Kotlin discussions). This is the annotation syntax typically used to flag experimental features in an API.
To use it, create an annotation denoting package private declarations:
#RequiresOptIn(message = "Only to be used in MyPackage")
#Retention(AnnotationRetention.BINARY)
annotation class MyPackagePrivate
Then annotate any methods you want to be package private with it:
#MyPackagePrivate
fun aPackagePrivateMethod() {
// do something private within a package
}
In this way a warning will be generated on any method that calls the annotated method unless the calling method is itself annotated with the corresponding #OptIn annotation, here shown at class level:
#OptIn(MyPackagePrivate::class)
class AClassInThePackage {
fun userOfPackagePrivateMethod() {
aPackagePrivateMethod()
}
}
This, then, produces a similar effect to Java's package private, except that calling methods need to explicitly opt in to using a package private declaration.
If it is desired to generate an error rather than a warning, the level parameter of #RequiresOptIn can be specified:
#RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "Only to be used in MyPackage")
// annotation declaration as before
Package-based protection is pointless in Kotlin because packages themselves are unprotected
In Java, package was tied to directory structure. So if you put your classes in com\example\yoursecretengine, any attempt (deliberate or accidental) to add a rogue class there would be easily noticeable. This is the kind of security we've depended on.
Kotlin removes the ties between directory and package, so I can put my class in "my" directory (eg.src\java\pl\agent_l\illegalaccess) yet declare its package as com.example.yoursecretengine - and gain access to all the properties you've meant as package private.
In fact, a Kotlin project works perfectly without ANY package declarations. This only highlights that packages are "more what you'd call guidelines than actual rules". They're a convenience feature, useful only to unclutter namespace and nothing more.
Relevant quotes from kotlinlang:
unlike many other languages, Kotlin packages do not require files to have any specific locations w.r.t. itself; the connection between a file and its package is established only via a package header.
And:
an absence of a package header in a file means it belongs to the special root package.

How much of a mock is a Mockito mock?

This is a serious question, I promise. I've spent the last 2 hours reading as many definitions of Mock that I could find and none explain this to me.
I've got a class I want to test and that class requires a mapper class as part of it's primary constructor:
open class PoiListViewModel #Inject constructor(
private val mapper: PoiMapper
) : ViewModel() {
In my unit test I have the following code:
//Mock objects needed to instantiate the class under test
#Mock lateinit var mapper: PoiMapper
// Class being tested
lateinit var poiListViewModel: PoiListViewModel
#Before
fun setup() {
MockitoAnnotations.initMocks(this)
poiListViewModel = PoiListViewModel(mapper)
}
My question to you all smart developers is, what exactly is a mock? And specifically how much of my original class does it replicate?
I'll tell you my assumed definition. A mock is a fake stand-in class that stands in for my real class, but that it does nothing except keep track of what method calls get sent to it. If I want the mock to have any functionality I need to stub that functionality in.
At least that's my ignorant view of mocks. But I'm apparently wront because in my unit test, my "mock" mapper class seems to be an actual mapper class. If I debug my unit test I see it walk through all the code of my mapper class. I see it returning converted data.
Here's the mapper class code (if it matters):
open class PoiMapper #Inject constructor() {
fun mockTest(num: Int): Int{
return num *23
}
fun mapToPresentation(domainModel: Poi_Domain): Poi_Presentation {
var test = 3
var results = mockTest(test)
return Poi_Presentation(domainModel.id,domainModel.name,domainModel.description,
domainModel.img_url,domainModel.latitude,domainModel.longitude,domainModel.imgFocalpointX,
domainModel.imgFocalpointY,domainModel.collection,domainModel.collectionPosition,
domainModel.release,domainModel.stampText)
}
}
Can someone explain it to me, how much of a mock is a Mockito mock? Did I instantiate the mocks incorrectly? Can someone give me a better way to think of mocks so I can wrap my head around all this?
Your understanding of mocks is correct. You're bumping into Kotlin's final-by-default behavior, as an implementation detail of Mockito.
Mockito mocks (as distinct from Mockito spies) are defined to take the place of your class instance. Unless you've stubbed them to return otherwise, they record all interactions and return default dummy values (zero, an empty string, an empty list, null, etc). You can confirm that they collaborated correctly with your system-under-test by stubbing return values (when/thenReturn), stubbing specific behaviors for methods (when/thenAnswer), or by checking that certain methods were called (verify) and retrieving the specific instances they were called with (ArgumentCaptor).
Under the hood, Mockito achieves this by generating a subclass of the class you're mocking, and overriding all methods to delegate to Mockito's internal handler. This is what gives Mockito the power to override the behavior silently: Your system-under-test thinks it's calling your dependency, but your code is using Java's virtual method dispatch to override your dependency's behavior.
Here's the trick: Java methods are virtual by default, unless you mark them final. In Kotlin, functions are closed by default, unless you mark them open. (I'm going to keep calling them final, because that's the definition at play in the JVM or Android Dexer, which is reading the bytecode that Kotlin generates anyway.) When the virtual machine is sure of a reference's type based on static typing, and you're calling a final method, the compiler is allowed to inline that implementation's code (JLS 8.4.3.3) because you've asserted that the method cannot be overridden and any code that tries to override it will fail at compilation. Mockito's generated code isn't compiled this way, and Mockito can't detect this case.
In your case, mapToPresentation is not open, so the compiler sees it as final and does not keep the virtual method dispatch that would let Mockito override the behavior. Your definition of mocking is right, and your code would be right, except that Kotlin is closed-by-default where Java is open-by-default. Besides, you really do rely on the function being open, because you'd like it to be called with a different implementation than the one in the function.
Trivially, you could just make all functions open that you intend to override, or use Mockito 2.1+'s built-in feature to mock final methods. However, as a general best practice you want an object that behaves like a PoiMapper even if it doesn't follow your specific PoiMapper implementation. That might be a good reason for an interface/impl split, such that your class PoiListViewModel takes a PoiMapper interface. In production you can provide a PoiMapperImpl as you have, but Mockito can generate an arbitrary implementation of the PoiMapper interface without worrying about whether PoiMapperImpl and its functions are open or closed.
Have you added the annotation
#RunWith(MockitoJUnitRunner.class)
to your test class?

Using multiple TestRules in the same test

I have written a custom TestRule to use with my Android test suite. It populates a table in the database used by the app under test. Now I need to use this DataRule along with ActivityTestRule. Can I have two fields of my test class annotated with #Rule? How do I control the order in which the rules are applied?
Background:
The Android API provides a TestRule for starting an Activity which is the core class for every app with a UI. My app has a database and I have several tests which require the database to be pre-populated with some known data. Previously, with JUnit3-based tests, I used an abstract superclass to centralize the code which prepares the database and then I extended this superclass for different test cases. Now I am trying to implement the same logic using JUnit 4. I learned recently that test rules are one way to provide logic which is reused across tests, so I am trying to move the logic from my superclass to a test rule. Is this an appropriate way to achieve my goal?
You certainly can have multiple #Rule fields in a single test. I'm not sure what the default ordering of rule application is, or if it's even well-defined. However, if ordering is important you can control it with a RuleChain
which allows you to define an order on how rules are applied when you have multiple rules in a test case.
From the Javadoc...
#Rule
public RuleChain chain = RuleChain
.outerRule(new LoggingRule("outer rule")
.around(new LoggingRule("middle rule")
.around(new LoggingRule("inner rule");
RuleChain is deprecated and since 4.13 you can make use of order parameter in Rule.
org.junit.Rule annotation has a parameter "order" which you can use to order the Rules in one file.
check the doc in the link below
Rule.java
If you're using JUnit for your tests, which I personally recommend, it's not recommended to have multiple rules in the same file, because a Rule is a unit of your test, and as you're doing unit tests, you should have just one Rule per file.
If you need to create some sort of data before you run your tests you should use the #Before and then load the necessary information.
More on this can be found here: http://junit.sourceforge.net/javadoc/org/junit/Before.html
If you have to load the same data in multiple classes, I would recommend you to create a class with your logic, extend that class in your test class and then create a method annotated with #Before an call your super class method.
Hope that helps

Inheritance of TestCases on Android

I was wondering if it was good practice to subclass the test cases on Android. I mean, I need to test a lot of Parcelable objects and I could create a class like GenerericParcelableAndroidTestCase to test all these objects.
I also have a problem implementing it, I have something like this:
public class GenericParcelableTest extends AndroidTestCase {
private Parcelable p = null;
GenericParcelableTest(Parcelable p) {
this.p = p;
}
public void testDescribeContents() throws Exception {
assertEquals(0, p.describeContents());
}
}
And that:
public class AttachmentTest extends GenericParcelableTest {
public AttachmentTest() {
super(new Attachment());
}
}
Attachment implements Parcelable of course.
It returns me this error:
junit.framework.AssertionFailedError: Class GenericParcelableTest has no public constructor TestCase(String name) or TestCase()
I mean, I know that I created no empty constructor but why would I need one?
And generally, is there some known issues with this approach? If not why is there very few article on this topic on the internet (and actually some say even that it's not a good idea).
I have this conversation quite often when introducing new team members to unit testing. The way I explain it is by stating that your tests are first class citizens of your code base (no pun intended), they are susceptible to the same technical debt as any other part of your code base and have equivalent (maybe more?!) importance as that of the runtime code.
With this mindset, the questions begins to answer itself; if it makes sense from an OO perspective to use inheritance (i.e. your subclass is a insert name of test superclass) then subclass away. However, like any abuse of inheritance ever, be careful...the minute you add a test case that doesn't rely upon that superclass behaviour you may have a code smell.
In this scenario, it's likely (perhaps 90% of the time?) it is a separation of concern issue within the code being placed under test, i.e. the "unit" under test isn't actually (one) unit but has combinatorial behaviour. Refactoring that code to do one thing would be a good way of allowing your super-class test case to live on. However, watch this super class test case like a hawk...the minute you see booleans being added to signatures to "allow that similar but not the same" test case to run under your once unpolluted super class then you have a problem, a tech debt problem that is no different to your runtime code.
At last check AndroidTestCase depends on an Activity context so it's likely best described as an integration test which tend to regularly have boilerplate super-class test behaviour. In this case, try to narrow the focus of your superclass to the use case under test...i.e. extends LoginUseCase or extends LoginScenario to better "bucket" those subclasses in the first instance. This will help guide would be extenders as to whether they should be using it for their non-login scenario. Hopefully, conversation will ensue and tech debt accumulation be avoided!
Regarding your error, in JUnit3 do what #Allen recommends, if moving to JUnit4 with something like Robolectric then explore using Rules as well as #BeforeClass.
Personal note
I have only felt the need to write test super classes for pseudo-unit tests that mock an API end point (akin to MockWebServer if you are familiar with that product) and DAO integration tests whereby an in-memory db is started and torn down over the lifecycle of each test (warning - slow (but useful) tests!)
junit.framework.AssertionFailedError: Class GenericParcelableTest has no public constructor TestCase(String name) or TestCase()
You get this error because JUnit needs to be able to construct an instance of your test class. It only knows how to do this using no-arg, or single string constructors.
Instead of performing initialization in your constructor, you should put it in the setUp() method. This will let you use the default constructor while still initializing the object before the test method is called.

How does Dagger 2 make testing easier on Android?

One of the best advantages of using DI is it makes testing a lot easier (What is dependency injection? backs it too). Most of DI frameworks I've worked with on other programming languages (MEF on .NET, Typhoon on Obj-C/Swift, Laravel's IoC Container on PHP, and some others) allows the developer do register dependencies on a single entry point for each component, thus preventing the "creation" of dependency on the object itself.
After I read Dagger 2 documentation, it sounds great the whole "no reflection" business, but I fail to see how it makes testing easier as objects are still kind of creating their own dependencies.
For instance, in the CoffeMaker example:
public class CoffeeApp {
public static void main(String[] args) {
// THIS LINE
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();
}
}
Even though you're not explicitly calling new, you still have to create your dependency.
Now for a more detailed example, let's go to an Android Example.
If you open up DemoActivity class, you will notice the onCreate implementation goes like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Perform injection so that when this call returns all dependencies will be available for use.
((DemoApplication) getApplication()).component().inject(this);
}
You can clearly see there is no decoupling from the DI component, to the actual code. In summary, you'd need to mock/stub ((DemoApplication) getApplication()).component().inject(this); on a test case (if that's even possible).
Up to this point, I am aware Dagger 2 is pretty popular, so there is got to be something I am not seeing. So how does Dagger 2 makes testing classes easier? How would I mock, let's say a network service class that my Activity depends on? I would like the answer to be as simple as possible as I'm only interested in testing.
Dagger 2 doesn't make testing easier
...beyond encouraging you to inject dependencies in the first place, which naturally makes individual classes more testable.
The last I heard, the Dagger 2 team were still considering potential approaches to improving support for testing - though whatever discussions are going on, they don't seem to be very public.
So how do I test now?
You're correct to point out that classes which want to make explicit use of a Component have a dependency on it. So... inject that dependency! You'll have to inject the Component 'by hand', but that shouldn't be too much trouble.
The official way
Currently, the officially-recommended approach to swapping dependencies for testing is to create a test Component which extends your production one, then have that use custom modules where necessary. Something like this:
public class CoffeeApp {
public static CoffeeShop sCoffeeShop;
public static void main(String[] args) {
if (sCoffeeShop == null) {
sCoffeeShop = DaggerCoffeeShop.create();
}
coffeeShop.maker().brew();
}
}
// Then, in your test code you inject your test Component.
CoffeeApp.sCoffeeShop = DaggerTestCoffeeShop.create();
This approach works well for the things you always want to replace when you are running tests - e.g. Networking code where you want to run against a mock server instead, or IdlingResource implementations of things for running Espresso tests.
The unofficial way
Unfortunately, it the official way can involve a lot of boilerplate code - fine as a one-off, but a real pain if you only want to swap out a single dependency for one particular set of tests.
My favourite hack for this is to simply extend whichever Module has the dependency you want to replace, then override the #Provides method. Like so:
CoffeeApp.sCoffeeShop = DaggerCoffeeShop.builder()
.networkModule(new NetworkModule() {
// Do not add any #Provides or #Scope annotations here or you'll get an error from Dagger at compile time.
#Override
public RequestFactory provideRequestFactory() {
return new MockRequestFactory();
}
})
.build();
Check this gist for a full example.
"allows the developer do register dependencies on a single entry point for
each component" - analogues in Dagger 2 are the Modules and Components where you define the dependencies. The advantage is that you don't define the dependencies directly in your component thus decoupling it so later when writing unit tests you may switch the Dagger 2 component with a test one.
"it sounds great the whole "no reflection" business" - the "no reflection" thing is not the "big deal" about dagger. The "big deal" is the full dependency graph validation at compile time. Others DI frameworks don't have this feature and if you fail to define how some dependency is satisfied you will get an error late at runtime. If the error is located in some rarely used codepath your program may look like it is correct but it will fail at some point in the future.
"Even though you're not explicitly calling new, you still have to create your dependency." - well, you always have to somehow initiate dependency injection. Other DI may "hide"/automate this activity but at the end somewhere building of the graph is performed. For dagger 1&2 this is done at app start. For "normal" apps (as you shown in the example) in the main(), For android apps - in the Application class.
"You can clearly see there is no decoupling from the DI component, to the actual code" - Yes, you are 100% correct. That arises from the fact that you don't control directly the lifecycle of the activities, fragments and services in Android, i.e. the OS creates these objects for you and the OS is not aware that you are using DI. You need manually to inject your activities, fragments and services. At first this seem seems awkward but in real life the only problem is that sometimes you may forget to inject your activity in onCreate() and get NPE at runtime.

Categories

Resources