I have a service method with restTemplate. As part of unit test, I am trying to mock it but some how failing.
Service Method:
#Autowired
private RestTemplate getRestTemplate;
return getRestTemplate.getForObject(restDiagnosisGetUrl, SfdcCustomerResponseType.class);
Test Method:
private CaresToSfdcResponseConverter caresToSfdcResponseConverter;
#Before
public void setUp() throws Exception {
caresToSfdcResponseConverter = new CaresToSfdcResponseConverter();
}
#Test
public void testConvert(){
RestTemplate mock = Mockito.mock(RestTemplate.class);
Mockito.when(mock.getForObject(Matchers.anyString(), Matchers.eq(SfdcCustomerResponseType.class))).thenReturn(sfdcCustomerResponseType);
}
sfdcRequest = caresToSfdcResponseConverter.convert(responseForSfdcAndHybris);
It is giving NullPointerException. Looks like it is failing to mock rest template and it is breaking there as rest template is null. Any help would appreciated.Thanks
It's not failing to mock the rest template, but it's not injecting the mocked rest template to your production class. There are at least two ways to fix this.
You can change your production code and use constructor injection. Move the RestTemplate to the constructor as a parameter and then you can just pass the mock in the test:
#Service
public class MyService {
#Autowired
public MyService(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
}
In your test you will simply create the service as any other object and pass it your mocked rest template.
Or you can change your test to inject your service using the following annotation:
#RunWith(MockitoJUnitRunner.class)
public class MyServiceTest {
#InjectMocks
private MyService myService;
#Mock
private RestTemplate restTemplate;
#Test
public void testConvert(){
Mockito.when(mock.getForObject(Matchers.anyString(), Matchers.eq(SfdcCustomerResponseType.class))).thenReturn(sfdcCustomerResponseType);
}
}
You can see an example in another SO question: Using #Mock and #InjectMocks
I generally prefer constructor injection.
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 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")}
}
}
I am trying to unit test HttpStatusCodes in MVC4 but I keep getting a 'System.NullReferenceException' when the controller tries to set the status code on Response, which makes sense as the action is getting called directly. I cant for the life of me work out how to do it without it becoming an integration test. Somebody must have done this, any ideas? See my existing code below.
Controller
public ActionResult Index()
{
Response.StatusCode = (int)HttpStatusCode.ServiceUnavailable;
Response.Headers.Add("Retry-After", "120");
return View();
}
Test
[Test]
public void IndexActionShouldReturn503StatusCode()
{
//Given
var controller = new HomeController();
//When
var result = controller.Index() as HttpStatusCodeResult;
//Then
result.StatusCode.Should().Be((int)HttpStatusCode.ServiceUnavailable);
}
Note
The requirement is for a friendly 'site down' page so I need to return both a view and the status code.
You're returning a ViewResult, then trying to cast it as a HttpStatusCodeResult in your unit test. Try returning a HttpStatusCodeResult instead of a view.
public ActionResult Index()
{
Response.Headers.Add("Retry-After", "120");
return new HttpStatusCodeResult(HttpStatusCode.ServiceUnavailable);
}
I have a situation where I'm debating how to architect my controllers.
Consider the following controller:
public class FileSharingController : Controller
{
private readonly ICommandBus commandBus;
public FileSharingController(ICommandBus commandBus)
{
this.commandBus = commandBus;
}
[HttpPost]
public ActionResult PrepareMetadata(int blocksCount, string fileName, long fileSize)
{
...
}
[HttpPost]
public ActionResult ClearFileMetadata(string fileName){
...
}
[HttpPost] [ValidateInput(false)] //$.ajax({ data: html5FormDataFileChunk , processData: false ... })
public ActionResult UploadBlock(string fileName, int blockId){
var fileUploadCommand = (FileUploadCommand)ExtractFromSessionData(fileName);
var result = commandBus.Submit(fileUploadCommand);
...
}
public ActionResult CommitFileUploads(string[] filesToCommit){
var commitFileUploadCommand = (CommitFileUploadCommand)ExtractFromSessionData(fileName);
var result = commandBus.Submit(commitFileUploadCommand );
...
}
In this controller, I use the command pattern and pass a model to my commandBus which interfaces with my domain. The first three [HttpPost] methods on the controller are for handling jQuery ajax calls from a responsive file uploading UI.
Consider the situation where a user fills out a form (an interview) and uploads some files along with it. Although the user can upload the files before submitting the form, I don't want the uploaded files to be committed until AFTER they submit the form and it passes validation. That is why the last method on the controller is not an http endpoint. As such I have the following controller:
public class InterviewController : Controller
{
[HttpGet]
public ActionResult UserInterview()
{
InterviewViewModel viewModel = new InterviewViewModel ();
return PartialView(viewModel);
}
[HttpPost] [AllowAnonymous]
public ActionResult UserInterview(InterviewViewModel viewModel)
{
if(ModelState.IsValid)
{
var fileSharingController = new FileSharingController();
fileSharingController.CommitFileUploads(viewModel.Files);
}
return PartialView(viewModel);
}
}
The problem is I'm using IoC to inject a commandBus into the FileSharingController so I cannot just instantiate it with default constructor as I am doing.
My options to consider:
Create a custom controller factory to allow instantiating my controller anywhere in the code.
Turn my FileSharingController in a WebAPI controller and treat as a service
Which is the better design path for this situation? If the latter case, how can I keep the CommitFileUploads() method private? I don't want it to be exposed as an endpoint that can be triggered without first validating the rest of the form.
You can instantiate your controller like this:
ICommandBus commandBus = DependencyResolver.Current.GetService<ICommandBus>();
var fileShareController = new FileSharingController(commandBus);
Generic GetService() method is extension method, so make sure that you have "using System.Web.Mvc;" line in the cs file.
But then, it's better to have helper class that is responsible for keeping/storing already uploaded files, and call it from both controllers, instead instantiating controllers manually.
For example:
public class FileUploadManager
{
public FileUploadManager(ICommandBus commandBus, HttpSessionStateBase sessionState)
{
//....
}
}
and then you call it:
ICommandBus commandBus = DependencyResolver.Current.GetService<ICommandBus>();
var fileShareController = new FileUploadManager(commandBus, this.HttpContext.Session);
Or, if you don't want to use DependencyResolver, you pass ICommandBus to both controller's constructors, and use that reference to instantiate helper class.
simply just create the object of another conroller and use all its public methods.