Unit testing with dart's shelf_rest - unit-testing

I'm trying to test a Dart REST app run on shelf_rest. Assuming a setup similar to the shelf_rest example, how can one test the configured routes without actually running an HTTP server?
import 'package:shelf/shelf.dart';
import 'package:shelf/shelf_io.dart' as io;
import 'package:shelf_rest/shelf_rest.dart';
void main() {
var myRouter = router()
..get('/accounts/{accountId}', (Request request) {
var account = new Account.build(accountId: getPathParameter(request, 'accountId'));
return new Response.ok(JSON.encode(account));
});
io.serve(myRouter.handler, 'localhost', 8080);
}
class Account {
final String accountId;
Account.build({this.accountId});
Account.fromJson(Map json) : this.accountId = json['accountId'];
Map toJson() => {'accountId': accountId};
}
class AccountResource {
#Get('{accountId}')
Account find(String accountId) => new Account.build(accountId: accountId);
}
Without getting into too much additional logic, how could the GET account endpoint be unit tested? Some basic tests I'd like to run would be:
GET /accounts/123 returns 200
GET /accounts/bogus returns 404

To create a unit test (i.e. without a running server) then you need to split myRouter outside of the main function and put it in a file in the lib dir. e.g
import 'dart:convert';
import 'package:shelf/shelf.dart';
import 'package:shelf_rest/shelf_rest.dart';
var myRouter = router()
..get('/accounts/{accountId}', (Request request) {
var account =
new Account.build(accountId: getPathParameter(request, 'accountId'));
return new Response.ok(JSON.encode(account));
});
class Account {
final String accountId;
Account.build({this.accountId});
Account.fromJson(Map json) : this.accountId = json['accountId'];
Map toJson() => {'accountId': accountId};
}
Then create a test file in the test directory and test like
import 'package:soQshelf_rest/my_router.dart';
import 'package:test/test.dart';
import 'package:shelf/shelf.dart';
import 'dart:convert';
main() {
test('/account/{accountId} should return expected response', () async {
final Handler handler = myRouter.handler;
final Response response = await handler(
new Request('GET', Uri.parse('http://localhost:9999/accounts/123')));
expect(response.statusCode, equals(200));
expect(JSON.decode(await response.readAsString()),
equals({"accountId": "123"}));
});
}

Related

Trouble Writing to Jest Mocked Prisma Database

