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")}
}
}
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)
EDIT: This was just a bug with loopback, I've put in a PR.
The following code does not work:
// BAD CODE
import {ApplicationConfig} from '#loopback/core';
import {RestApplication, RestServer, get} from '#loopback/rest';
export class HelloController {
#get('/hello')
hello(): string {
return 'Hello world!';
}
}
export class HelloWorldApplication extends RestApplication {
constructor(options: ApplicationConfig = {}) {
super(options);
this.controller(HelloController); // Error Here
}
async start() {
await super.start();
const rest = await this.getServer(RestServer);
console.log(
`REST server running on localhost:${await rest.get('rest.port')}`,
);
}
}
I get this error:
UnhandledPromiseRejectionWarning: TypeError: paramTypes is not iterable
at resolveControllerSpec (/Users/seph/Code/Project/api-loopback/node_modules/#loopback/openapi-v3/src/controller-spec.ts:312:21)
How can I directly add a controller in LoopBack 4?
No sure if this helps but you can inject another controller through the constructor of current controller. You should also generate your controller like using
lb4 controller then assuming you have 2 controllers created and want to access 1st of them inside of the second one you could do:
constructor(
// Inject other controller
#inject('controllers.SomeOtherController')
// Create localy
public otherCtrl: SomeOtherController,
and then
otherCtrl.SomeMethod()
Read more Loopback Controllers
Regards,
How might a Flow.js interface be mocked with Jest? To my surprise, I haven't found this issue addressed anywhere.
I'm fairly new to both, but the only (untested) option I see is to create a class that inherits from the interface and then mock the implementing class. This seems quite cumbersome and I don't believe I could place the implementing classes (which are what would actually be mocked) inside the __mocks__ folders expected by Jest and still get the expected behavior.
Any suggestions? Is there a more appropriate mocking tool?
Update
Why do I want to create a mock for an interface? This code intends to have a clean separation of the domain and implementation layers, with the domain classes using Flow interfaces for all injected dependencies. I want to test these domain classes. Using a mocking tool could ideally allow me to more easily and expressively modify the behavior of the mocked services and confirm that the domain class being tested is making the appropriate calls to these mocked services.
Here's a simplified example of a class that I would be testing in this scenario. UpdateResources would be the class under test, while ResourceServer and ResourceRepository are interfaces for services that I would like to mock and 'spy' upon:
// #flow
import type { ResourceServer } from '../ResourceServer';
import type { ResourceRepository } from '../ResourceRepository';
/**
* Use case for updating resources
*/
export default class UpdateResources {
resourceServer: ResourceServer;
resourceRepository: ResourceRepository;
constructor(resourceServer: ResourceServer, resourceRepository: ResourceRepository) {
this.resourceServer = resourceServer;
this.resourceRepository = resourceRepository;
}
async execute(): Promise<boolean> {
const updatesAvailable = await this.resourceServer.checkForUpdates();
if (updatesAvailable) {
const resources = await this.resourceServer.getResources();
await this.resourceRepository.saveAll(resources);
}
return updatesAvailable;
}
}
A solution
The approach I've arrived at which seems to work quite well for my purposes is to create a mock implementation of the interface in the __mocks__ directory what exposes jest.fn objects for all implemented methods. I then instantiate these mock implementations with new and skip any use of jest.mock().
__mocks__/MockResourceServer.js
import type { ResourceServer } from '../ResourceServer';
export default class MockResourceServer implements ResourceServer {
getResources = jest.fn(() => Promise.resolve({}));
checkForUpodates = jest.fn(() => Promise.resolve(true));
}
__mocks__/MockResourceRepository.js
import type { ResourceRepository } from '../ResourceRepository';
export default class MockResourceRepository implements ResourceRepository {
saveAll = jest.fn(() => Promise.resolve());
}
__tests__/UpdateResources.test.js
import UpdateResources from '../UpdateResources';
import MockResourceRepository from '../../__mocks__/MockResourceRepository';
import MockResourceServer from '../../__mocks__/MockResourceServer';
describe('UpdateResources', () => {
describe('execute()', () => {
const mockResourceServer = new MockResourceServer();
const mockResourceRepository = new MockResourceRepository();
beforeEach(() => {
jest.clearAllMocks();
});
it('should check the ResourceServer for updates', async () => {
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceServer.checkForUpdates).toHaveBeenCalledTimes(1);
});
it('should save to ResourceRepository if updates are available', async () => {
mockResourceServer.load.mockResolvedValue(true);
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceRepository.saveAll).toHaveBeenCalledTimes(1);
});
it('should NOT save to ResourceRepository if NO updates are available', async () => {
mockResourceServer.load.mockResolvedValue(false);
const updateResources = new UpdateResources(mockResourceServer, mockResourceRepository);
await updateResources.execute();
expect(mockResourceRepository.saveAll).not.toHaveBeenCalled();
});
});
});
If anyone can offer any improvements, I'm open!
The thing is, you don't actually need to mock an implementation of an interface. The purpose of a mock is to 'look like' the real thing, but if you already have an interface that says what the real thing should look like, any implementation that conforms to the interface will automatically serve equally well as a mock. In fact, from the point of view of the typechecker, there won't be a different between the 'real' and the 'mock' implementation.
Personally what I like to do is to create a mock implementation that can be constructed by feeding it mock responses. Then it can be reused in any test case by constructing it directly in that test case with the exact responses it should provide. I.e., you 'script' the mock with what it should say by injecting the responses at the time of construction. The difference between it and your mocking implementation is that if it doesn't have a response, it throws a exception and fails the test. Here's an article I wrote that shows this method: https://dev.to/yawaramin/interfaces-for-scaling-and-testing-javascript-1daj
With this technique, a test case might look like this:
it('should save to ResourceRepository if updates are available', async () => {
const updateResources = new UpdateResources(
new MockResourceServer({
checkForUpdates: [true],
getResources: [{}],
}),
new MockResourceRepository({
saveAll: [undefined],
}),
);
const result = await updateResources.execute();
expect(result).toBeTruthy();
});
What I like about these mocks is that all the responses are explicit, and show you the sequence of calls that are happening.
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.
I am writing unit tests to test MVC 3 controllers. I want to ensure that that the view that comes back from the controller is the right view. In my unit test I have:
[Test]
public void It_Should_Return_The_Right_Page()
{
FormController fc = this.CreateFormController();
var view = fc.FindX();
Assert.AreEqual("FindX", view.ViewName);
}
In my controller, I have:
public ViewResult FindX()
{
return View();
}
This fails because ViewName is null. If I change the call to say return View("FindX") and explicitly define the view to be returned, it works. However, I would like to avoid this if possible. Is there a generally accepted way to approach this?
It sounds like what you want to convey is: Assert that the default view for this method was returned. One way to convey this is using this line:
var view = fc.FindX();
Assert.IsNull(view.ViewName)
But this doesn't convey your intent very well. One way to convey it more clearly is to create an extension method on ActionResult or ViewResult called AssertIsDefaultView like so:
public static class ActionResultAssertions
{
public static void AssertIsDefaultView(this ActionResult actionResult)
{
var viewResult = actionResult as ViewResult;
Assert.IsNotNull(viewResult);
Assert.IsNull(viewResult.ViewName);
}
}
Then in your test you can say:
var view = fc.FindX();
view.AssertIsDefaultView();
MvcContrib has a set of these assertions (I think the name of the method is AssertViewRendered), but I prefer to just write the extensions myself so I can understand MVC better.
If you don't set a viewname, then isn't ViewName being null the correct and expected outcome, so code your test accordingly.
Assert.IsNull(view.ViewName);
that worked for me
public ViewResult FindX()
{
return View("FindX");
}