I am testing Java app with JUnit. The following is the source code of a specific method:
public class Surgery {
Vector<Patient> patients;
String name;
public Surgery(String name) {
patients = new Vector<Patient>();
this.name = name;
}
public Patient findPatient(String name) {
Iterator<Patient> patientIt = patients.iterator();
while(patientIt.hasNext()) {
Patient next = patientIt.next();
if (next.getName().equals(name))
return next;
}
return null;
}
This is JUnit test method:
public class SurgeryTest {
private Vector<Patient> vector;
Surgery surgery_N =new Surgery("Teddy");
ByteArrayOutputStream ans = new ByteArrayOutputStream();
final String separator = System.getProperty("line.separator");
#Test
public void testFindPatient() {
surgery_N.findPatient("Teddy");
}
}
I need to test each statement in the source code method. I stuck, don't know what else to do. Any solution?
Your Surgery class contains no way to add patients to it from the code sample you have given us, so your unit test should be finding nothing.
To test each statement in the source code method you should create multiple tests that cover each one of the possible paths in your code. That means, in your tests you will want to test for the scenario where you return a patient name if it exists, and one for where the patient doesn't exist (returning null).
Here's some example methods for you to work from:
public void testFindPatientWherePatientExists() {
Patient thePatient = surgery.findPatient("Teddy");
assertEquals("Teddy", thePatient.getName());
}
public void testFindPatientWherePatientDoesntExist() {
assertNull(surgery.findPatient("I dont exist"));
}
What is the expected result when you call findPatient(...)? You can compare the expected result with the actual result using assertEqual(...)
public class SurgeryTest {
Surgery surgery;
#Before
public void setUp() {
surgery = new Surgery("Teddy");
}
#Test
public void testFindPatient() {
Patient p = ...; // expected result
assertEquals(p, surgery.findPatient("Teddy"));
}
}
The method with #Before annotation will be called before each method with #Test annotation. Therefore, the new surgery object is tested every time.
First, note that the name you pass to the Surgery constructor does NOT get placed into the patients Vector. It is a field of Surgery. Suggest you rename the arg to "surgeryName" and then you'll need a new method, addPatient(String patientName);, and call addPatient("Teddy").
That said, in general, you should test for both the name present and name absent case. e.g.
(exact name of the assert methods might vary)
assertNotNull(surgery.findPatient("Teddy"));
assertNull(surgery.findPatient("A Name I did not add"));
(first line would be more precise if it were assertEquals() like wannik suggested.
Related
I am trying to do when().thenReturn() but facing issues. Following is the sample code that I came up with for SOers:
import java.util.List;
public class Sample {
public void function(List<SampleA> list) {
for (SampleA s : list) {
List<SampleB> nameList = s.v1;
for (SampleB m : nameList) {
SampleC value = m.getV2();
if (value != null) {
doSomething(value);
} else {
LOGGER.warn("No valid value");
}
}
}
}
}
public class SampleA{
List<SampleB> v1;
}
public class SampleB{
SampleC v2;
public SampleC getV2(){
return this.v2;
}
}
This might be silly but I just want to understand why can't I do:
SampleB sampleB = new SampleB();
when(sampleB.getV2()).thenReturn(any(SampleC.class));
I'm guessing that you are trying to test Sample.function and are trying to work out how to mock SampleC values. If so, then your code should probably look something like:
#Test
void testFunction() {
SampleC mockC = mock(SampleC.class);
SampleB mockB = mock(SampleB.class);
SampleA mockA = mock(SampleA.class);
when(mockB.getV2()).thenReturn(mockC);
when(mockA.getV1()).thenReturn(List.of(mockB));
Sample sample = new Sample();
sample.function(List.of(mockA));
// verify doSomething was called with mockC as its argument
when(mockB.getV2()).thenReturn(null);
sample.function(List.of(mockA));
// verify "No valid value" was logged
}
I've added a getV1 method rather than direct access to the field because my fingers refuse to write the code that directly accesses a public field :-)
If you're struggling with verifying that doSomething was called then that's where argument matching comes in. Ideally you'd have a SomethingDoer that you inject into your sample:
interface SomethingDoer {
void doSomething(SampleC sample);
}
class Sample {
private final SomethingDoer somethingDoer;
public Sample(SomethingDoer somethingDoer) {
this.somethingDoer = somethingDoer;
}
}
Then you'd add the following to your test:
SomethingDoer doer = mock(SomethingDoer.class);
Sample sample = new Sample(doer);
...
verify(doer).doSomething(mockC);
Also note that the code above is poor style: the tests should be split up with the setup code in a #BeforeEach method. I've just put it in a single test to keep the answer simple.
any(…) is an ArgumentMatcher. It is used to match arguments. You cannot use it to return instances of classes. What would "any SampleC" be?
You'd use it with mock objects, like so:
SampleInterface sample = Mockito.mock(SampleInterface.class);
when(sample.function(any(List.class))).thenReturn(new ArrayList<>());
// or: .thenAnswer(a -> new ArrayList<>()); to return a new instance for each call
you can't use ArgumentMatchers inside thenReturn or thenAnswer. You have to specify exactly what to return. For Example:
SampleB sampleB = mock(SampleB.class)
when(sampleB.getV2()).thenReturn(new SampleC());
I am very rusty in java and trying to relearn it again in a youtube tutorial, the code is as follows: This is Person.java
package com.gurmeet.javacourse.lesson2;
import com.gurmeet.javacourse.lesson3.Name;
public class Person {
private Name personName;
private static int personCounter;
public Person()
{
personCounter++;
/*
* empty on purpose - default constructor
*/
}
public Person(Name personName) {
this.personName = personName;
}
public String helloWorld() {
// TODO Auto-generated method stub
return "Hello World";
}
public String hello(String name) {
// TODO Auto-generated method stub
return "Hello " + name;
}
public static int numberOfPersons() {
// TODO Auto-generated method stub
return personCounter;
}
}
And this is PersonTest.java
package com.gurmeet.javacourse.lesson2;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class PersonTest {
#Test
public void shouldReturnHelloWorld() {
Person marcus = new Person();
assertEquals("Hello World", marcus.helloWorld());
}
#Test
public void shouldReturnHelloMarcus() {
Person person = new Person();
assertEquals("Hello Marcus", person.hello("Marcus"));
}
#Test
public void shouldReturnNumberOfPersons() {
Person person1 = new Person();
Person person2 = new Person();
Person person3 = new Person();
Person person4 = new Person();
assertEquals(4, Person.numberOfPersons() - 1);
}
}
these two above are in the same package but I created another package in the same project like below:
package com.gurmeet.javacourse.lesson3;
public class Name {
}
I am using JUnit to test my code, but in the for the last testing in my code I keep getting an error, you see the number of persons is supposed to be 4, but the result keeps showing 5. The youtube guy got the correct answer and I didnt even though I followed his coding correctly. What I came to understand in the tutorial is that the static is a global, at class level and since I have two classes created in the same package, the personCounter is not at 1 but 2 at default, therefore the it keeps showing 5 instead of 4. I solved it by subtracting 1 in the method but I dont think that is the correct way. Is my reasoning correct? or is there another explanation. And above all else, how do I solve this? Go easy on me if I made any mistake.
I guess the main problem is that when you run the first two tests, the static variable personCounter becomes 2 as you are creating two objects. Then when you run the third test, it starts from the 3 and goes upto 6.
I guess if you add another method to set the person counter to zero and call this method before creating the objects in the third test, you would get 4 as your output.
Add this in you class as a function
public static void setNumberOfPersons(int value) { personCounter = value; }
And then call Person.setNumberOfPersons(0); first in the third test
Edit:
Thanks to seelenvirtuose(see comment) for pointing out the random order part for tests. As said, I suppose the problem would go away by setting the value to 0 in the third test. This way the order would not matter.
edit: I apologize if the post has been considered too confusing, i'll edit it and leave only the parts regarding my problem...
I have written a class named "ArithmeticNode" which implements an interface containing the following methods:
public void turnOn() {
arithmeticServer.start();
}
public void turnOff(){
arithmeticServer.stop();
}
and contains also a private method:
private void negotiatePort(NodeManifest nodeManifest, ThriftServer arithmeticServer) {
while(true) {
int proposedPort = arithmeticServer.start();
int returnedPort = managementClient.registerNode(nodeManifest, proposedPort);
if(proposedPOrt != returnedPort) {
arithnemticServer.stop();
}
else
break;
}
}
What I'm trying to do is to write a test in which I create a number of these arithmetic nodes and make them register to a management node that I've already written and use as server. Then there will be a second part of my project where I'll make those nodes interact, but that's not part of the actual problem.
I have already written a working junit test:
#Test
public void testArithmeticServer() throws Exception {
List<NodeManifest> nodeManifests = new ArrayList<>();
nodeManifests.add(new NodeManifest("localhost", Arrays.asList(new String[]{"addition"})));
nodeManifests.add(new NodeManifest("localhost", Arrays.asList(new String[]{"subtraction","multiplication"})));
nodeManifests.add(new NodeManifest("localhost", Arrays.asList(new String[]{"addition","multiplication","division"})));
nodeManifests.add(new NodeManifest("localhost", Arrays.asList(new String[]{"addition","division"})));
List<ThriftServer> arithmeticServers = new ArrayList<>();
for (NodeManifest nodeManifest : nodeManifests) {
ThriftServer arithmeticServer = new ThriftServer(ArithmeticServiceHandler.class);
arithmeticServers.add(arithmeticServer);
negotiatePort(nodeManifest,arithmeticServer);
}
private void negotiatePort() {
while(true) {
int proposedPort = arithmeticServer.start();
int returnedPort = managementClient.registerNode(nodeManifest, proposedPort);
if(proposedPOrt != returnedPort) {
arithnemticServer.stop();
}
else
break;
}
}
I need to write a test where i don't have to put the negotiatePort method directly inside the test code, but I call for each node the private negotiatePort method I have inside my arithmeticNode class; I'm not sure if it's possible to do that and how. I hope I've been less confusing than the previous version of the post :-)
You can make the negotiatePort method package local. Then you would be able to call it within your test (which should reside in the same package). And nobody outside the package would be able to use it
I have encountered what I assume might be a bug with Mockito, but was wondering if anyone else can shed light as to why this test doesn't work.
Basically, I have two objects, like this:
public class FirstObject {
private SecondObject secondObject;
public SecondObject getSecondObject() { return secondObject; }
}
public class SecondObject {
private String name;
public String getName() { return name; }
}
The first object is mocked via annotation and the before method:
#Mock
FirstObject mockedFirstObject;
#Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
The second object is mocked in a method:
public SecondObject setupMockedSecondObject() {
SecondObject secondObject = Mockito.mock(SecondObject.class);
Mockito.when(secondObject.getName()).thenReturn("MockObject");
return secondObject;
}
When thenReturn contains a direct call to this method to setup and obtain a mock of the second object, it fails:
#Test
public void notWorkingTest() {
Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(setupMockedSecondObject());
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
But, when the mock returned by the same method is assigned to a local variable, which is used in thenReturn, it works:
#Test
public void workingTest() {
SecondObject mockedSecondObject = setupMockedSecondObject();
Mockito.when(mockedFirstObject.getSecondObject()).thenReturn(mockedSecondObject);
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
Are we doing something wrong or is this indeed a bug/limitation in Mockito? Is there a deliberate reason for this not working?
This is indeed a limitation of Mockito, and it is referenced in their FAQ:
Can I thenReturn() an inlined mock()?
Unfortunately you cannot do this:
when(m.foo()).thenReturn(mock(Foo.class));
// ^
The reason is that detecting unfinished stubbing wouldn't work if we allow above construct. We consider is as a 'trade off' of framework validation (see also previous FAQ entry). However you can slightly change the code to make it working:
//extract local variable and start smiling:
Foo foo = mock(Foo.class);
when(m.foo()).thenReturn(foo);
The workaround, as mentioned, is to store the desired returned value in a local variable, like you have done.
The way I understand it is that Mockito validates the usage you make of it every time you call its methods. When another method is called during an on-going stubbing process, you are breaking its validation process.
You can't use a method in thenReturn, but you can in thenAnswer
Your code will be called after the when condition will occur,
unlike any workaround based on thenReturn
Thus you could write:
#Test
public void nowWorkingTest() {
Mockito.when(mockedFirstObject.getSecondObject()).thenAnswer(new Answer<Map>() {
#Override
public Map answer(InvocationOnMock invocation) {
return setupMockedSecondObject();
}
});
Assert.assertEquals(mockedFirstObject.getSecondObject().getName(), "MockObject");
}
Let find another example here
#Test
public void testAuthenticate_ValidCredentials() throws FailedToAuthenticateException {
String username = "User1";
String password = "Password";
/*Configure Returning True with when...thenReturn configuration on mock Object - Q5*/
//Write your code here
assertTrue(authenticator.authenticateUser(username, password));
}
I am very new to Mockito and jUnit and TDD in general and I try to learn the right way to do TDD. I need couples of example to kick start my brain. SO please help me
So I have a method getNameInc(String dirPath, String filenName). So given a fileName like bankAccount.pdf, and if in this folder, no file name bankAccount.pdf, then return bankAccountAA.pdf. If there is exist one bankAccount.pdf then return bankAccountBB.pdf The increment is AA-ZZ. When it reach ZZ then it roll back to AA. I already implement the logic of this method. How do I unit test this method using Mockiti and jUnit?
EDIT
Here is the class and methods that are involved.
public class PProcessor{
private final Map<Integer, String> incMap = new HashMap<Integer, String>();
private String getNameInc(String dirPath, String filenName){
String[] nameList = new File(dirPath).list(new FilenameFilter(){
public boolean accept(File file, String name) {
//only load pdf files
return (name.toLowerCase().endsWith(".pdf"));
}
});
//Return the number of occurance that a given file name appear
//inside the output folder.
int freq = 0;
for(int i=0; i<nameList.length; i++){
if(fileName.equals(nameList[i].substring(0, 8))){
freq++;
}
}
return incMap.get(freq);
}
private void generateIncHashMap(){
incMap.put(new Integer(0), "AA");
incMap.put(new Integer(1), "BB");
incMap.put(new Integer(2), "CC");
...
}
}
generateIncHashMap() will be called in the constructor to pre-generate the hash map
You are trying to test your getNameInc(..) method, I assume. When you call it, it looks for the files in the directory you specify, and based on what it finds, decorates the name you gave it.
To make the class unit-testable, you should abstract the dependency on the file system, so that in a mock, you can simulate whatever directory contents you want. Your class will accept an instance of this interface as a dependency, and call it to find out what's in the directory. When you use the class in your program for real, you will supply an implementation of this interface that delegates to the JDK filesystem calls. When you unit-test the class, you will supply Mockito mocks of this interface.
Avoid putting too much logic into the FilesystemImpl class, since you can't write a strict unit test for it. Keep it a very simple wrapper around the filesystem, so that all the intelligent stuff is in Yourclass, which you will write plenty of unit tests for.
public interface Filesystem {
boolean contains(String dirpath, String filename);
}
public class FilesystemImpl {
boolean contains(String dirpath, String filename) {
// Make JDK calls to determine if the specified directory has the file.
return ...
}
}
public class Yourmainclass {
public static void main(String[] args) {
Filesystem f = new FilesystemImpl();
Yourclass worker = new Yourclass(f);
// do something with your worker
// etc...
}
}
public class Yourclass {
private Filesystem filesystem;
public Yourclass(Filesystem filesystem) {
this.filesystem = filesystem;
}
String getNameInc(String dirpath, String filename) {
...
if (filesystem.contains(dirpath, filename) {
...
}
}
}
public class YourclassTest {
#Test
public void testShouldAppendAAWhenFileExists() {
Filesystem filesystem = Mockito.mock(Filesystem.class);
when(filesystem.contains("/some/mock/path", "bankAccount.pdf").thenReturn(true);
Yourclass worker = new Yourclass(filesystem);
String actual = worker.getNameInc("/some/mock/path", "bankAccount.pdf");
assertEquals("bankAccountAA.pdf", actual);
}
#Test
public void testShouldNotAppendWhenFileDoesNotExist {
Filesystem filesystem = Mockito.mock(Filesystem.class);
when(filesystem.contains("/some/mock/path", "bankAccount.pdf").thenReturn(false);
Yourclass worker = new Yourclass(filesystem);
String actual = worker.getNameInc("/some/mock/path", "bankAccount.pdf");
assertequals("bankAccount.pdf", actual);
}
}
Since there's a lot of duplication between the tests, you'd probably create a setup method and do some of the work there, and create some instance variables for the tests to use:
private static final String TEST_PATH = "/some/mock/path";
private static final String TEST_FILENAME = "bankAccount.pdf";
private Filesystem filesystem;
private Yourclass worker;
#Before
public void setUp() {
filesystem = Mockito.mock(Filesystem.class);
worker = new Yourclass(filesystem);
}
#Test
public void testShouldAppendAAWhenFileExists() {
when(filesystem.contains(TEST_PATH, TEST_FILENAME).thenReturn(true);
String actual = worker.getNameInc(TEST_PATH, TEST_FILENAME);
assertEquals("bankAccountAA.pdf", actual);
}
etc...
For what you have described there I wouldn't bother with Mockito, there doesn't seem to be anything to mock (because it is easy to manipulate the file system).
I would test ...
- What happens if I call getNameInc and there are no matching files already
- What happens if I call getNameInc and there are files AA-YY there already
- What happens if I call getNameInc and file ZZ is there already
The point of TDD though is that you should have already written these tests and then implemented your code to make the tests pass. So you won't really be doing TDD since you already have the code.