1

I'm developing on Android but i'm new on unit Testing (JUnit and mockito).

I wrote a method which you can call and forget about it (fire&forget). I't doesn't matter to you (at all) if it's correct or not.

The method just call to Google Analytics to send some analytic, so it's not really important on the App.

I call on my code to myGoogleAnalyticSingleton.googleAnalytics_sendViewProduct(myProduct) and i forget about anymore about this.

The method "googleAnalytics_sendViewProduct(MyProduct myProduct)" is something like:

    public void googleAnalytics_sendViewProduct(MyProduct myProduct) {
    final GA_Product gaProduct = new GA_Product(myProduct.getId(),
                                                myProduct.getName(),
                                                myProduct.(so on));

trackerAnalytics.send(gaProduct);
    }

So the question is: How can i test that each method (View a product, add to Cart a product, remove from cart a product, etc...) sends metrics and sends the correct parameters?

public void test_gaViewProduct() {
   assertEquals(gaProduct.getId, myTestIdonMyProductModel);
   assertEquals(gaProduct.getName, myTestNameOnMyProductModel);
   assertEquals .....
  assertTrue(sendWasCalled());
}

I'm new on testing, so i'm not sure if maybe there is something wrong on my thoughts.

Thank you.

Sulfkain
  • 5,082
  • 1
  • 20
  • 38
  • 1
    Mock the `trackerAnalytics` object; verify that the `send` method was called on the mock, passing in an `ArgumentCaptor` instance to capture the argument with which that was called; assert properties of the argument captured. – Andy Turner Jan 13 '16 at 09:09
  • Note that `assert` is a keyword, I think you might mean `assertEquals`; the ordering of `assertEquals` parameters in JUnit is *expected*, *actual*; you have your parameters the wrong way round. This will still check that they're not equal, just the failure messages will be confusing. – Andy Turner Jan 13 '16 at 09:12
  • Aham, could work, could you please put some code to the ArgumentCaptor way? I'm reading right now about it, but any info would be appreciate ;). And if you want an accepted answer, you need not a comment, but an Answer heheh – Sulfkain Jan 13 '16 at 09:14
  • Yeah yeah it's just pseudocode, the google analytics code isn't correct neither. – Sulfkain Jan 13 '16 at 09:14
  • Answers to this question explains how to use `ArgumentCaptor`: http://stackoverflow.com/questions/12295891/how-to-use-argumentcaptor-for-stubbing – Andy Turner Jan 13 '16 at 09:15

2 Answers2

1

Mock the Tracker and inject it to your code under test:

Tracker mockTracker = mock(Tracker.class);
product.setTracker(mockTracker);

Call your test code (View a product, add to Cart,...)

product.addToCart();

Then simply verify if the desired methods have been called, e.g.:

verify(mockTracker).setScreenName("myscreenname");
verify(mockTracker).send(new HitBuilders.AppViewBuilder()
        .setCategory("category", TRACKING_ID)
        .build());
dipdipdip
  • 2,326
  • 1
  • 21
  • 31
  • Thank you, the verify about "send" that i know how to be done, the real problem it's how to test that product sent is correct, because it's internal instance, not testable outside – Sulfkain Jan 13 '16 at 09:23
  • You should make the tracker accessible from the outside (for testing purposes). Package private should be fine. If you can't you can use Mockitos `Whitebox.setInternalState()` method to inject private fields. If you want to know if the correct data was send you can add the desired methods to the `verify()` method. Or I didn't understand your problem correctly. – dipdipdip Jan 13 '16 at 09:35
  • That would be wrong, because it's private for a reason, a test must not change this behaviour. I will take a look about white.box you said maybe works. Thank you. – Sulfkain Jan 13 '16 at 10:51
0

Enter Dependency Injection.

Hide the analytics calls behind an interface. Create one implementation used in the actual production code, and one used only in the test. The test class could perhaps count the number of calls it has received and so on. Then you can verify it from the test by calling something like

assertTrue(analyticsConnector.connectionsMade() == n);

in your test code.

A lecture/talk on the matter of testing and dependency injection. I recommend this video, there are some nuggets in there.

Erik Nyström
  • 537
  • 4
  • 9
  • wrong! duplicating code, a test is not to do this... it's to avoid this – Sulfkain Jan 13 '16 at 09:31
  • @Sulfkain I fail to see how this is wrong, please enlighten me? It may require some extra code, but what you get is testability. Using mocks, you can mock your class instead of having to write one, but to be honest it should really be an interface anyway. What if you wanted to log different products in different locations, or perhaps in multiple locations? – Erik Nyström Jan 13 '16 at 09:44
  • @Sulfkain Tests are not for finding duplicate code (although if you do, that's good to remove). They are to ensure correctness of code. They also carry the positives of forcing you to write extensible code. When writing small programs (<100000 lines of code), it seems silly at times. Once you get over that limit, it starts to reduce the code you write, as everything is so modular that you almost always have 90% of what you need already. You just stick it together in new ways. – Erik Nyström Jan 13 '16 at 09:48
  • Not finding, but avoid. I agree on the second part – Sulfkain Jan 13 '16 at 10:27
  • I can't implement 2 methods, one for production and one for testing. I have to have test the prod. method – Sulfkain Jan 13 '16 at 10:28
  • Ahh, you do not implements one for each, you implement the Tacker interface twice: once for production code, and once for the test code. The test class implementation also has these extra functions (should you need them). Since you create an instance of this class, you have access to its full array of functions. Inside the class under test, only the functions defined in the Tracker interface is visible. If it is the Tracker class you need tested, then test that class separately. This test we are talking about can/should only assert that the Tracker interface is indeed called correctly. – Erik Nyström Jan 13 '16 at 10:35
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/100571/discussion-between-sulfkain-and-erik-nystrom). – Sulfkain Jan 13 '16 at 10:39