I have two databases that I need to interact with in my code. I have a simple function that takes an object and writes it to my PostgreSQL database using Prisma. I've tested the function with Postman, and it works perfectly, but when I try to execute it using a Jest mock (using the singleton pattern found in the Prisma unit testing guide), it returns undefined indicating that it didn't interact with the database and create the new record. Here's my code:
/prisma/clinical-schema.prisma
generator client {
provider = "prisma-client-js"
output = "./generated/clinical"
}
datasource clinicalDatabase {
provider = "postgresql"
url = "postgresql://postgres:postgres#localhost:5432/clinical-data?schema=public"
}
model pcc_webhook_update {
id Int #id #default(autoincrement())
event_type String
organization_id Int
facility_id Int
patient_id Int
resource_id String?
webhook_date DateTime #default(now()) #clinicalDatabase.Timestamptz(6)
status pcc_webhook_update_status #default(pending)
status_changed_date DateTime? #clinicalDatabase.Timestamptz(6)
error_count Int #default(0)
##unique([organization_id, facility_id, patient_id, resource_id, event_type, status])
}
enum pcc_webhook_update_status {
pending
processing
processed
error
}
/prisma/clinical-client.ts
import { PrismaClient } from './generated/clinical';
const prismaClinical = new PrismaClient();
export default prismaClinical;
/testing/prisma-clinical-mock.ts
import { PrismaClient } from '../prisma/generated/clinical';
import { mockDeep, mockReset, DeepMockProxy } from 'jest-mock-extended';
import prisma from '../prisma/clinical-client';
jest.mock('../prisma/clinical-client', () => ({
__esModule: true,
default: mockDeep<PrismaClient>()
}));
beforeEach(() => {
mockReset(prismaClinicalMock);
});
export const prismaClinicalMock = prisma as unknown as DeepMockProxy<PrismaClient>;
Everything up to this point follows the conventions outlined by the Prisma unit testing docs. The only modification I made was to make it database specific. Below is my function and tests. The request object in handle-pcc-webhooks.ts is a sample http request object, the body of which contains the webhook data I care about.
/functions/handle-pcc-webhooks/handler.ts
import prismaClinical from '../../../prisma/clinical-client';
import { pcc_webhook_update } from '../../../prisma/generated/clinical';
import { requestObject } from './handler.types';
export const handlePccWebhook = async (request: requestObject) => {
try {
const webhook = JSON.parse(request.body);
// if the webhook doesn't include a resource id array, set it to an array with an empty string to ensure processing and avoid violating
// the multi-column unique constraint on the table
const { resourceId: resourceIds = [''] } = webhook;
let records = [];
for (const resourceId of resourceIds) {
// update an existing record if one exists in the pending state, otherwise create a new entry
const record: pcc_webhook_update = await prismaClinical.pcc_webhook_update.upsert({
where: {
organization_id_facility_id_patient_id_resource_id_event_type_status: {
organization_id: webhook.orgId,
facility_id: webhook.facId,
patient_id: webhook.patientId,
resource_id: resourceId,
event_type: webhook.eventType,
status: 'pending'
}
},
update: {
webhook_date: new Date()
},
create: {
event_type: webhook.eventType,
organization_id: webhook.orgId,
facility_id: webhook.facId,
patient_id: webhook.patientId,
resource_id: resourceId,
status: 'pending' // not needed
}
});
records.push(record);
}
return records;
} catch (error) {
console.error(error);
}
};
/functions/handle-pcc-webhooks/handler.spec.ts
import fs from 'fs';
import path from 'path';
import MockDate from 'mockdate';
import { prismaClinicalMock } from '../../../testing/prisma-clinical-mock';
import { createAllergyAddRecord } from './__mocks__/allergy';
import { requestObject } from './handler.types';
import { handlePccWebhook } from './handler';
describe('allergy.add', () => {
let requestObject: requestObject;
let allergyAddRecord: any;
beforeAll(() => {
requestObject = getRequestObject('allergy.add');
});
beforeEach(() => {
MockDate.set(new Date('1/1/2022'));
allergyAddRecord = createAllergyAddRecord(new Date());
});
afterEach(() => {
MockDate.reset();
});
test('should create an allergy.add database entry', async() => {
prismaClinicalMock.pcc_webhook_update.create.mockResolvedValue(allergyAddRecord);
// this is where I would expect handlePccWebhook to return the newly created database
// record, but instead it returns undefined. If I run the function outside of this
// unit test, with the same input value, it functions perfectly
await expect(handlePccWebhook(requestObject)).resolves.toEqual([allergyAddRecord]);
});
});
// This just builds a request object with the current webhook being tested
function getRequestObject(webhookType: string) {
// read the contents of request object file as a buffer, then convert it to JSON
const rawRequestObject = fs.readFileSync(path.resolve(__dirname, '../../sample-data/handle-pcc-webhook-request.json'));
const requestObject: requestObject = JSON.parse(rawRequestObject.toString());
// read the contents of the webhook file as a buffer, then convert it to a string
const rawWebhook = fs.readFileSync(path.resolve(__dirname, `../../sample-data/${webhookType}.json`));
const webhookString = rawWebhook.toString();
// set the body of the request object to the contents of the target webhook
requestObject.body = webhookString;
return requestObject;
}
Finally, here is the result of running the unit test:
So after banging my had against the wall for a few hours, I figured out the issue. In my handler.spec.ts file, I had the following line:
prismaClinicalMock.pcc_webhook_update.create.mockResolvedValue(allergyAddRecord);
what that does is mock the value returned for any create functions run using Prisma. The issue is that my function is using an upsert function, which I wasn't explicitly mocking, thus returning undefined. I changed the above line to
prismaClinicalMock.pcc_webhook_update.upsert.mockResolvedValue(allergyAddRecord);
and it started working.

Nestjs unit test: TypeError: this.userModel.findById(...).exec is not a function

