Test Error Maven RoboGuice [No implementation for "class" was bound] - android

I have an Android Project which contains two source folders src and test. In test, I have my test classes and some mock classes. I'm using RoboGuice dependency injection for Android in some class I wrote tests for.
The tests run perfectly fine in Eclipse on an emulator but fail using maven clean install.
No implementation for com.Store<com.MessageEvent> was bound.
The tests fail at setUp when using the injector.
mm = Guice.createInjector(new TestModule()).getInstance(MM.class);
And here is my binding module:
public class TestModule implements Module{
#Override
public void configure(com.google.inject.Binder binder) {
binder.bind(Context.class).toInstance(getContext());
binder.bind(Scheduler.class).to(MockScheduler.class);
binder.bind(EventManager.class).to(MockEventManager.class);
binder.bind(new TypeLiteral<Store<Message>>(){}).to(new TypeLiteral<JsonStore<Message>>(){});
binder.bind(new TypeLiteral<Store<MessageEvent>>(){}).to(new TypeLiteral<JsonStore<MessageEvent>>(){});
}
#Provides
JsonStore<MessageEvent> provideMessageEventJsonStore(Context context){
return new JsonStore<MessageEvent>(context, "message_events_test.json", MessageEvent.class);
}
#Provides
JsonStore<Message> provideMessageJsonStore(Context context){
return new JsonStore<Message>(context, "message_manager_test.json", Message.class);
}
}
Why would the exception be thrown while running tests in Maven but not in Eclipse?

"No implementation was bound" means that you need to add a line for this particuar class in your modules file.

Related

Mockito is not recognized by Android (Kotlin)

Here is my class test:
private const val FAKE_STRING = "APP NAME"
#RunWith(MockitoJUnitRunner::class)
class UnitTestSample {
#Mock
private lateinit var mockContext: Context
#Test
fun readStringFromContext_LocalizedString() {
// Given a mocked Context injected into the object under test...
`when`(mockContext.getString(R.string.app_name))
.thenReturn(FAKE_STRING)
val myObjectUnderTest = ClassUnderTest(mockContext)
// ...when the string is returned from the object under test...
val result: String = myObjectUnderTest.getHelloWorldString()
// ...then the result should be the expected one.
assertThat(result, `is`(FAKE_STRING))
}
}
Here is a piece of my gradle.build.kt (Kotlin DSL):
plugins {
id("com.android.application")
kotlin("android")
kotlin("kapt")
kotlin("android.extensions")
id("com.onesignal.androidsdk.onesignal-gradle-plugin")
jacoco
maven
}
dependencies {
...
//Test base
testImplementation("junit:junit:4.12")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.0.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.0.3")
androidTestImplementation("androidx.test:runner:1.2.0")
androidTestImplementation("androidx.test.espresso:espresso-core:3.2.0")
//Unit Tests
testImplementation("org.mockito:mockito-core:3.0.0")
testImplementation("org.mockito:mockito-inline:3.0.0") //support for kotlin final classes
//Android UI Test
androidTestImplementation("org.robolectric:robolectric:3.7.1")
}
As you can see, Android Studio doesn't reognize Mockito. I've already imported org.mockito.junit.MockitoJUnitRunner
I'm running this sample unit test under
src/test/java/.../UnitTestSample.kt
Do you have any idea on how to make it work?
Edit (Solution):
I finally made it work with some help of the comments section. The problem was caused by "maven" plugin import on plugins section, and I didn't see that because the base project I downloaded to convert my Gradle to DSL Kotlin had those plugins working. Somehow this was causing Mockito not to be available at compile time, as #MartinZeitler stated. According to #second, "Maven's runtime does not translate to gradle's runtimeOnly but instead compile".
The error message is pretty clear: an annotation argument must be a compile time argument.
Replace testImplementation with:
debugImplementation "org.mockito:mockito-android:3.2.4"
debugImplementation "org.mockito:mockito-inline:3.2.4"
Edit: Cleaned up the answer
For JUnit5 and mockito use the following dependencies (or newer) and scopes:
testImplementation("org.junit.jupiter:junit-jupiter-api:5.4.2")
compile("org.junit.jupiter:junit-jupiter-engine:5.4.2")
testImplementation("org.mockito:mockito-core:3.0.0")
testImplementation("org.mockito:mockito-junit-jupiter:3.0.0")
testImplementation("org.mockito:mockito-inline:3.0.0")
In your test use the Extension instead of the Runner (which is for JUnit 4).
#ExtendWith(MockitoExtension::class)
When running with the JUnit 5 dependencies of 5.0.3, I got the following error, so consider upgrading to a newer version (as shown in the dependencies above).
java.lang.NoSuchMethodError:
org.junit.platform.commons.support.AnnotationSupport.findAnnotation(Ljava/util/Optional;Ljava/lang/Class;)Ljava/util/Optional;
...
Suppressed: java.lang.NullPointerException
at org.mockito.junit.jupiter.MockitoExtension.afterEach(MockitoExtension.java:214)
For the maven to gradle conversion I used this site
https://sagioto.github.io/maven2gradle/

