I am trying to build unit-test case for a custom dialog that extends the Android DialogFragment using Roboletric but I hit a hard wall. Basically, the unit-test framework cannot make the fragment "visible"; therefore, I cannot test anything.
anyway, the code is pretty basic and based on this thread:
How to create a Custom Dialog box in android?
here's is my app code:
public class CustomDialog extends DialogFragment {
public Dialog mCustomDialogTest;
public EditText mEditText;
public Dialog onCreateDialog(Bundle savedInstanceState) {
mCustomDialogTest = new Dialog(getActivity());
mCustomDialogTest.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
mCustomDialogTest.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
mCustomDialogTest.setContentView(R.layout.custom_dialog);
mCustomDialogTest.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
mEditText = (EditText) mCustomDialogTest.findViewById(R.id.edit_text_id);
return mCustomDialogTest;
}
}
the super basic custom layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<EditText
android:id="#+id/edit_text_id"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_margin="20dp"
android:hint="#string/app_name"
android:inputType="text" />
</LinearLayout>
Finally my unit-test:
#Test
public void testCustomDialog() {
CustomDialog customDialog = new CustomDialog();
customDialog.show(mActivity.getFragmentManager(), getClass().getName());
boolean visible = customDialog.isVisible(); //This never works :(
Assert.assertTrue("Dialog is visible", visible);
}
It always asserts saying it is not visible :( Yet, the code works fine on the device.
Here are the relevant parts in the gradle:
testOptions {
unitTests {
includeAndroidResources = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
testImplementation 'androidx.test.espresso:espresso-core:3.1.0'
testImplementation 'androidx.test:core:1.0.0'
testImplementation 'androidx.test.ext:junit:1.0.0'
testImplementation 'junit:junit:4.12'
testImplementation 'org.mockito:mockito-core:2.23.0'
testImplementation 'org.robolectric:robolectric:4.0.2'
androidTestImplementation 'com.google.truth:truth:0.42'
testImplementation 'com.google.truth:truth:0.42'
androidTestImplementation 'com.android.support.test:rules:1.0.2'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
}
Anyone can help me? I am totally stuck on the most basic functionally for the unit-test.
thank you!
try using the getUserVisibleHint method instead of the isVisible method
Related
I need some help about writing unit tests in android, related to the viewmodel, livedata and flow mechanics and dispatching.
First of all, im writing unit tests, and not instrumeted test.
Actually, im creating an Unit test for an android app, for testing a ViewModel that uses a repository for fetching some data from internet.
The code for the viewmodel im using is like this:
class ViewModel(private var repository: Repository? = Repository()) :
androidx.lifecycle.ViewModel() {
val data: LiveData<Result<Item>> = repository!!.remoteData.asLiveData()
}
The unit test code is as follows:
import junit.framework.TestCase
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestCoroutineDispatcher
import kotlinx.coroutines.test.resetMain
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.setMain
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.`when`
import org.mockito.Mockito.mock
import org.mockito.junit.MockitoJUnitRunner
#ExperimentalCoroutinesApi
#RunWith(MockitoJUnitRunner::class)
class ViewModelTest : TestCase() {
private val testDispatcher = TestCoroutineDispatcher()
private lateinit var repository: Repository
private lateinit var viewModel: ViewModel
#Before
public override fun setUp() {
Dispatchers.setMain(testDispatcher)
repository = mock(Repository::class.java)
viewModel = ViewModel(repository)
}
#After
public override fun tearDown() {
super.tearDown()
Dispatchers.resetMain()
testDispatcher.cleanupTestCoroutines()
}
#Test
fun `remote data is returned`() = runBlockingTest {
try {
`when`(repository.remoteData).thenReturn(
flowOf(Result.success(Item(SampleData.remoteData.apiResult!!)))
)
viewModel.data.observeForever { result ->
assertTrue(result.isSuccess)
}
} catch (exception: Exception) {
fail()
}
}
}
When creating the unit test, and running it, the following error happen:
java.lang.IllegalArgumentException: Parameter specified as non-null is null: method androidx.lifecycle.FlowLiveDataConversions.asLiveData, parameter $this$asLiveData
As for the error, it seems that i need to pass a parameter to the viewmodel.data value, but, which one? as per the code, it not need parameters.
I like to know about mocking the methods that returns a flow object, as the asLiveData() function is the one that, when running the test, throws the exception above.
Also, i think i need to know about the observeForever function for executing and observing values from the livedata, after all, is then observing where i can assert the results of the unit test.
Any help would be great. :)
Im using the following libraries in the app build.gradle file:
testImplementation "junit:junit:4.13"
testImplementation "com.squareup.okhttp3:mockwebserver:4.7.2"
testImplementation "org.mockito:mockito-core:3.3.3"
testImplementation "androidx.arch.core:core-testing:2.1.0"
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2"
androidTestImplementation "androidx.test.ext:junit:1.1.2"
androidTestImplementation "androidx.test.espresso:espresso-core:3.3.0"
You need to mock the repository.remoteData first, and after that you can initialize the ViewModel
`when`(repository.remoteData).thenReturn(
flowOf(Result.success(Item(SampleData.remoteData.apiResult!!)))
)
viewModel = ViewModel(repository)
I've a basic .net core api web app and a unit test project that uses a TestServer to make http requests.
I've a TestStartup class that subclassed the Startup class in the api project.
If the Startup class is in the unit test project i get a 404 response.
If the TestStartup class is moved to the api project i get a 200 reponse.
Api Project
Api.csproj
<PackageReference Include="Microsoft.AspNetCore.App" />
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
}
Startup.cs
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseMvc();
}
}
TestController.cs
public class TestController : ControllerBase
{
[HttpGet("test")]
public ObjectResult Get()
{
return Ok("data");
}
}
Unit Test Project
Tests.csproj
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.1.1" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="2.1.2" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="2.1.1" />
<PackageReference Include="Microsoft.Extensions.PlatformAbstractions" Version="1.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" />
<PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.10.0" />
Tests.cs
[TestFixture]
public class Tests
{
[Test]
public async Task Test()
{
var server = new TestServer(WebHost.CreateDefaultBuilder()
.UseStartup<TestStartup>()
.UseEnvironment("Development"));
var response = await server.CreateClient().GetAsync("test");
}
}
Startup.cs
public class TestStartup : Startup
{ }
In case someone experiences this targeting ASP.NET Core 3.0 or 3.1, don't forget to change the test project SDK to Microsoft.NET.Sdk.Web, i.e.
<Project Sdk="Microsoft.NET.Sdk.Web">
as per Test app prerequisites.
You could try two options below:
Add AddApplicationPart to Startup.cs
namespace IntegrationTestMVC
{
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().AddApplicationPart(Assembly.Load(new AssemblyName("IntegrationTestMVC"))); //"IntegrationTestMVC" is your original project name
}
Try to convert TestFixture to IClassFixture
public class IntegrationTestMVCUnitTest : IClassFixture<WebApplicationFactory<TestStartup>>
{
private readonly HttpClient _client;
private readonly WebApplicationFactory<TestStartup> _factory;
public IntegrationTestMVCUnitTest(WebApplicationFactory<TestStartup> factory)
{
_factory = factory;
_client = factory.CreateClient();
}
[Fact]
public async Task IndexRendersCorrectTitle()
{
var response = await _client.GetAsync(#"/test");
}
}
For the second option, you could refer Integration tests in ASP.NET Core
Adding a reference to Microsoft.AspNetCore.Mvc.Testing to the project file containing the test, as per the instructions in aspnetcore-2.2#test-app-prerequisities fixed the issue for me.
I had to update my nuget packages in test project to work
I am contributing to a project which is built with React (with webpack) running in Electron. When executing unit tests with Jest, it fails with the error TypeError: Cannot read property 'on' of undefined (and works fine when not testing, eg. run with Electron).
The code:
import React, { Component } from 'react';
import { ipcRenderer } from 'electron';
// some more imports
class Setup extends Component {
constructor(props) {
super(props);
this.state = {
// some state
};
ipcRenderer.on('open-file-reply', this.someMethod); // << fails on this line
}
// more class stuff
}
It took me a few days but finally, I found this answer in this great blog post. Quote:
Jest is called from Node and doesn't run test code through Webpack.
Instead, we have to use Jest's mocking functions to replace the import
with a stub file.
Jest has a helper method called moduleNameMapper [object<string, string>] . From jest documentation:
A map from regular expressions to module names that allow to stub out
resources, like images or styles with a single module.
It should be added in your package.json root object like this:
{
"name": "My awesome app",
"jest": {
"moduleNameMapper": {
"electron": "<rootDir>/src/components/tests/mock/electron.js"
}
}
}
and the mock file itself (/src/components/tests/mock/electron.js):
export const ipcRenderer = {
on: jest.fn()
};
This way you can stub other electron modules and methods (like remote which is shown in the blog above).
Another way is creating an electron.js file in __mocks__ in your root folder.
The electron.js should look something like
export const ipcRenderer = {
on: jest.fn(),
};
You can read more at https://jestjs.io/docs/en/manual-mocks#mocking-node-modules
I am writing unit tests for spring application that uses tiles, for one controller the forwardedUrl is different to view name, and for another controller they are the same but as far as I know the way everything is hooked up is the same.
Can anyone tell me why?
I have a controller method:
#RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView root(Locale locale, Model model) {
ModelAndView mv = new ModelAndView("base/index/view");
mv.addObject("display_title", "Home");
return mv;
}
And its unit test:
#Test
public void testApplicationRootUrl() throws Exception {
mockMvc.perform(get("/"))
.andExpect(status().isOk())
.andExpect(view().name("base/index/view"))
.andExpect(forwardedUrl("/WEB-INF/views/base/index/view.jsp"));
}
The forwardedUrl is /WEB-INF/views/base/index/view.jsp so I would have expected the same pattern to apply to another controller.
Here I have another controller method (in a different controller):
#RequestMapping(value = "/products", method = RequestMethod.GET)
public ModelAndView getAllProducts(Locale locale, Model model) {
logger.info("Getting all products");
List<Product> allProducts = productService.getAllProducts();
ModelAndView mv = new ModelAndView("base/product_list/view");
mv.addObject("products", allProducts);
return mv;
}
And the unit test:
#Test
public void testGetAllProducts() throws Exception {
when(productService.getAllProducts()).thenReturn(getAllProducts());
mockMvc.perform(get("/products"))
.andExpect(status().isOk())
.andExpect(view().name("base/product_list/view"))
.andExpect(forwardedUrl("/WEB-INF/views/base/product_list/view.jsp"))
.andExpect(model().attributeExists("products"))
.andExpect(model().attribute("products", hasSize(1)))
.andExpect(model().attribute("products", hasItem(
allOf(
hasProperty("id", is(1)),
hasProperty("productName", is("Yellow")),
hasProperty("material", is("Wood"))
)
)));
verify(productService, times(1)).getAllProducts();
}
This test fails with the following assertion error, this is what I dont understand as tiles is used throughout the application so I would expect the forwardedUrl to remain consistent in terms of pattern:
java.lang.AssertionError: Forwarded URL expected:</WEB-INF/views/base/product_list/view.jsp> but was:<base/product_list/view>
If in the slim chance that someone ever wonders about this and wants to know the answer it is because of a difference in the way the mockMvc object is created for the tests.
For the navigation tests which do not have a mocked service I am using the WebApplicationContext:
#Autowired
private WebApplicationContext wac;
private MockMvc mockMvc;
#Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();
}
However for the other tests which require a mocked service I am using Mockito and the standaloneSetup to build the mockMvc object:
#Mock
private ProductService productService;
#InjectMocks
private ProductController productController;
private MockMvc mockMvc;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
mockMvc = MockMvcBuilders.standaloneSetup(productController).build();
}
It seems that they return different forwardedUrl's even though tiles is used throughout and there is no difference in the actual controllers, only in the tests.
According to the documentation, to unit test controllers, I need to make my controllers a trait, then override the methods
http://www.playframework.com/documentation/2.2.0/ScalaTest
However, if I override my methods, i'm effectively not testing my logic. I may not be grasping something, but I don't see how this unit tests my controller's methods?
The problem with the example in the link you've provided is that it doesn't really show the benefit of having your controller implementation within a trait. In other words, the same example could've been accomplished without using traits by just testing the controller companion object directly.
The benefit of having your controller logic be within a trait is that it allows you to override dependencies that controller may have with mock implementations/values.
For example, you could define a controller as:
trait MyController extends Controller {
lazy val someService : SomeService = SomeServiceImpl
}
object MyController extends MyController
And in your test, you can override the service dependency:
val controller = new MyController {
override lazy val someService = mockService
}
As mentioned in the link the controllers in play are scala objects not classes so can't be instantiated like a class. By making it a trait instead you can make a test class which you can instantiate in your test. No need to override the methods though.
To use the example from the link, here we are making a TestController class that has the same behaviour as the ExampleController object. We don't need to override our index method as we inherit the behaviour from the trait.
Main file
trait ExampleController {
this: Controller =>
def index() = Action {
Ok("ok")
}
}
object ExampleController extends Controller with ExampleController
Test File
object ExampleControllerSpec extends PlaySpecification with Results {
class TestController() extends Controller with ExampleController
"Example Page#index" should {
"should be valid" in {
val controller = new TestController()
val result: Future[SimpleResult] = controller.index().apply(FakeRequest())
val bodyText: String = contentAsString(result)
bodyText must be equalTo "ok"
}
}
}
Here is my simple example of how you can check if some url is available
import org.specs2.mutable._
import org.specs2.runner._
import org.junit.runner._
import play.api.test._
import play.api.test.Helpers._
/**
* Set of tests which are just hitting urls and check
* if response code 200 returned
*/
#RunWith(classOf[JUnitRunner])
class ActionsSanityCheck extends Specification {
def checkIfUrlAccessible(url: String): Unit = {
val appRoute = route(FakeRequest(GET, url)).get
status(appRoute) must equalTo(OK)
contentType(appRoute) must beSome.which(_ == "text/html")
}
"Application" should {
"send 404 on a bad request" in new WithApplication {
route(FakeRequest(GET, "/nowhere")) must beNone
}
"render the index page" in new WithApplication {checkIfUrlAccessible("/")}
"render team page" in new WithApplication {checkIfUrlAccessible("/team")}
}
}