diff --git a/app/build.gradle b/app/build.gradle index be31b511..d1ac7ab7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -107,4 +107,5 @@ dependencies { testImplementation libs.androidx.core.testing testImplementation libs.mockito.core testImplementation libs.mockito.inline + testImplementation libs.robolectric } \ No newline at end of file diff --git a/app/src/test/java/com/d4rk/androidtutorials/java/ads/managers/NativeAdLoaderTest.java b/app/src/test/java/com/d4rk/androidtutorials/java/ads/managers/NativeAdLoaderTest.java new file mode 100644 index 00000000..7adce22e --- /dev/null +++ b/app/src/test/java/com/d4rk/androidtutorials/java/ads/managers/NativeAdLoaderTest.java @@ -0,0 +1,119 @@ +package com.d4rk.androidtutorials.java.ads.managers; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.mockConstruction; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.view.View; +import android.widget.Button; +import android.widget.FrameLayout; +import android.widget.TextView; + +import androidx.test.core.app.ApplicationProvider; + +import com.d4rk.androidtutorials.java.R; +import com.google.android.gms.ads.AdListener; +import com.google.android.gms.ads.AdLoader; +import com.google.android.gms.ads.AdRequest; +import com.google.android.gms.ads.LoadAdError; +import com.google.android.gms.ads.nativead.NativeAd; +import com.google.android.gms.ads.nativead.NativeAdView; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.MockedConstruction; +import org.robolectric.RobolectricTestRunner; + +import java.util.concurrent.atomic.AtomicReference; + +@RunWith(RobolectricTestRunner.class) +public class NativeAdLoaderTest { + + @Test + public void loadSuccessAddsPopulatedViewAndRequestsLayout() { + Context context = ApplicationProvider.getApplicationContext(); + TestContainer container = new TestContainer(context); + AdRequest adRequest = mock(AdRequest.class); + + NativeAd nativeAd = mock(NativeAd.class); + when(nativeAd.getHeadline()).thenReturn("Headline"); + when(nativeAd.getBody()).thenReturn("Body"); + when(nativeAd.getCallToAction()).thenReturn("Install"); + when(nativeAd.getAdvertiser()).thenReturn("Advertiser"); + + AtomicReference listenerRef = new AtomicReference<>(); + + try (MockedConstruction mocked = mockConstruction(AdLoader.Builder.class, (builder, ctx) -> { + when(builder.forNativeAd(any())).thenAnswer(inv -> { + listenerRef.set(inv.getArgument(0)); + return builder; + }); + when(builder.withAdListener(any())).thenReturn(builder); + AdLoader adLoader = mock(AdLoader.class); + when(builder.build()).thenReturn(adLoader); + doAnswer(inv -> { + listenerRef.get().onNativeAdLoaded(nativeAd); + return null; + }).when(adLoader).loadAd(any(AdRequest.class)); + })) { + NativeAdLoader.load(context, container, R.layout.ad_home_banner_large, adRequest, null); + } + + assertEquals(1, container.getChildCount()); + assertTrue(container.requestedLayout); + NativeAdView adView = (NativeAdView) container.getChildAt(0); + TextView headline = adView.findViewById(R.id.ad_headline); + assertEquals("Headline", headline.getText().toString()); + Button cta = adView.findViewById(R.id.ad_call_to_action); + assertEquals("Install", cta.getText().toString()); + } + + @Test + public void loadFailureClearsContainerAndHides() { + Context context = ApplicationProvider.getApplicationContext(); + TestContainer container = new TestContainer(context); + container.addView(new View(context)); + AdRequest adRequest = mock(AdRequest.class); + + AtomicReference listenerRef = new AtomicReference<>(); + + try (MockedConstruction mocked = mockConstruction(AdLoader.Builder.class, (builder, ctx) -> { + when(builder.forNativeAd(any())).thenReturn(builder); + when(builder.withAdListener(any())).thenAnswer(inv -> { + listenerRef.set(inv.getArgument(0)); + return builder; + }); + AdLoader adLoader = mock(AdLoader.class); + when(builder.build()).thenReturn(adLoader); + doAnswer(inv -> { + listenerRef.get().onAdFailedToLoad(mock(LoadAdError.class)); + return null; + }).when(adLoader).loadAd(any(AdRequest.class)); + })) { + NativeAdLoader.load(context, container, R.layout.ad_home_banner_large, adRequest, null); + } + + assertEquals(0, container.getChildCount()); + assertEquals(View.GONE, container.getVisibility()); + } + + private static class TestContainer extends FrameLayout { + boolean requestedLayout = false; + + TestContainer(Context context) { + super(context); + } + + @Override + public void requestLayout() { + super.requestLayout(); + requestedLayout = true; + } + } +} + diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index f62cc9df..68965fc7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -17,6 +17,7 @@ lifecycle = "2.9.3" lottie = "6.6.7" mockitoCore = "5.19.0" mockitoInline = "5.2.0" +robolectric = "4.13.2" navigationUi = "2.9.4" preference = "1.2.1" review = "2.0.2" @@ -61,6 +62,7 @@ material = { module = "com.google.android.material:material", version.ref = "mat materialratingbar-library = { module = "me.zhanghai.android.materialratingbar:library", version.ref = "libraryVersion" } mockito-core = { module = "org.mockito:mockito-core", version.ref = "mockitoCore" } mockito-inline = { module = "org.mockito:mockito-inline", version.ref = "mockitoInline" } +robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" } play-services-ads = { module = "com.google.android.gms:play-services-ads", version.ref = "playServicesAds" } review = { module = "com.google.android.play:review", version.ref = "review" } volley = { module = "com.android.volley:volley", version.ref = "volley" }