I am trying to create a unit test for the following method:
public CommandDTO update(final MenuRequest request) {
Menu menu = menuRepository.findByUuid(request.getUuid());
MenuConverter.convert(request, menu); // map fields
Menu saved = menuRepository.save(menu);
return CommandDTO.builder().uuid(saved.getUuid()).build();
}
I want to capture saved parameter as it is not returned from the method using the following unit test:
#RunWith(MockitoJUnitRunner.class)
public class MenuServiceImplTest {
#InjectMocks
private MenuServiceImpl menuService;
#Captor
private ArgumentCaptor<Menu> menuCaptor;
// mocks (code omitted for brevity)
#Test
public void test_Update() {
// ...
CommandDTO result = menuService.update(request);
verify(menuRepository).save(menuCaptor.capture());
final Menu captured = menuCaptor.getValue();
}
}
However, the parameter in menuRepository.save() is the unsaved parameter, but I need to capture saved parameter. So, how can I do this? Should I use #Spy instead of ArgumentCaptor?
If that is a unit test, then I assume you already mock MenuRepository.
To properly mock MenuRepository, you need to define the behavior of menuRepository.findByUuid(request.getUuid()) and menuRepository.save(menu).
Once you define menuRepository.findByUuid(request.getUuid()), you need to create a Menu instance. All you need to do is, to check if the fields of that menu are updated properly.
Let's try to break down the functionality of the MenuServiceImpl's update method:
public CommandDTO update(final MenuRequest request) {
Menu menu = menuRepository.findByUuid(request.getUuid());
MenuConverter.convert(request, menu); // map fields
Menu saved = menuRepository.save(menu);
return CommandDTO.builder()
.uuid(saved.getUuid())
.build();
}
Here is what this method is doing:
Fetching existing Menu from the repository, finding by uuid
Converting it i.e. updating it from request
Persisting it back in repository using save
Creating a CommandDTO from saved uuid.
Now, when we write UnitTest for a unit then that means we are testing the working of that unit only.
Here MenuServiceImpl is not responsible to ensure whether the values were mapped correctly or if the updated value was saved correctly, the only work MenuServiceImpl performs is to call these dependencies in a particular order and that is all we need to test here.
So, here is a correct test for MenuServiceImpl's update method:
#Test
void test_Update() {
MenuRequest request = new MenuRequest("1234");
Menu existingMockMenu = mock(Menu.class);
when(menuRepository.findByUuid("1234")).thenReturn(existingMockMenu);
Menu savedMockMenu = mock(Menu.class);
when(menuRepository.save(existingMockMenu)).thenReturn(savedMockMenu);
menuServiceImpl.update(request);
verify(menuRepository).save(existingMockMenu);
}
If at all you want to test if MenuRepository is saving the Menu correctly, you will have to write a new test for MenuRepository, if this is actually a repository that interacts with the database, then it will be helpful to write DB Integration Test.
Regarding using ArgumentCaptor, let's suppose your MenuConverter is changing the values of the menu, something like this:
public class MenuConverter {
public static void convert(MenuRequest request, Menu menu) {
menu.setName(request.getName() + "_new");
}
}
then first of all, you should have a different test for MenuConverter that tests this logic, still if you want to test this in the test of MenuServiceImpl itself, then you don't have to use ArgumentCaptor, you can use something like:
#Test
void test_Update() {
// same as above
verify(existingMockMenu).setName("Menu1_new");
}
This verifies that MenuConverter attempted to update the name of your existingMockMenu
Related
I need to unit test a method, and I would like mock the behavior so that I can test the necessary part of the code in the method.
For this I would like access the object returned by a private method inside the method I am trying to test. I created a sample code to give a basic idea of what I am trying to achieve.
Main.class
Class Main {
public String getUserName(String userId) {
User user = null;
user = getUser(userId);
if(user.getName().equals("Stack")) {
throw new CustomException("StackOverflow");
}
return user.getName();
}
private User getUser(String userId) {
// find the user details in database
String name = ""; // Get from db
String address = ""; // Get from db
return new User(name, address);
}
}
Test Class
#Test (expected = CustomException.class)
public void getUserName_UserId_ThrowsException() {
Main main = new Main();
// I need to access the user object returned by getUser(userId)
// and spy it, so that when user.getName() is called it returns Stack
main.getUserName("124");
}
There are only two ways to access private:
using reflection
extend the scope
maybe waiting for Java 9 to use new scope mechanisms?
I would change the scope modifier from private to package scope. Using reflection is not stable for refactoring. It doesn't matter if you use helpers like PowerMock. They only reduce the boiler-plate code around reflection.
But the most important point is you should NOT test too deep in whitbox tests. This can make the test setup explode. Try to slice your code into smaller pieces.
The only information the method "getUserName" needs from the User-object is the name. It will validate the name and either throw an exception or return it. So it should not be necessary to introduce a User-object in the test.
So my suggestion is you should extract the code retreiving the name from the User-object into a separate method and make this method package scope. Now there is no need to mock a User-Object just the Main-Object. But the method has its minimal information available to work properly.
class Main {
public String getUserName(String userId) {
String username = getUserNameFromInternal(userId);
if (userName.equals("Stack")) {
throw new CustomException("StackOverflow");
}
return user.getName();
}
String getUserNameFromInternal(String userId) {
User user = getUser(userId);
return user.getName();
}
...
}
The test:
#Test (expected = CustomException.class)
public void getUserName_UserId_ThrowsException() {
Main main = Mockito.mock(new Main());
Mockito.when(main.getUserNameInternal("124")).thenReturn("Stack");
main.getUserName("124");
}
Your problem that call to new within your private method.
And the answer is not to turn to PowerMock; or to change the visibility of that method.
The reasonable answer is to "extract" that dependency on "something that gives me a User object" into its own class; and provide an instance of that class to your "Main" class. Because then you are able to simply mock that "factory" object; and have it do whatever you want it to do.
Meaning: your current code is simply hard-to-test. Instead of working around the problems that are caused by this, you invest time in learning how to write easy-to-test code; for example by watching these videos as a starting point.
Given your latest comment: when you are dealing with legacy code, then you are really looking towards using PowerMockito. The key part to understand: you don't "mock" that private method; you rather look into mocking the call to new User() instead; as outlined here.
You can use a PowerMock's mockPrivate but I don't recommend it.
If you has such a problem it usually mean that your design is bad.
Why not making the method protected?
I'm using Play 2.3.7 and I basically have the same question as this but I am using Java and I don't know Scala.
In my case I have a CSVData class with a name attribute that I want to be unique. In my controller actions for both create() and update(Long id) I bind a form to this class with
Form<CSVData> formData = Form.form(CSVData.class).bindFromRequest();
As explained in the Play documentation, the validate() function gets called on bindFromRequest(). Here's my current validate function:
public List<ValidationError> validate() {
List<ValidationError> errors = new ArrayList<ValidationError>();
// Unique validation on name
if(CSVData.find.where().eq("name", getName()).findRowCount() != 0) {
errors.add(new ValidationError("name", "Name must be unique. That value is already taken."));
}
// other stuff
}
Clearly the issue with my current solution is the fact that when update() gets called, there already is a row in the DB with that name. This answer suggests checking the id attribute of the object in the controller action, and then provides a Scala code snippet, but that example contained a User class, and my application does not. How can I can check if the object in the database with the matching name is the same one that I am currently updating? Should I check for this in the validate() function or in my controller?
Create two separate classes one for insert() - InsertCsvData and one for updated() - UpdateCsvData with two different validate() implementations. Optionally, create base class for storing common properties/behaviours.
It's not possible to perform different validations for create() and update() in the validate() function in the model, but you can perform the validations in the respective controller actions, like so
public static Result create() {
Form<CSVData> formData = Form.form(CSVData.class).bindFromRequest();
}
public static Result update() {
Form<CSVData> formData = Form.form(CSVData.class).bindFromRequest();
if( !formData.hasErrors() ){
if(formData.field("name").value().equals( .... ) {
// do some check here
// add ValidationError if needed
}
}
}
I had not considered performing the validations directly in the controller before, but this is possible.
Is there any way to check directly, if the content of a form field in play framework has changed?
for example my Device.java is something like this:
class Device{
String name;
String type;}
and then somewhere in my controller, I have a form of type Device. is there any way to check using boundForm if the value of the name property has changed?
public class Devices extends Controller {
private static final Form<Device> deviceForm = Form.form(Device.class);
public static Result details(Device device) {
if (device == null) {
return notFound(String.format("Device does not exist. "));
}
Form<Device> filledForm = deviceForm.fill(device);
return ok(views.html.devices.details.render(filledForm));
}
public static Result save() {
Form<Device> boundForm = deviceForm.bindFromRequest();
...
[here]
...
}
}
note: details method will show the user the filled form, user may or may not change the values, and then by pressing a Save button , the save() method will be called.
In shortest words Form<T> isn't able to check if fields are changed it's just stateless between request and to check it you just need to get record from DB and compare field, by field.
Also you shouldn't rely on client-side validation as it's mainly for cosmetic, NOT for safety. Remember that it can be manipulated or omitted quite easy with common webdev tools.
Finally you shouldn't resign from Form validation possibilities,as it's very handy tool, instead you can cooperate with it, i.e. it can be something like:
public static Result save() {
Form<Device> boundForm = deviceForm.bindFromRequest();
if (boundForm.hasErrors()){
return badRequest(devices.details.render(boundForm));
}
Device boundDevice = boundForm.get();
Device existingDevice = Device.find.byId(boundDevice.id);
if (boundDevice.name.equals(existingDevice.name)){
boundForm.reject("Contents are identical");
return badRequest(devices.details.render(boundForm));
}
// else... form hasn't errors, name changed - should be updated...
boundDevice.update(boundDevice.id);
}
So you can display it in your view i.e.:
#if(yourForm.error("identicalContent")!=null) {
<div class="alert alert-danger">#yourForm.error("identicalContent").message</div>
}
As you can see from this sample - if you want just to skip UPDATE query if no changes - to save resources - it does not make sense, as you need to make SELECT query anyway to compare. In other cases (like i.e. additional logging ONLY if changed) above snippet is correct solution.
EDIT: replaced 'retrieve.name == "name1"' by 'retrieve.name.equals("name1")'.
EDIT2: Added #BeforeClass and #AfterClass (credit: http://digitalsanctum.com/2012/06/01/play-framework-2-tutorial-ebean-orm/).
I'm writing JUnit tests for a play web app and for some odd reason I can't seem to modify the database entries. Here's the simplest example I could come up with that illustrates the problem:
#BeforeClass
public static void setup() throws IOException {
app = Helpers.fakeApplication(Helpers.inMemoryDatabase());
Helpers.start(app);
server = Ebean.getServer("default");
ServerConfig config = new ServerConfig();
config.setDebugSql(true);
ddl = new DdlGenerator((SpiEbeanServer) server, new H2Platform(), config);
// drop
String dropScript = ddl.generateDropDdl();
ddl.runScript(false, dropScript);
// create
String createScript = ddl.generateCreateDdl();
ddl.runScript(false, createScript);
}
#AfterClass
public static void stopApp() {
// drop
String dropScript = ddl.generateDropDdl();
ddl.runScript(false, dropScript);
Helpers.stop(app);
}
#Test
public void UserModify(){
// create user (User extends Model)
User user = new User();
user.id = (long) 1;
user.name = "name1";
user.save();
// modify
user.name = "name2";
user.update();
user.save();
// look-up
User retrieve = User.find.byId((long) 1);
assertFalse("Old name", retrieve.name.equals("name1"));
}
Needless to say this should pass, but it doesn't... I know you can use "update()" to change database fields, because someone else on the project says he uses it like that and it works.
Play Framework documentation: http://www.playframework.com/documentation/2.1.1/Home
Any ideas why this test fails?
This is happening because of a problem in Play Framework.
Play doesn't enhance code in "test" folder, only in "app" folder.
Because of that getters and setters are not generated, and Ebean is relying on setters to detect that object is dirty and to support lazy loading. This way in your case Ebean doesn't know that property was updated on object.
As a simple solution, you can create getters and setters yourself. Also, that seems to be fixed already and probably should be included in next Play release: https://github.com/playframework/Play20/blob/master/framework/test/integrationtest-java/test/models/EbeanEnhancementTest.java
Here's a simple Play project with User model and working test: https://github.com/pharod/so-play2-issue1
See more details on getters and setters generation by Play here, in "Caveats" section near bottom of page: http://www.playframework.com/documentation/2.1.1/JavaEbean
As others have stated, you should use .equals() for string equality.
But the main problem is that to run this kind of tests (accessing the database) You need a running application.
In play, this could be done, by running a fake application with the test. Check out this guide:
http://www.playframework.com/documentation/2.0/JavaTest
I'm starting some testing using Mockito on some service classes I use for connecting to my data store. I now want to determine the best practice way writing tests for it. The principle is for each entity there is a way to list, add, delete etc a row from the data store(mongo/mysql etc) for a specific entity.
Take this class that allows me to talk to my database that stores a list of companies
public class CompanyService extends Service{
public CompanyService() {
...
}
public saveCompany(Company company) {
...
}
// get a list of all companies
public List<Company> getCompanies() {
List<Company> companies = new ArrayList<Company>();
try {
CompanyResult<Rows<String, String>> rows = db.query(....);
for (Row<String, String> row : rows.getResult()) {
companies.add(row.getColumns());
}
catch (Exception e){
logger.warn("Error retrieving companies", e);
}
}
}
What exactly should I test in the getCompanies method and how can I use Mockito to do it?
Your System Under Test is the Company Service. You want to test that, assuming all its dependencies/collaborators function properly, it functions properly.
The db object looks like the only dependency/collaborator you need to worry about within the getCompanies() method. Use Mockito to mock the call to db.query()
You could set up a test method like so:
#Test
public void testGetCompanies() {
/*** Arraign ***/
CompanyService cs = new CompanyService();
// Setup mock db
DB mockDb = mock(DB.class);
// Setup fake results for query
CompanyResult<Rows<String, String>> sampleResults = ... // build sample results here
// Have query on mock return fake results
when(db.query(/* match arguments */)).thenReturn(sampleResults);
// Tell your System Under Test to use the mock collaborator
cs.setDB(mockDb);
/*** Act ***/
CompanyResult<Rows<String, String>> results = cs.getCompanies();
/*** Assert ***/
... // Test that results and sampleResults are effectively the same
}
You could test how your code would work if the db query returned and empty result,null or if row has null values or unexpected values. You could mock the db class to return these values. You could also mock the db class to throw an exception to see how your code could react.