using nestjs framework and with a repository class that uses mongoose to do the CRUD operations we have a simple users.repository.ts file like this:
#Injectable()
export class UserRepository {
constructor(#InjectModel(User.name) private userModel: Model<UserDocument>) {}
async create(createUserInput: CreateUserInput) {
const createdUser = new this.userModel(createUserInput);
return await createdUser.save();
}
}
async findById(_id: MongooseSchema.Types.ObjectId) {
return await this.userModel.findById(_id).exec();
}
and it works normally when the server is up.
consider this users.repository.spec file :
import { Test, TestingModule } from '#nestjs/testing';
import { getModelToken } from '#nestjs/mongoose';
import { Model } from 'mongoose';
// User is my class and UserDocument is my typescript type
// ie. export type UserDocument = User & Document; <-- Mongoose Type
import { User, UserDocument } from '../domain/user.model';
import { UserRepository } from './users.repository';
//import graphqlScalars from 'graphql-scalar-types';
describe('UsersRepository', () => {
let mockUserModel: Model<UserDocument>;
let mockRepository: UserRepository;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: getModelToken(User.name),
useValue: Model, // <-- Use the Model Class from Mongoose
},
UserRepository,
//graphqlScalars,
],
}).compile();
// Make sure to use the correct Document Type for the 'module.get' func
mockUserModel = module.get<Model<UserDocument>>(getModelToken(User.name));
mockRepository = module.get<UserRepository>(UserRepository);
});
it('should be defined', () => {
expect(mockRepository).toBeDefined();
});
it('should return a user doc', async () => {
// arrange
const user = new User();
const userId = user._id;
const spy = jest
.spyOn(mockUserModel, 'findById') // <- spy
.mockResolvedValue(user as UserDocument); // <- set resolved value
// act
await mockRepository.findById(userId);
// assert
expect(spy).toBeCalled();
});
});
so my question:
for the should return a user doc test i get TypeError: metatype is not a constructor when and i guess
.mockResolvedValue(user as UserDocument);
should be fixed.
Note:graphql is used the query to the API and i have no idea that if the scalars should be provieded or not, if i uncomment the scalar, the expect(mockRepository).toBeDefined(); test would not pass any more
so any idea to fix the test would be apreciated.
to handle a chained .exec we should define it via mockReturnThis():
static findById = jest.fn().mockReturnThis();
I needed the constructor to be called via new so i preferd to define a mock class in this way:
class UserModelMock {
constructor(private data) {}
new = jest.fn().mockResolvedValue(this.data);
save = jest.fn().mockResolvedValue(this.data);
static find = jest.fn().mockResolvedValue(mockUser());
static create = jest.fn().mockResolvedValue(mockUser());
static remove = jest.fn().mockResolvedValueOnce(true);
static exists = jest.fn().mockResolvedValue(false);
static findOne = jest.fn().mockResolvedValue(mockUser());
static findByIdAndUpdate = jest.fn().mockResolvedValue(mockUser());
static findByIdAndDelete = jest.fn().mockReturnThis();
static exec = jest.fn();
static deleteOne = jest.fn().mockResolvedValue(true);
static findById = jest.fn().mockReturnThis();
}

Mocking a http response in kotlin

Currently I am working on a hobby project with that I want to learn a bit about kotlin.
I implemented an object that makes HTTP get requests and returns the Json object from the response.
What I'm struggeling with is the mocking of the response or the http framework in my tests.
I think if the framework would provide a class, I could manage the mocking. But as it only provides functions like khttp.get(), I'm a bit confused how to mock that.
Can someone help me, please? :)
Thanks!
The HTTPClient Class:
package dao.http.HTTPClient
import khttp.get
import org.json.JSONObject
import java.net.URLDecoder
class HTTPClient {
fun getClient(): HTTPClient {
return this
}
fun httpRequestGET(url: String): JSONObject {
val r = get(url)
return r.jsonObject
}
}
And the related test Class
import dao.http.HTTPClient
import io.mockk.every
import io.mockk.spyk
import org.hamcrest.MatcherAssert.assertThat
import org.json.JSONObject
import org.junit.jupiter.api.Test
import org.hamcrest.CoreMatchers.`is` as Is
import khttp.responses.GenericResponse
class HTTPClientTest {
#Test
fun testHTTPRequestGET() {
val http_get = spyk(khttp.get( "https://somepage.com/wp-json/tsapi/v1/user/ts/isregistered/12323"))
val httpClient = HTTPClient()
var expectedAnswer: JSONObject = JSONObject("""{"uid":"1","user":"user","is_registered":"true"}""")
every { http_get } returns GenericResponse()
var url = "https://somepage.com/wp-json/tsapi/v1/user/ts/isregistered/12323"
var actualAnswer = httpClient.httpRequestGET(url)
assertThat(actualAnswer.get("user"), Is(expectedAnswer.get("user")))
}
}
you can use it like this:
#Test
fun test() {
mockkStatic("khttp.KHttp")
verify { khttp.get(any()) }
verify(exactly = 1) { khttp.get(url = "http://google.com") }
}