java.lang.NoClassDefFoundError: dagger.internal.Preconditions android dagger2

I am switching between two project using build flavor. I am using dagger2 and one project working fine but when switching another project and trying to run it showing below error:
java.lang.NoClassDefFoundError: dagger.internal.Preconditions
at common.di.DaggerAppComponent$Builder.appModule(DaggerAppComponent.java:35)
Here is my gradle dependency:
// Dependency Injection
annotationProcessor 'com.google.dagger:dagger-compiler:2.14.1'
implementation 'com.google.dagger:dagger:2.14.1'
compileOnly 'javax.annotation:jsr250-api:1.0'
The issue is mainly when adding appModule in AppComponent.
private AppComponent createAppComponent() {
return DaggerAppComponent.builder()
.appModule(new AppModule(this)) //Problem is here
.networkModule(new NetworkModule())
.build();
}
Finally, I have found the root cause. Its basically API level issue.
For android API level below 21 you need to add following dependency in build gradle file. Also change the Application class to MultiDexApplication like below:
In app build.gradle file:
Implementation 'com.android.support:multidex:1.0.3'
In you BaseApplication change Application class to MultiDexApplication:
public class BaseApplication extends MultiDexApplication {
#Override
public void onCreate() {
super.onCreate();
}
}

Kotlin Unit Test Not Finding Module Dependency Interface

