I am new to Android testing. All I am trying now is new Espresso with Junit4. the thing I go till now is Espresso is for ui testing and with junit we can do logical testing. So I am trying Junit to test my retrofit code:
protected String signIn(String emailNumber, String password) {
RestAdapter restAdapter = new RestAdapter.Builder().setEndpoint(Constants.API_URL).build();
RetroInterface retroInterface = restAdapter.create(RetroInterface.class);
retroInterface.signIn(emailNumber, password, new Callback<SignInPojo>() {
#Override
public void success(SignInPojo signInPojo, Response response) {
if (signInPojo.getError()) {
Snackbar.with(getApplicationContext()).text(signInPojo.getMessage())
.textColor(getResources().getColor(R.color.red_400)).show(SignInActivity.this);
result = "successerror";
} else {
Log.d("Json", signInPojo.getName());
result = "Successfully Signed In";
editor = sharedPreferences.edit();
editor.putBoolean(Constants.IS_LOGGED_IN, true);
editor.apply();
startActivity(new Intent(getApplicationContext(), LenderActivity.class));
finish();
}
}
#Override
public void failure(RetrofitError error) {
Log.d("RetroError", error.toString());
Log.d("RetroUrl", error.getUrl());
result = "failed";
}
});
return result;
}
with these test Class:
SignInActivityJunit.java
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.ActivityInstrumentationTestCase2;
import android.test.suitebuilder.annotation.SmallTest;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
#RunWith(AndroidJUnit4.class)
#SmallTest
public class SignInActivityJunit extends ActivityInstrumentationTestCase2<SignInActivity>{
private SignInActivity signInActivity;
public SignInActivityJunit() {
super(SignInActivity.class);
}
#Before
public void setUp() throws Exception {
super.setUp();
injectInstrumentation(InstrumentationRegistry.getInstrumentation());
signInActivity = getActivity();
}
#Test
public void checkSignIn_Success() {
String result = signInActivity.signIn("99929992", "aaaaaaaa");
assertThat(result, is(equalTo("success")));
}#Test
public void checkSignIn_SuccessButError() {
String result = signInActivity.signIn("99929992", "aaaaaaaa");
assertThat(result, is(equalTo("successerror")));
}#Test
public void checkSignIn_Fail() {
String result = signInActivity.signIn("99929992", "aaaaaaaa");
assertThat(result, is(equalTo("success")));
}
#Override
protected void tearDown() throws Exception {
super.tearDown();
}
}
Now these all cases failed because on debugging I saw that they are not waiting for the network to return call(As per my guess). They are skipping success and failure methods.
So the question is.. how to make unit test wait till retrofit returns the request. Or is there any other efficient way to test these network connectivity.
Thank you
This won't answer your question, but I think a more correct way of testing web services would be to mock the server and request, or to actually test the UI with espresso.
In first case, with jUnit, you don't create an actual request, but rather a mocking one that will return a predifined result (the result could be read from a file). Here you could test that the conversion of response to your model classes is successful, if you have any.
In the second case, with Espresso, test the actual thing on device, like open LoginActivity -> retrieve the login/username fields -> type text -> click on Login button. And as expected result here you could put some controls from the main activity (the one that redirects you after a successful login)
Related
I am testing out new technologies and generated a HelloWorld template using AWS SAM.
I wrote some simple unit tests for my simple app but I am having trouble using #Before and #BeforeClass. If I move the contents of the initialize method inside each of my tests, then they pass but I get all NullPointerExceptions when I try to do it separately.
Originally I decided to use #Before and my class was like so-
package helloworld;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.Test;
public class AppTest {
App app;
APIGatewayProxyResponseEvent result;
String content;
#Before
public void initialize(){
App app = new App();
APIGatewayProxyResponseEvent result = app.handleRequest(null, null);
System.out.println(result);
String content = result.getBody();
System.out.println(content);
}
#Test
public void successfulResponseCode(){
System.out.println("1 " + content);
assertEquals(result.getStatusCode().intValue(), 200);
}
#Test
public void successfulResponseString() {
System.out.println("2 " + content);
assertNotNull(content);
assertTrue(content.contains("\"message\""));
assertTrue(content.contains("\"hello world\""));
assertTrue(content.contains("\"location\""));
}
#Test
public void readsDatabase(){
System.out.println("3 " + content);
assertTrue(content.contains("Devon"));
assertTrue(content.contains("Luana"));
}
#Test
public void headersCreated(){
System.out.println("4 " + content);
assertEquals(result.getHeaders().get("Content-Type"), "application/json");
assertTrue(result.getHeaders().get("Access-Control-Allow-Origin").contains("*"));
assertEquals(result.getHeaders().get("Access-Control-Allow-Methods"), "*");
}
}
The system logs within the initialize class were outputting correctly but inside each #Test they were null. For some reason the instantiated variables were not making it within the scope of the test.
I am a bit of novice at tests and I realized I should be using #BeforeClass anyways, since I didn't need to make a new call for each separate test and I hoped that possibly that might fix my problem. It still didn't work so then I changed the variables to static but that also didn't help. I'm sure the error must be simple but I can't figure out what it is.
I see a few other people were having the same problem here- Junit #Before not working properly and I tried changing my variables to private but still no luck.
I also saw this - Java JUnit testing not working with #Before annotation but I am using junit 4.13.1 although like the author, my tests work if I simply initialize outside the #Before annotation like so -
package helloworld;
import com.amazonaws.services.lambda.runtime.events.APIGatewayProxyResponseEvent;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class AppTest {
App app = new App();
APIGatewayProxyResponseEvent result = app.handleRequest(null, null);
String content = result.getBody();
// #BeforeClass
// public static void initialize(){
// App app = new App();
// APIGatewayProxyResponseEvent result = app.handleRequest(null, null);
// System.out.println(result);
// String content = result.getBody();
// System.out.println(content);
// }
#Test
public void successfulResponseCode(){
System.out.println("1 " + content);
assertEquals(result.getStatusCode().intValue(), 200);
}
#Test
public void successfulResponseString() {
System.out.println("2 " + content);
assertNotNull(content);
assertTrue(content.contains("\"message\""));
assertTrue(content.contains("\"hello world\""));
assertTrue(content.contains("\"location\""));
}
#Test
public void readsDatabase(){
System.out.println("3 " + content);
assertTrue(content.contains("Devon"));
assertTrue(content.contains("Luana"));
}
#Test
public void headersCreated(){
assertEquals(result.getHeaders().get("Content-Type"), "application/json");
assertTrue(result.getHeaders().get("Access-Control-Allow-Origin").contains("*"));
assertEquals(result.getHeaders().get("Access-Control-Allow-Methods"), "*");
}
}
I have tried to integrate paytm using php. I followed this url for implementing paytm https://www.simplifiedcoding.net/paytm-integration-android-example/.
Then downloaded sample code of them to try. Changed all credential like MID, callback url and channel iD, etc. When running on device it open buying screen when tapping on buy button it takes to some time to load and show oops "Oops! Payment failed"
In android console i am able to get order id. Here is the code of constants.java
package simplifiedcoding.net.paytmpaymentsample;
/**
* Created by Belal on 1/10/2018.
*/
public class Constants {
public static final String M_ID = "xxxxx301208461"; //Paytm Merchand Id
we got it in paytm credentials
public static final String CHANNEL_ID = "WAP"; //Paytm Channel Id, got it in
paytm credentials
public static final String INDUSTRY_TYPE_ID = "Retail"; //Paytm industry type
got it in paytm credential
public static final String WEBSITE = "APP_STAGING";
public static final String CALLBACK_URL =
"https://pguat.paytm.com/paytmchecksum/paytmCallback.jsp";
}
here is the code API.java
package simplifiedcoding.net.paytmpaymentsample;
import retrofit2.Call;
import retrofit2.http.Field;
import retrofit2.http.FormUrlEncoded;
import retrofit2.http.POST;
/**
* Created by Belal on 1/10/2018.
*/
public interface Api {
//this is the URL of the paytm folder that we added in the server
//make sure you are using your ip else it will not work
String BASE_URL = "http://10.208.1.229/paytm/";
#FormUrlEncoded
#POST("generateChecksum.php")
Call<Checksum> getChecksum(
#Field("MID") String mId,
#Field("ORDER_ID") String orderId,
#Field("CUST_ID") String custId,
#Field("CHANNEL_ID") String channelId,
#Field("TXN_AMOUNT") String txnAmount,
#Field("WEBSITE") String website,
#Field("CALLBACK_URL") String callbackUrl,
#Field("INDUSTRY_TYPE_ID") String industryTypeId
);
}
MainActivity.java code
package simplifiedcoding.net.paytmpaymentsample;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import com.paytm.pgsdk.PaytmOrder;
import com.paytm.pgsdk.PaytmPGService;
import com.paytm.pgsdk.PaytmPaymentTransactionCallback;
import java.util.HashMap;
import java.util.Map;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
//implementing PaytmPaymentTransactionCallback to track the payment result.
public class MainActivity extends AppCompatActivity implements
PaytmPaymentTransactionCallback {
//the textview in the interface where we have the price
TextView textViewPrice;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//getting the textview
textViewPrice = findViewById(R.id.textViewPrice);
//attaching a click listener to the button buy
findViewById(R.id.buttonBuy).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
//calling the method generateCheckSum() which will generate the paytm checksum for payment
generateCheckSum();
}
});
}
private void generateCheckSum() {
//getting the tax amount first.
String txnAmount = textViewPrice.getText().toString().trim();
//creating a retrofit object.
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Api.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
//creating the retrofit api service
Api apiService = retrofit.create(Api.class);
//creating paytm object
//containing all the values required
final Paytm paytm = new Paytm(
Constants.M_ID,
Constants.CHANNEL_ID,
txnAmount,
Constants.WEBSITE,
Constants.CALLBACK_URL,
Constants.INDUSTRY_TYPE_ID
);
//creating a call object from the apiService
Call<Checksum> call = apiService.getChecksum(
paytm.getmId(),
paytm.getOrderId(),
paytm.getCustId(),
paytm.getChannelId(),
paytm.getTxnAmount(),
paytm.getWebsite(),
paytm.getCallBackUrl(),
paytm.getIndustryTypeId()
);
//making the call to generate checksum
call.enqueue(new Callback<Checksum>() {
#Override
public void onResponse(Call<Checksum> call, Response<Checksum> response) {
//once we get the checksum we will initiailize the payment.
//the method is taking the checksum we got and the paytm object as the parameter
initializePaytmPayment(response.body().getChecksumHash(), paytm);
}
#Override
public void onFailure(Call<Checksum> call, Throwable t) {
}
});
}
private void initializePaytmPayment(String checksumHash, Paytm paytm) {
//getting paytm service
PaytmPGService Service = PaytmPGService.getStagingService();
//use this when using for production
//PaytmPGService Service = PaytmPGService.getProductionService();
//creating a hashmap and adding all the values required
Map<String, String> paramMap = new HashMap<>();
paramMap.put("MID", Constants.M_ID);
paramMap.put("ORDER_ID", paytm.getOrderId());
paramMap.put("CUST_ID", paytm.getCustId());
paramMap.put("CHANNEL_ID", paytm.getChannelId());
paramMap.put("TXN_AMOUNT", paytm.getTxnAmount());
paramMap.put("WEBSITE", paytm.getWebsite());
paramMap.put("CALLBACK_URL", paytm.getCallBackUrl());
paramMap.put("CHECKSUMHASH", checksumHash);
paramMap.put("INDUSTRY_TYPE_ID", paytm.getIndustryTypeId());
//creating a paytm order object using the hashmap
PaytmOrder order = new PaytmOrder(paramMap);
//intializing the paytm service
Service.initialize(order, null);
//finally starting the payment transaction
Service.startPaymentTransaction(this, true, true, this);
}
//all these overriden method is to detect the payment result accordingly
#Override
public void onTransactionResponse(Bundle bundle) {
Toast.makeText(this, bundle.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void networkNotAvailable() {
Toast.makeText(this, "Network error", Toast.LENGTH_LONG).show();
}
#Override
public void clientAuthenticationFailed(String s) {
Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}
#Override
public void someUIErrorOccurred(String s) {
Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}
#Override
public void onErrorLoadingWebPage(int i, String s, String s1) {
Toast.makeText(this, s, Toast.LENGTH_LONG).show();
}
#Override
public void onBackPressedCancelTransaction() {
Toast.makeText(this, "Back Pressed", Toast.LENGTH_LONG).show();
}
#Override
public void onTransactionCancel(String s, Bundle bundle) {
Toast.makeText(this, s + bundle.toString(), Toast.LENGTH_LONG).show();
}
}
For paytm integration there is 4 step process that you should follow. Some misleading information is given in Paytm Official documentation but you can follow this reference tutorial for lastest Paytm SDK integration 2019.
Add Paytm Dependency
Add Runtime Permission & Paytm Activity
Get Merchant ID & Secret Key
Upload Checksum Files on Server
Generate Paytm Checksum
Start Paytm Payment Transaction
Add paytm dependency
// paytm dependency
implementation('com.paytm:pgplussdk:1.2.3') {
transitive = true;
}
Add runtime permission
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_SMS"/>
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
Also add this line in main Activity class
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_SMS) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_SMS, Manifest.permission.RECEIVE_SMS}, 101);
}
To Generate Paytm Checksum you first need to know Order id must be unique on every transaction even in test model transaction.
Order id and customer ID must be generate from Your Server only.
For testing model you can manually give order id and customer id.
The flow of android code is as follow-
Main Activity – get order id and customer id from App UI.
Pass order id and customer id to another activity name as checksum.java
get checksum from URL by passing param.
String url ="https://www.blueappsoftware.com/payment/payment_paytm/generateChecksum.php";
String varifyurl = "https://pguat.paytm.com/paytmchecksum/paytmCallback.jsp";
// "https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID"+orderId;
String param=
"MID="+mid+
"&ORDER_ID=" + orderId+
"&CUST_ID="+custid+
"&CHANNEL_ID=WAP&TXN_AMOUNT=100&WEBSITE=WEBSTAGING"+
"&CALLBACK_URL="+ varifyurl+"&INDUSTRY_TYPE_ID=Retail";
start paytm transaction by calling
Service.startPaymentTransaction(checksum.this, true, true,
checksum.this );
Please read this reference tutorial for complete process
I have 5 test cases in a class name QaTest with TestNG annotations. when I run the extent report it does show on results on 5 tests. I have configured XML file correctly. here I am pasting the code of QaTest and extent report.please help
package satinder.com.satinder;
import java.io.File;
import org.testng.Assert;
import org.testng.ITestResult;
import org.testng.SkipException;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.AfterTest;
import org.testng.annotations.BeforeTest;
import org.testng.annotations.Test;
import com.relevantcodes.extentreports.ExtentReports;
import com.relevantcodes.extentreports.ExtentTest;
import com.relevantcodes.extentreports.LogStatus;
public class GenerateExtentReportTest{
ExtentReports extent;
ExtentTest logger;
#BeforeTest
public void startReport(){
//ExtentReports(String filePath,Boolean replaceExisting)
//filepath - path of the file, in .htm or .html format - path where your report needs to generate.
//replaceExisting - Setting to overwrite (TRUE) the existing file or append to it
//True (default): the file will be replaced with brand new markup, and all existing data will be lost. Use this option to create a brand new report
//False: existing data will remain, new tests will be appended to the existing report. If the the supplied path does not exist, a new file will be created.
extent = new ExtentReports (System.getProperty("user.dir") +"/test-output/STMExtentReport.html", true);
//extent.addSystemInfo("Environment","Environment Name")
extent
.addSystemInfo("Host Name", "SoftwareTestingMaterial")
.addSystemInfo("Environment", "Automation Testing")
.addSystemInfo("User Name", "Rajkumar SM");
//loading the external xml file (i.e., extent-config.xml) which was placed under the base directory
//You could find the xml file below. Create xml file in your project and copy past the code mentioned below
extent.loadConfig(new File(System.getProperty("user.dir")+"\\extent-config.xml"));
}
#Test
public void passTest(){
//extent.startTest("TestCaseName", "Description")
//TestCaseName – Name of the test
//Description – Description of the test
//Starting test
logger = extent.startTest("passTest");
Assert.assertTrue(true);
//To generate the log when the test case is passed
logger.log(LogStatus.PASS, "Test Case Passed is passTest");
}
#Test
public void failTest(){
logger = extent.startTest("failTest");
Assert.assertTrue(false);
logger.log(LogStatus.PASS, "Test Case (failTest) Status is passed");
}
#Test
public void skipTest(){
logger = extent.startTest("skipTest");
throw new SkipException("Skipping - This is not ready for testing ");
}
#AfterMethod
public void getResult(ITestResult result){
if(result.getStatus() == ITestResult.FAILURE){
logger.log(LogStatus.FAIL, "Test Case Failed is "+result.getName());
logger.log(LogStatus.FAIL, "Test Case Failed is "+result.getThrowable());
}else if(result.getStatus() == ITestResult.SKIP){
logger.log(LogStatus.SKIP, "Test Case Skipped is "+result.getName());
}
// ending test
//endTest(logger) : It ends the current test and prepares to create HTML report
extent.endTest(logger);
}
#AfterTest
public void endReport(){
// writing everything to document
//flush() - to write or update test information to your report.
extent.flush();
//Call close() at the very end of your session to clear all resources.
//If any of your test ended abruptly causing any side-affects (not all logs sent to ExtentReports, information missing), this method will ensure that the test is still appended to the report with a warning message.
//You should call close() only once, at the very end (in #AfterSuite for example) as it closes the underlying stream.
//Once this method is called, calling any Extent method will throw an error.
//close() - To close all the operation
extent.close();
}
}
and here is the code for QaTest which i want to show in extent reports
package satinder.com.satinder;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
public class QaTest extends Bhandle {
#BeforeMethod
public void browser1()
{
super.browser();
}
#Test
// Scenario is to login with invalid login
public void loginwithinval()
{
Login a=new Login(driver);
a.login().click();
a.username().sendKeys("qwer");
a.password().sendKeys("123435");
a.log().click();
boolean sp=a.failed().isDisplayed();
if (sp==sp)
{
System.out.println("The test case with invalid data is pass");
}
else
{ System.out.println("The test case with invalid data is failed");
}
}
#Test
//Scenario is to esignup
public void esignup() throws InterruptedException
{
signup b= new signup(driver);
b.sign().click();
b.esignup().click();
String u=driver.getTitle();
b.efirstn().sendKeys("jhskwnhj");
b.elastn().sendKeys("mjkdekt");
b.eemail().sendKeys("nhldjkdh232#yahoo.com");
b.eusern().sendKeys("sdljwdkv23");
b.epass().sendKeys("losangeles1");
b.erepass().sendKeys("losangeles1");
b.esubmit().click();
Thread.sleep(5000);
String t=driver.getTitle();
if (u.equals(t))
{
System.out.println("the test case with signup data is falied ");
}
else
{
System.out.println( "the test case with signup data is sucessfull and pass");
}
}
#Test
//Scenario is to login with valid credential
public void login1() throws InterruptedException
{
Login a=new Login(driver);
a.login().click();
String u=driver.getTitle();
a.username().sendKeys("sdljwdkv23");
a.password().sendKeys("losangeles1");
a.log().click();
Thread.sleep(4000);
String t=driver.getTitle();
if (u.equals(t)) {
System.out.println("The log in unsuccessfull and test failed");
}
else
{
System.out.println("The user logged in successfully and test pass");
}
}
#Test
//Scenario is to login with valid credential
public void forgetpassword() throws InterruptedException
{
Login a=new Login(driver);
a.login().click();
a.username().sendKeys("sdljwdkv23");
a.password().sendKeys("losangele1");
a.log().click();
a.forget().click();
String s=driver.getTitle();
a.resetemailbox().sendKeys("nhldjkdh232#yahoo.com");
a.sendpasswordbutton().click();
Thread.sleep(4000);
String w=driver.getTitle();
if (w.equals(s))
{
System.out.println(" This email is not registered and test is failed");
}
else
{ System.out.println("The password is reset and test pass");}
}
#Test
public void ifnotregistered() throws InterruptedException{
Login a=new Login(driver);
a.login().click();
a.notregistered().click();
signup b= new signup(driver);
//b.sign().click();
b.esignup().click();
String u=driver.getTitle();
b.efirstn().sendKeys("jhaskwnhj");
b.elastn().sendKeys("mjkadekt");
b.eemail().sendKeys("anhldjkdh232#yahoo.com");
b.eusern().sendKeys("sadlajwdkv23");
b.epass().sendKeys("losangeles1");
b.erepass().sendKeys("losangeles1");
b.esubmit().click();
Thread.sleep(5000);
String t=driver.getTitle();
if (u.equals(t))
{
System.out.println("the test case with signup data is falied ");
}
else
{
System.out.println( "the test case with signup data is sucessfull and pass");
}
}
#AfterMethod
public void close()
{
driver.close();
}
}
I think you have to set it to false here:
extent = new ExtentReports (System.getProperty("user.dir") +"/test-output/STMExtentReport.html", false);
Ive been doing this tutorial using Android Studio IDE.
The problem I have is that the tutorial was done with older libraries of gson and retrofit 1.8.0...
I was following along well with retrofit2.0-beta3 until I came upon this error that I cant seem to resolve..
It has something to do with this line...(this line is in my MainActivity.Java under onCreate())
SCService scService = SoundCloud.getService();
scService.getRecentTracks(new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()), new Callback<List<Track>>() {
#Override
public void onResponse(Response<List<Track>> tracks) {
Log.d("TAG", "ONRESPONSE() - -- - some else wrong");
// response.isSuccess() is true if the response code is 2xx
if (tracks.isSuccess()) {
Log.d("TAG", "ONRESPONSE()_isSuccess? - -- - some else wrong");
List<Track> track = tracks.body();
loadTracks(track);
} else {
Log.d("TAG", "some else wrong");
}
}
#Override
public void onFailure(Throwable t) {
// handle execution failures like no internet connectivity
Log.d("Error", t.getMessage());
}
});
so I think that the problem starts with scService Interface..
import java.util.List;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.http.GET;
import retrofit2.http.Query;
interface SCService {
#GET("tracks?client_id=" + Config.CLIENT_ID)
public void getRecentTracks(#Query("created_at[from]") String date, Callback<List<Track>> cb);
}
Here is my Soundcloud class....
import retrofit2.Retrofit;
import retrofit2.Retrofit;
public class SoundCloud {
private static final Retrofit REST_ADAPTER = new Retrofit.Builder().baseUrl(Config.API_URL).build();
private static final SCService SERVICE = REST_ADAPTER.create(SCService.class);
public static SCService getService() {
return SERVICE;
}
}
This is the Config class didnt think it would be needed...
public class Config {
public static final String CLIENT_ID = "c85f6828ae5eaf5981937ead09ef1b45";
public static final String API_URL = "https://api.soundcloud.com/";
}
I have been at this the whole day, Any help would be much appreciated..
It could be few things, but most likely the problem is that Gson converter is no longer automatically registered with retrofit and thus your code doesn't know how to get you object from Json. I.e. in retrofit2 you need to register Gson like:
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://api.nuuneoi.com/base/")
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Take a look at this article: Retrofit 2 changes, Custom Gson Object (in the middle of the page)
I'm trying to unit test presenter in my Android app. Method I'm trying to test looks like this:
#Override
public boolean loadNextPage() {
if (!mIsLoading) {
mIsLoading = true;
if (mViewReference.get() != null) {
mViewReference.get().showProgress();
}
mService.search(mSearchQuery, ++mCurrentPage)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(itemsPage -> {
mIsLoading = false;
mTotalPages = itemsPage.getPagination().getTotalPages();
if (mViewReference.get() != null) {
mViewReference.get().showMovies(itemsPage.getItems());
}
},
error -> {
mIsLoading = false;
Log.d(LOG_TAG, error.toString());
});
}
return mTotalPages == 0 || mCurrentPage < mTotalPages;
}
mService is Retrofit interface and mService.search() method returns RxJava's Observable<SearchResults>. My unit test code looks like this:
package mobi.zona.presenters;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import java.util.ArrayList;
import java.util.List;
import com.example.api.Service;
import com.example.model.Movie;
import com.example.model.SearchResults;
import com.example.views.MoviesListView;
import rx.Observable;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class SearchPresenterTest {
#Mock
Service mService;
#Mock
MoviesListView mMoviesListView;
#Test
public void testLoadNextPage() throws Exception {
String searchQuery = "the hunger games";
SearchResults searchResults = new SearchResults();
List<Movie> movies = new ArrayList<>();
searchResults.setItems(movies);
when(mService.search(searchQuery, 1)).thenReturn(Observable.just(new SearchResults()));
MoviesListPresenter presenter = new SearchPresenter(mZonaService, mMoviesListView, searchQuery);
presenter.loadNextPage();
verify(mService, times(1)).search(searchQuery, 1);
verify(mMoviesListView, times(1)).showProgress();
verify(mMoviesListView, times(1)).showMovies(movies);
}
}
The problem is the third verify(mMoviesListView, times(1)).showMovies(movies); line - it allways fails. Whem I'm trying to debug this test I see that control flow never goes into .subscribe(itemPage - {.... I think that it's something related to the fact that I'm subscribing on Schedulers.io() thread, but have no idea on how to fix this. Any ideas?
EDIT 1:
Changed the presenter to take Scheduler's as constructor parameters. Changed test to look like this:
#Test
public void testLoadNextPage() throws Exception {
String searchQuery = "the hunger games";
SearchResults searchResults = new SearchResults();
List<Movie> movies = new ArrayList<>();
searchResults.setItems(movies);
when(mZonaService.search(searchQuery, 1)).thenReturn(Observable.just(new SearchResults()));
MoviesListPresenter presenter = new SearchPresenter(mZonaService, mMoviesListView, searchQuery,
Schedulers.test(), Schedulers.test());
presenter.loadNextPage();
verify(mZonaService, times(1)).search(searchQuery, 1);
verify(mMoviesListView, times(1)).showProgress();
verify(mMoviesListView, times(1)).showMovies(movies);
}
Still getting this test failure message:
Wanted but not invoked:
mMoviesListView.showMovies([]);
-> at com.example.presenters.SearchPresenterTest.testLoadNextPage(SearchPresenterTest.java:46)
However, there were other interactions with this mock:
mMoviesListView.showProgress();
-> at com.example.presenters.SearchPresenter.loadNextPage(SearchPresenter.java:41)
In my apps interactors/use-cases/model (mService in your case) is responsible for specifying Scheduler for the operation (since it knows better what kind of operation it does).
So, move your subscribeOn to mService. After that your mock will work fine.
Going deeper, if now you'll want to test mService I would recommend you to make it "dependent" on Scheduler. In other words - add Sheduler as a constructor parameter.
public class MyService {
private final Scheduler taskScheduler;
public MyService(Scheduler taskScheduler) {
this.taskScheduler = taskScheduler;
}
// ...
public Observable<Something> query() {
return someObservable.subscribeOn(taskScheduler);
}
}
Then, in tests you can use Schedulers.immediate() and for actual app Schedulers.io() (or whatever you like, really).