How to test file upload in Grails

I have a controller that takes in a file as part of the parameter. I'm wondering how can I test this?
my controller action:
def save () {
def colorInstance = new Color(params.color)
CommonsMultipartFile file = request.getFile('color.filename')
fileUploadService.upload(colorInstance, file, "foldername")
if (requestInstance.save(flush: true)) {
withFormat {
html {redirect(action: "list") }
js {render "test"}
}
}
}
I've started with something like this:...
import org.junit.Before
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
#TestFor(ColorController)
#Mock(Color)
class ColorControllerTests {
#Before
void setUp() {
controller.fileUploadService = new FileUploadService
}
}
Question
I can't figure out how to test the CommonsMultipartFile for file upload.
Also, this test case will sit in unit test folder. How can I execute it?
I am using Grails 2.4, and you can simply use GrailsMockMultipartFile and request.addFile method in your unit test.
This code works on Grails 2.4.4, with Spock testing framework:
Controller side:
class FileUploadController {
def upload() {
def multipartFile = request.getFile('requestParamName')
if (multipartFile.empty) {
flash.message = 'Error: file cannot be empty'
render(view: 'upload')
return
}
// do something now with your file
}
}
Unit test side:
import grails.test.mixin.TestFor
import org.codehaus.groovy.grails.plugins.testing.GrailsMockMultipartFile
import spock.lang.Specification
#TestFor(FileUploadController)
class FileUploadControllerSpec extends Specification {
void "upload should add flash error message if empty file in request"() {
given:
def multipartFile = new GrailsMockMultipartFile('requestParamName', 'someExcelFile.xls', 'application/vnd.ms-excel', new byte[0])
request.addFile(multipartFile)
when:
controller.upload()
then:
assertEquals('Error: file cannot be empty', flash.message)
}
}
Since the request will be multipart during file upload, the actual request servlet would be MultipartHttpServletRequest. For unit test case, mock the same and use it as the request in controller. On successful mocking you should be able to addFile in tests and getFile in action.
import org.junit.Before
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
#TestFor(ColorController)
#Mock(Color)
class ColorControllerTests {
#Before
void setUp() {
controller.fileUploadService = new FileUploadService
//Mock MultipartHttpServletRequest somethign like below
controller.metaClass.request = mockFor(MultipartHttpServletRequest).createMock()
}
void testFileUpload(){
//Add a mock multipart file to request
controller.request.addFile(new MockMultipartFile('myFile', 'IronMan3.jpg', 'image/jpeg', "1234567" as byte[]))
//call the controller action
//assert response
}
}
The accepted answer did not work for me. The following code did:
import org.junit.Before
import grails.test.mixin.Mock
import grails.test.mixin.TestFor
import org.springframework.mock.web.MockMultipartHttpServletRequest
import org.springframework.mock.web.MockMultipartFile
#TestFor(ColorController)
class ColorControllerTests {
def mockRequest
#Before
void setUp() {
mockRequest = new MockMultipartHttpServletRequest()
controller.metaClass.request = mockRequest
// your service here
controller.fileUploadService = new FileUploadService
}
void testFileUpload(){
//Add a mock multipart file to request
mockRequest.addFile(new MockMultipartFile('myFile', 'IronMan3.jpg', 'image/jpeg', "1234567" as byte[]))
//call the controller action
//assert response
}
}

How to test my servlet using JUnit