I have an app module and a domain module. In my domain module I have an interface called Repository. In my app module I use dagger to inject an implementation for this into my class and this works fine.
When I then go to test it using a kotlin unit test, at runtime I get a NoClassDefFoundError.
I have also tried to include the domain module in my app modules dependencies like so but that also did not work:
testImplementation project(':domain')
Here are my current test dependencies and also how I'm including the module
implementation project(':domain')
testImplementation 'junit:junit:4.12'
testImplementation 'com.nhaarman:mockito-kotlin:1.5.0'
In my unit test I'm using it like this which could be the issue:
#Mock lateinit var mockRepo : Repository
Thanks to #Mark Keen, I was able to find a reported bug on the Jetbrains site.
This contained a solution from a user called #Calin. Adding the following to the projects's build.gradle file and triggering a gradle sync does the trick.
subprojects { subProject ->
afterEvaluate {
if (subProject.plugins.hasPlugin("kotlin") && subProject.plugins.hasPlugin("java-library")) {
subProject.kotlin.copyClassesToJavaOutput = true
subProject.jar.duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
}
}

Dagger 2 inject error in AppCompatActivity

I'm newbie for Dagger.
Current I create sample project some snip code:
MyComponent.java
#PerActivity
#Component(modules = MyModule.class)
public interface MyComponent {
void inject(TutorialActivity activity);
}
MyModule.java
#Module
public class MyModule {
#Provides
Position providePosition() {
return new Position();
}
}
PerActivity.java
#Scope
#Retention(RUNTIME)
public #interface PerActivity {}
TutorialActivity.java
public class TutorialActivity extends AppCompatActivity{}
When compile project I get error:
Error:Execution failed for task ':app:compileDebugJavaWithJavac'.
> java.lang.IllegalArgumentException: expected one element but was: <android.support.v4.app.FragmentActivity, android.support.v4.app.TaskStackBuilder.SupportParentable>
So if I change TutorialActivity as:
public class TutorialActivity extends Activity{}
or even
public class TutorialActivity{} // Without extends
Then it will working normally.(I can see class generated by Dagger2).
Please help !
Thanks.
UPDATE
My project structure:
common module.
app module. (app module will use common module as depended in gradle).
In both build.gradle (common and app module) I added:
apt "com.google.dagger:dagger-compiler:${daggerVersion}"
compile "com.google.dagger:dagger:${daggerVersion}"
In build.gradle at common module:
provide "org.glassfish:javax.annotation:${javaxAnnotationVersion}"
An error only occurs if I have 2 module. (module app depended on common).
If I move my Component/Module to module common -> It work.
But when I move that to app module -> Error when compile.
I'm not sure that your issue is a problem with Dagger because I don't see you requesting any dependencies in your Android components.
Nonetheless you need this in your build.gradle to use the depdendency injection annotations.
provided 'javax.annotation:jsr250-api:1.0'
Thanks #plash for your answer.
After I re-check for both module.
I found I only added:
provide "org.glassfish:javax.annotation:${javaxAnnotationVersion}"
in common module.
After I added that provide for both module then compile success.(Dagger generated class.)

Gradle - add dependency to tests of another module

I have a multi-module gradle project that looks like this:
Parent
|--server
|--application (android module)
+--common
The server tests have a dependency on the common module tests. For this, I added
testCompile files(project(':common').sourceSets.test.output.classesDi
compileTestJava.dependsOn tasks.getByPath(':common:testClasses')
and it worked great. Unfortunately, when I tried to do the same thing for the application module that also has a dependency on the common module tests, it wouldn't work. It fails with:
Build file 'application\build.gradle' line: 103
A problem occurred evaluating project ':application'.
Could not find property 'sourceSets' on project ':common'
After googling a bit I also tried
project.evaluationDependsOn(':common')
testCompile files(project(':common').sourceSets.test.output.classesDir)
But fails with another exception:
Project application: Only Jar-type local dependencies are supported. Cannot handle: common\build\classes\test
Any ideas on how to fix this?
There's a couple of approaches solving the problem of importing test classes in this article. https://softnoise.wordpress.com/2014/09/07/gradle-sub-project-test-dependencies-in-multi-project-builds/ The one I used is:
code in shared module:
task jarTest (type: Jar) {
from sourceSets.test.output
classifier = 'test'
}
configurations {
testOutput
}
artifacts {
testOutput jarTest
}
code in module depending on the shared module:
dependencies{
testCompile project(path: ':common', configuration: 'testOutput')
}
And there seems to be a plugin for it as well! https://plugins.gradle.org/plugin/com.github.hauner.jarTest/1.0
Following the approach from sakis, this should be the configuration you need to get the tests available from another project in the Android platform (done for debug variant).
Shared module:
task jarTests(type: Jar, dependsOn: "assembleDebugUnitTest") {
classifier = 'tests'
from "$buildDir/intermediates/classes/test/debug"
}
configurations {
unitTestArtifact
}
artifacts {
unitTestArtifact jarTests
}
Your module:
dependencies {
testCompile project(path: ":libName", configuration: "unitTestArtifact")
}
The solution mentioned by droidpl for Android + Kotlin looks like this:
task jarTests(type: Jar, dependsOn: "assembleDebugUnitTest") {
getArchiveClassifier().set('tests')
from "$buildDir/tmp/kotlin-classes/debugUnitTest"
}
configurations {
unitTestArtifact
}
artifacts {
unitTestArtifact jarTests
}
Gradle for project that is going to use dependencies:
testImplementation project(path: ':shared', configuration: 'unitTestArtifact')
I know it's kinda an old question but the solution mentioned in the following blog solves the problem very nicely and is not a sort of hack or a temporary workaround:
Shared test sources in Gradle multi-module project
It works something like this:
// in your module's build.gradle file that needs tests from another module
dependencies {
testCompile project(path: ':path.to.project', configuration: 'test')
}
Also you should note that in the very last paragraph he mentioned that you need to enable Create separate module per source set in IntelliJ settings. But it works fine without using that option too. Probably due to changes in the recent IntelliJ versions.
EDIT: IntelliJ recognizes this fine as of 2020.x versions.
I think you could use gradles java test fixtures. This will automatically create a testFixtures source set, in which you can write your test that you want to reuse.
Test fixtures are configured so that:
they can see the main source set classes
test sources can see the test fixtures classes
For example, if you have some class in common module:
public class CommonDto {
private final Long id;
private final String name;
// getters/setters and other methods ...
}
Then in the common module, you could write into src/testFixtures/java following utils:
public class Utils {
private static final CommonDto A = new CommonDto(1, "A");
private static final CommonDto B = new CommonDto(2, "B");
public static CommonDto a() { return A; }
public static CommonDto b() { return B; }
}
Then in you other modules you could add this to reuse Utils class
dependencies {
// other dependencies ...
testImplementation(testFixtures(project(":common")))
}
All of this is better explained in the documentation that I provided initially. There are some nuances that you need to take into account until you create this not to leak test classes into production.

Categories

Resources