I have created a web system using Java Servlets and now want to make JUnit testing. My dataManager is just a basic piece of code that submits it to the database. How would you test a Servlet with JUnit?
My code example that allows a user to register/sign up, which is submitted from my main page via AJAX:
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
// Get parameters
String userName = request.getParameter("username");
String password = request.getParameter("password");
String name = request.getParameter("name");
try {
// Load the database driver
Class.forName("com.mysql.jdbc.Driver");
//pass reg details to datamanager
dataManager = new DataManager();
//store result as string
String result = dataManager.register(userName, password, name);
//set response to html + no cache
response.setContentType("text/html");
response.setHeader("Cache-Control", "no-cache");
//send response with register result
response.getWriter().write(result);
} catch(Exception e){
System.out.println("Exception is :" + e);
}
}
You can do this using Mockito to have the mock return the correct params, verify they were indeed called (optionally specify number of times), write the 'result' and verify it's correct.
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.*;
import javax.servlet.http.*;
import org.apache.commons.io.FileUtils;
import org.junit.Test;
public class TestMyServlet extends Mockito{
#Test
public void testServlet() throws Exception {
HttpServletRequest request = mock(HttpServletRequest.class);
HttpServletResponse response = mock(HttpServletResponse.class);
when(request.getParameter("username")).thenReturn("me");
when(request.getParameter("password")).thenReturn("secret");
StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
when(response.getWriter()).thenReturn(writer);
new MyServlet().doPost(request, response);
verify(request, atLeast(1)).getParameter("username"); // only if you want to verify username was called...
writer.flush(); // it may not have been flushed yet...
assertTrue(stringWriter.toString().contains("My expected string"));
}
}
First off, in a real application, you would never get database connection info in a servlet; you would configure it in your app server.
There are ways, however, of testing Servlets without having a container running. One is to use mock objects. Spring provides a set of very useful mocks for things like HttpServletRequest, HttpServletResponse, HttpServletSession, etc:
http://static.springsource.org/spring/docs/3.0.x/api/org/springframework/mock/web/package-summary.html
Using these mocks, you could test things like
What happens if username is not in the request?
What happens if username is in the request?
etc
You could then do stuff like:
import static org.junit.Assert.assertEquals;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.junit.Before;
import org.junit.Test;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.mock.web.MockHttpServletResponse;
public class MyServletTest {
private MyServlet servlet;
private MockHttpServletRequest request;
private MockHttpServletResponse response;
#Before
public void setUp() {
servlet = new MyServlet();
request = new MockHttpServletRequest();
response = new MockHttpServletResponse();
}
#Test
public void correctUsernameInRequest() throws ServletException, IOException {
request.addParameter("username", "scott");
request.addParameter("password", "tiger");
servlet.doPost(request, response);
assertEquals("text/html", response.getContentType());
// ... etc
}
}
I find Selenium tests more useful with integration or functional (end-to-end) testing. I am working with trying to use org.springframework.mock.web, but I am not very far along. I am attaching a sample controller with a jMock test suite.
First, the Controller:
package com.company.admin.web;
import javax.validation.Valid;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;
import com.company.admin.domain.PaymentDetail;
import com.company.admin.service.PaymentSearchService;
import com.company.admin.service.UserRequestAuditTrail;
import com.company.admin.web.form.SearchCriteria;
/**
* Controls the interactions regarding to the refunds.
*
* #author slgelma
*
*/
#Controller
#SessionAttributes({"user", "authorization"})
public class SearchTransactionController {
public static final String SEARCH_TRANSACTION_PAGE = "searchtransaction";
private PaymentSearchService searchService;
//private Validator searchCriteriaValidator;
private UserRequestAuditTrail notifications;
#Autowired
public void setSearchService(PaymentSearchService searchService) {
this.searchService = searchService;
}
#Autowired
public void setNotifications(UserRequestAuditTrail notifications) {
this.notifications = notifications;
}
#RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE)
public String setUpTransactionSearch(Model model) {
SearchCriteria searchCriteria = new SearchCriteria();
model.addAttribute("searchCriteria", searchCriteria);
notifications.transferTo(SEARCH_TRANSACTION_PAGE);
return SEARCH_TRANSACTION_PAGE;
}
#RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="cancel")
public String cancelSearch() {
notifications.redirectTo(HomeController.HOME_PAGE);
return "redirect:/" + HomeController.HOME_PAGE;
}
#RequestMapping(value="/" + SEARCH_TRANSACTION_PAGE, method=RequestMethod.POST, params="execute")
public String executeSearch(
#ModelAttribute("searchCriteria") #Valid SearchCriteria searchCriteria,
BindingResult result, Model model,
SessionStatus status) {
//searchCriteriaValidator.validate(criteria, result);
if (result.hasErrors()) {
notifications.transferTo(SEARCH_TRANSACTION_PAGE);
return SEARCH_TRANSACTION_PAGE;
} else {
PaymentDetail payment =
searchService.getAuthorizationFor(searchCriteria.geteWiseTransactionId());
if (payment == null) {
ObjectError error = new ObjectError(
"eWiseTransactionId", "Transaction not found");
result.addError(error);
model.addAttribute("searchCriteria", searchCriteria);
notifications.transferTo(SEARCH_TRANSACTION_PAGE);
return SEARCH_TRANSACTION_PAGE;
} else {
model.addAttribute("authorization", payment);
notifications.redirectTo(PaymentDetailController.PAYMENT_DETAIL_PAGE);
return "redirect:/" + PaymentDetailController.PAYMENT_DETAIL_PAGE;
}
}
}
}
Next, the test:
package test.unit.com.company.admin.web;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.junit.Assert.assertThat;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.jmock.integration.junit4.JMock;
import org.jmock.integration.junit4.JUnit4Mockery;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.support.SessionStatus;
import com.company.admin.domain.PaymentDetail;
import com.company.admin.service.PaymentSearchService;
import com.company.admin.service.UserRequestAuditTrail;
import com.company.admin.web.HomeController;
import com.company.admin.web.PaymentDetailController;
import com.company.admin.web.SearchTransactionController;
import com.company.admin.web.form.SearchCriteria;
/**
* Tests the behavior of the SearchTransactionController.
* #author slgelma
*
*/
#RunWith(JMock.class)
public class SearchTransactionControllerTest {
private final Mockery context = new JUnit4Mockery();
private final SearchTransactionController controller = new SearchTransactionController();
private final PaymentSearchService searchService = context.mock(PaymentSearchService.class);
private final UserRequestAuditTrail notifications = context.mock(UserRequestAuditTrail.class);
private final Model model = context.mock(Model.class);
/**
* #throws java.lang.Exception
*/
#Before
public void setUp() throws Exception {
controller.setSearchService(searchService);
controller.setNotifications(notifications);
}
#Test
public void setUpTheSearchForm() {
final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;
context.checking(new Expectations() {{
oneOf(model).addAttribute(
with(any(String.class)), with(any(Object.class)));
oneOf(notifications).transferTo(with(any(String.class)));
}});
String nextPage = controller.setUpTransactionSearch(model);
assertThat("Controller is not requesting the correct form",
target, equalTo(nextPage));
}
#Test
public void cancelSearchTest() {
final String target = HomeController.HOME_PAGE;
context.checking(new Expectations(){{
never(model).addAttribute(with(any(String.class)), with(any(Object.class)));
oneOf(notifications).redirectTo(with(any(String.class)));
}});
String nextPage = controller.cancelSearch();
assertThat("Controller is not requesting the correct form",
nextPage, containsString(target));
}
#Test
public void executeSearchWithNullTransaction() {
final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;
final SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.seteWiseTransactionId(null);
final BindingResult result = context.mock(BindingResult.class);
final SessionStatus status = context.mock(SessionStatus.class);
context.checking(new Expectations() {{
allowing(result).hasErrors(); will(returnValue(true));
never(model).addAttribute(with(any(String.class)), with(any(Object.class)));
never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId());
oneOf(notifications).transferTo(with(any(String.class)));
}});
String nextPage = controller.executeSearch(searchCriteria, result, model, status);
assertThat("Controller is not requesting the correct form",
target, equalTo(nextPage));
}
#Test
public void executeSearchWithEmptyTransaction() {
final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;
final SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.seteWiseTransactionId("");
final BindingResult result = context.mock(BindingResult.class);
final SessionStatus status = context.mock(SessionStatus.class);
context.checking(new Expectations() {{
allowing(result).hasErrors(); will(returnValue(true));
never(model).addAttribute(with(any(String.class)), with(any(Object.class)));
never(searchService).getAuthorizationFor(searchCriteria.geteWiseTransactionId());
oneOf(notifications).transferTo(with(any(String.class)));
}});
String nextPage = controller.executeSearch(searchCriteria, result, model, status);
assertThat("Controller is not requesting the correct form",
target, equalTo(nextPage));
}
#Test
public void executeSearchWithTransactionNotFound() {
final String target = SearchTransactionController.SEARCH_TRANSACTION_PAGE;
final String badTransactionId = "badboy";
final PaymentDetail transactionNotFound = null;
final SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.seteWiseTransactionId(badTransactionId);
final BindingResult result = context.mock(BindingResult.class);
final SessionStatus status = context.mock(SessionStatus.class);
context.checking(new Expectations() {{
allowing(result).hasErrors(); will(returnValue(false));
atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class)));
oneOf(searchService).getAuthorizationFor(with(any(String.class)));
will(returnValue(transactionNotFound));
oneOf(result).addError(with(any(ObjectError.class)));
oneOf(notifications).transferTo(with(any(String.class)));
}});
String nextPage = controller.executeSearch(searchCriteria, result, model, status);
assertThat("Controller is not requesting the correct form",
target, equalTo(nextPage));
}
#Test
public void executeSearchWithTransactionFound() {
final String target = PaymentDetailController.PAYMENT_DETAIL_PAGE;
final String goodTransactionId = "100000010";
final PaymentDetail transactionFound = context.mock(PaymentDetail.class);
final SearchCriteria searchCriteria = new SearchCriteria();
searchCriteria.seteWiseTransactionId(goodTransactionId);
final BindingResult result = context.mock(BindingResult.class);
final SessionStatus status = context.mock(SessionStatus.class);
context.checking(new Expectations() {{
allowing(result).hasErrors(); will(returnValue(false));
atLeast(1).of(model).addAttribute(with(any(String.class)), with(any(Object.class)));
oneOf(searchService).getAuthorizationFor(with(any(String.class)));
will(returnValue(transactionFound));
oneOf(notifications).redirectTo(with(any(String.class)));
}});
String nextPage = controller.executeSearch(searchCriteria, result, model, status);
assertThat("Controller is not requesting the correct form",
nextPage, containsString(target));
}
}
I hope this might help.
Updated Feb 2018: OpenBrace Limited has closed down, and its ObMimic product is no longer supported.
Here's another alternative, using OpenBrace's ObMimic library of Servlet API test-doubles (disclosure: I'm its developer).
package com.openbrace.experiments.examplecode.stackoverflow5434419;
import static org.junit.Assert.*;
import com.openbrace.experiments.examplecode.stackoverflow5434419.YourServlet;
import com.openbrace.obmimic.mimic.servlet.ServletConfigMimic;
import com.openbrace.obmimic.mimic.servlet.http.HttpServletRequestMimic;
import com.openbrace.obmimic.mimic.servlet.http.HttpServletResponseMimic;
import com.openbrace.obmimic.substate.servlet.RequestParameters;
import org.junit.Before;
import org.junit.Test;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Example tests for {#link YourServlet#doPost(HttpServletRequest,
* HttpServletResponse)}.
*
* #author Mike Kaufman, OpenBrace Limited
*/
public class YourServletTest {
/** The servlet to be tested by this instance's test. */
private YourServlet servlet;
/** The "mimic" request to be used in this instance's test. */
private HttpServletRequestMimic request;
/** The "mimic" response to be used in this instance's test. */
private HttpServletResponseMimic response;
/**
* Create an initialized servlet and a request and response for this
* instance's test.
*
* #throws ServletException if the servlet's init method throws such an
* exception.
*/
#Before
public void setUp() throws ServletException {
/*
* Note that for the simple servlet and tests involved:
* - We don't need anything particular in the servlet's ServletConfig.
* - The ServletContext isn't relevant, so ObMimic can be left to use
* its default ServletContext for everything.
*/
servlet = new YourServlet();
servlet.init(new ServletConfigMimic());
request = new HttpServletRequestMimic();
response = new HttpServletResponseMimic();
}
/**
* Test the doPost method with example argument values.
*
* #throws ServletException if the servlet throws such an exception.
* #throws IOException if the servlet throws such an exception.
*/
#Test
public void testYourServletDoPostWithExampleArguments()
throws ServletException, IOException {
// Configure the request. In this case, all we need are the three
// request parameters.
RequestParameters parameters
= request.getMimicState().getRequestParameters();
parameters.set("username", "mike");
parameters.set("password", "xyz#zyx");
parameters.set("name", "Mike");
// Run the "doPost".
servlet.doPost(request, response);
// Check the response's Content-Type, Cache-Control header and
// body content.
assertEquals("text/html; charset=ISO-8859-1",
response.getMimicState().getContentType());
assertArrayEquals(new String[] { "no-cache" },
response.getMimicState().getHeaders().getValues("Cache-Control"));
assertEquals("...expected result from dataManager.register...",
response.getMimicState().getBodyContentAsString());
}
}
Notes:
Each "mimic" has a "mimicState" object for its logical state. This provides a clear distinction between the Servlet API methods and the configuration and inspection of the mimic's internal state.
You might be surprised that the check of Content-Type includes "charset=ISO-8859-1". However, for the given "doPost" code this is as per the Servlet API Javadoc, and the HttpServletResponse's own getContentType method, and the actual Content-Type header produced on e.g. Glassfish 3. You might not realise this if using normal mock objects and your own expectations of the API's behaviour. In this case it probably doesn't matter, but in more complex cases this is the sort of unanticipated API behaviour that can make a bit of a mockery of mocks!
I've used response.getMimicState().getContentType() as the simplest way to check Content-Type and illustrate the above point, but you could indeed check for "text/html" on its own if you wanted (using response.getMimicState().getContentTypeMimeType()). Checking the Content-Type header the same way as for the Cache-Control header also works.
For this example the response content is checked as character data (with this using the Writer's encoding). We could also check that the response's Writer was used rather than its OutputStream (using response.getMimicState().isWritingCharacterContent()), but I've taken it that we're only concerned with the resulting output, and don't care what API calls produced it (though that could be checked too...). It's also possible to retrieve the response's body content as bytes, examine the detailed state of the Writer/OutputStream etc.
There are full details of ObMimic and a free download at the OpenBrace website. Or you can contact me if you have any questions (contact details are on the website).
EDIT: Cactus is now a dead project: http://attic.apache.org/projects/jakarta-cactus.html
You may want to look at cactus.
http://jakarta.apache.org/cactus/
Project Description
Cactus is a simple test framework for unit testing server-side java code (Servlets, EJBs, Tag Libs, Filters, ...).
The intent of Cactus is to lower the cost of writing tests for server-side code. It uses JUnit and extends it.
Cactus implements an in-container strategy, meaning that tests are executed inside the container.
Another approach would be to create an embedded server to "host" your servlet, allowing you to write calls against it with libraries meant to make calls to actual servers (the usefulness of this approach somewhat depends on how easily you can make "legitimate" programatic calls to the server - I was testing a JMS (Java Messaging Service) access point, for which clients abound).
There are a couple of different routes you can go - the usual two are tomcat and jetty.
Warning: something to be mindful of when choosing the server to embed is the version of servlet-api you are using (the library which provides classes like HttpServletRequest). If you are using 2.5, I found Jetty 6.x to work well (which is the example I'll give below). If you're using servlet-api 3.0, the tomcat-7 embedded stuff seems to be a good option, however I had to abandon my attempt to use it, as the application I was testing used servlet-api 2.5. Trying to mix the two will result in NoSuchMethod and other such exceptions when attempting to configure or start the server.
You can set up such a server like this (Jetty 6.1.26, servlet-api 2.5):
public void startServer(int port, Servlet yourServletInstance){
Server server = new Server(port);
Context root = new Context(server, "/", Context.SESSIONS);
root.addServlet(new ServletHolder(yourServletInstance), "/servlet/context/path");
//If you need the servlet context for anything, such as spring wiring, you coudl get it like this
//ServletContext servletContext = root.getServletContext();
server.start();
}
Use Selenium for webbased unit tests. There's a Firefox plugin called Selenium IDE which can record actions on the webpage and export to JUnit testcases which uses Selenium RC to run the test server.
First you should probably refactor this a bit so that the DataManager is not created in the doPost code.. you should try Dependency Injection to get an instance. (See the Guice video for a nice intro to DI.). If you're being told to start unit testing everything, then DI is a must-have.
Once your dependencies are injected you can test your class in isolation.
To actually test the servlet, there are other older threads that have discussed this.. try here and here.
public class WishServletTest {
WishServlet wishServlet;
HttpServletRequest mockhttpServletRequest;
HttpServletResponse mockhttpServletResponse;
#Before
public void setUp(){
wishServlet=new WishServlet();
mockhttpServletRequest=createNiceMock(HttpServletRequest.class);
mockhttpServletResponse=createNiceMock(HttpServletResponse.class);
}
#Test
public void testService()throws Exception{
File file= new File("Sample.txt");
File.createTempFile("ashok","txt");
expect(mockhttpServletRequest.getParameter("username")).andReturn("ashok");
expect(mockhttpServletResponse.getWriter()).andReturn(new PrintWriter(file));
replay(mockhttpServletRequest);
replay(mockhttpServletResponse);
wishServlet.doGet(mockhttpServletRequest, mockhttpServletResponse);
FileReader fileReader=new FileReader(file);
int count = 0;
String str = "";
while ( (count=fileReader.read())!=-1){
str=str+(char)count;
}
Assert.assertTrue(str.trim().equals("Helloashok"));
verify(mockhttpServletRequest);
verify(mockhttpServletResponse);
}
}