So here is the thing, I found new Integer() in my code and which is deprecated now I planed to remove that pic of code (new Integer()) to Integer.valueOf() but as company rules I have to write the Junit for that change, I just need to verify that pic of code no need to write whole coverage of the method, Now I am getting confused, how I can do it.
Before:
#SuppressWarnings({ "rawtypes", "unchecked" })
public List getLogsByLcrErrorCodeInLast(String code, long period) throws Exception {
Object params = new ObjectPair(new Integer(code), new MesDbUtil(ibatis).getPastMesDbDate(period));
List mesdbLogs = client.queryForList("LcrLog.getLogsByLcrErrorCodeInLast", params);
Iterator mesdbLogsIter = mesdbLogs.iterator();
List results = new ArrayList();
while (mesdbLogsIter.hasNext()) {
MesDbLcrLog mesdbLog = (MesDbLcrLog) mesdbLogsIter.next();
results.add(mesdbLog.getLog());
}
return results;
}
After::
#SuppressWarnings({ "rawtypes", "unchecked" })
public List getLogsByLcrErrorCodeInLast(String code, long period) throws Exception {
Object params = new ObjectPair(Integer.valueOf(code), new MesDbUtil(ibatis).getPastMesDbDate(period));
List mesdbLogs = client.queryForList("LcrLog.getLogsByLcrErrorCodeInLast", params);
Iterator mesdbLogsIter = mesdbLogs.iterator();
List results = new ArrayList();
while (mesdbLogsIter.hasNext()) {
MesDbLcrLog mesdbLog = (MesDbLcrLog) mesdbLogsIter.next();
results.add(mesdbLog.getLog());
}
return results;
}
I tried to do like following, I don't know is it correct or not, and need to go for some professional way for it too because of code review;
public class integerValueOf(){
private Integer code=23;
#Test
public void testGetLogsByLcrErrorCodeInlast() throws Exception{
assertEquals(new Integer(code), Integer.valueOf(code));
}
}
Are you unit testing every other API in the JRE? No? Why would you unit test Integer.valueOf(...) then?
Your existing unit tests for getLogsByLcrErrorCodeInLast should cover the change in the integer instantiation. E.g. you have tests for different values of code and check that you get the expected params in client.queryForList(...).
If you don't have the tests, then you need to write them before you change the code.
Edit: I didn't even notice it earlier, but your production code converts a String to an Integer and your sugested unit test converts an Integer to an Integer. You're idea is fundamentally flawed because it doesn't test the code that you execute in production. It lets errors like this pass testing with green flags.
Related
Suppose I have a code to test
void myMethod()
{
byte []data = new byte[1];
data[0]='a';
output.send(42, data);
data[0]='b';
output.send(55, data);
}
I write a test:
testSubject.myMethod();
verify(output).send(eq(42), aryEq(new byte[]{'a'}));
verify(output).send(eq(55), aryEq(new byte[]{'b'}));
The test will fail as the method implementation reuses the same array for both calls, it is impossible to match args of the first send invocation after method finishes, so techically the verify statements should be specified before the method call, something like an expectation.
What is the right way to test such methods?
Well it looks like Mockito is a bit inconvenient here. It detects the method call and logs it (use mock(MyOutput.class, withSettings().verboseLogging()); to enable logging), but it stores the reference to the array you're passing and thus is influenced when you mutate the array. It then thinks the method call was send(42, [98]) rather than send(42, [97]).
A possible way to work with that is to use the expectations you've mentioned. For example you can use a counter and increment it if a call matched the expectations (it is really just a workaround and rather nasty):
MyOutput mock = mock(MyOutput.class, withSettings().verboseLogging());
Main subject = new Main(mock);
AtomicInteger correctCallsCounter = new AtomicInteger(0);
doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(42), aryEq(new byte[]{'a'}));
doAnswer(invocation -> correctCallsCounter.incrementAndGet()).when(mock).send(eq(55), aryEq(new byte[]{'b'}));
subject.myMethod();
assertThat(correctCallsCounter.get(), is(2));
It works, because doAnswer is triggered when the call happens and when the byte array hasn't been changed yet.
The big downside of this workaround is, that it only works with void methods. If send would return "something", then I currently don't see a way to work around that.
Well and the other one is that this is obviously a rather nasty workaround.
So I would suggest to refactor your code a bit (use a new array) if that is possible. That would avoid these issues here.
If your send method would indeed return something and your myMethod method would rely on it, then you would usually mock it this way (send is expected to return a String in this example):
when(mock.send(eq(55), aryEq(new byte[]{'b'}))).thenReturn("something");
In order to still use the above mentioned workaround, you could change the doAnswer method to increment your counter and return your String (which you would mock anyway, thus it isn't that bad):
doAnswer(invocation -> {
correctCallsCounter.incrementAndGet();
return "something";
}).when(mock).send(eq(42), aryEq(new byte[]{'a'}));
Use an Answer to copy the value of the parameter.
Here is some code (it is not pretty):
public class TestMyClass
{
private static List<byte[]> mockDataList = new ArrayList<>();
#InjectMocks
private MyClass classToTest;
private InOrder inOrder;
#Mock
private ObjectClass mockOutputClass;
#After
public void afterTest()
{
inOrder.verifyNoMoreInteractions();
verifyNoMoreInteractions(mockOutputClass);
}
#Before
public void beforeTest()
{
MockitoAnnotations.initMocks(this);
doAnswer(new Answer()
{
#Override
public Object answer(
final InvocationOnMock invocation)
throws Throwable
{
final byte[] copy;
final byte[] source = invocation.getArgument(1);
copy = new byte[source.length];
System.arraycopy(source, 0, copy, 0, source.length);
mockDataList.add(copy);
return null;
}
}).when(mockOutputClass).send(anyInt(), any(byte[].class));;
inOrder = inOrder(
mockOutputClass);
}
#Test
public void myMethod_success()
{
byte[] actualParameter;
final byte[] expectedFirstArray = { (byte)'a' };
final byte[] expectedSecondArray = { (byte)'b' };
classToTest.myMethod();
actualParameter = mockDataList.get(0);
assertArrayEquals(
expectedFirstArray,
actualParameter);
inOrder.verify(mockOutputClass).send(eq(42), any(byte[].class));
actualParameter = mockDataList.get(1);
assertArrayEquals(
expectedSecondArray,
actualParameter);
inOrder.verify(mockOutputClass).send(eq(55), any(byte[].class));
}
}
Note that the value of the parameter is compared separate of the verification of the call, but the order of the parameters is still verified (i.e. 'a' array first, then 'b' array).
I want to mock "source" when the method "ProductAdapterService.adapt" is called by other class.
How to deal that? I really tried a lot of ways. Please help me. I am a new guy.Thanks a lot!
public class ProductAdapterService {
private final SearchParameter parameter;
private List<Festival> festivals;
public ProductAdapterService(SearchParameter parameter) {
this.parameter = parameter;
}
public SingleProduct adapt(SearchHit hit, boolean bidding) {
//I want to mock "source", I don't want to use "hit.getSource()"
Map<String, Object> source = hit.getSource();
SingleProduct sp = new SingleProduct();
sp.setId(TypeConverter.toInt(source.get(FieldName.PRODUCT_ID)));
sp.setName(TypeConverter.toString(source.get(FieldName.NAME)));
sp.setPrice(this.price(source.get(FieldName.PRICE), source.get(FieldName.PRICE_MAP), source.get(FieldName.FIRST_START_CITIES)));
sp.setLevel(TypeConverter.toInt(source.get(FieldName.PRODUCT_LEVEL)));
sp.setDepartureCityId(this.departureCity(source.get(FieldName.DEPARTURE_CITY_ID), source.get(FieldName.FIRST_START_CITIES)));
sp.setSaleMode(TypeConverter.toString(source.get(FieldName.SALE_MODE)));
sp.setBrandName(this.providerBrandName(source.get(FieldName.PROVIDER_BRAND)));
sp.setSaleCount(TypeConverter.toInt(source.get(FieldName.MONTHLY_ORDER)));
sp.setCommentCount(TypeConverter.toInt(source.get(FieldName.COMMENT_COUNT)));
sp.setCommentScore(TypeConverter.toFloat(source.get(FieldName.COMMENT_SCORE)));
sp.setBuType(BuType.GT);
sp.setType(this.productType(source.get(FieldName.SEARCH_TAB_TYPE_SHOW), sp.getSaleMode()));
sp.setSaleout(this.saleout(source.get(FieldName.NON_SALEOUT_CITIES), sp.getDepartureCityId()));
if (!sp.isSaleout()) {
sp.setFestival(this.festival(source.get(FieldName.FESTIVAL_IDS)));
}
System.out.println("sp.getName(): " + sp.getName());
return sp;
}}
And below is my test code:
public class TabSearcherTest0 {
#Test
public void test() {
SearchParameter parameter = SearchParameter.create();
Ghost.begin();
parameter.getFiltered().setTab(TabType.ALL);
parameter.getPoi().setKeyword("Spa");
parameter.getClient().setTrace(TraceMode.MAIN);
Map<String, Object> mapMock = new HashMap<String, Object>();
mapMock.put("productgroupid", "12877");
mapMock.put("productid", "5539739");
mapMock.put("firststartcitys", "[1, 2]");
mapMock.put("nonsaleoutcities", "[1, 2]");
mapMock.put("productdiamondlevel", "4");
mapMock.put("commentcount", "0");
mapMock.put("price", "0.0");
mapMock.put("name", "TestName");
mapMock.put("searchtabtypeshow", "1");
mapMock.put("comment", "0.0");
mapMock.put("salemode", "S");
mapMock.put("providerbrandid", "999999");
mapMock.put("departurecityid", "2");
// how to inject the map?
// ???
SearchModel model = SearchContext.createContext(parameter).search();
Ghost.end();
System.out.println(model);
}}
You are getting "mocking" the wrong way. You only used it when you can not use the real class implementation; but you need to control how some object reacts to methods calls to it.
Your method to test looks like:
public SingleProduct adapt(SearchHit hit, boolean bidding) {
//I want to mock "source", I don't want to use "hit.getSource()"
Map<String, Object> source = hit.getSource();
Wrong: you want to make sure that hit.getSource() is used. Because your production code is using is; and you write your unit tests to text that code. So you want that your production code does its "normal" thing.
So, the very simply solution here is:
#Test
public void testAdapt() {
SearchHit mockedHit = mock(SearchHit.class);
Map<String, Object> resonseForGetSource = new HashMap<>();
resonseForGetSource.put("productgroupid", "12877");
...
doReturn(resonseForGetSource).when(mockedHit.getSource());
ProductAdapterService underTest = ...
underTest.adapt(mockedHit, true);
... probably some asserts
or something alike (don't nail me on the doReturn/when details here)
What you can see here: your production code needs that map to do its job; so you just make sure that such a map object shows up in your production code.
And in case it would be possible to use a real SearchHit object (that you could configure with such a map); then using that would even be better than mocking that object.
You absolutely try to minimize your usage of mocking. You only use it to gain control over objects that are used during a certain test.
And beyond that: you are not clear about the scope of your unit testing. In order to test that one method, you dont need no ghosts. The unit test code you are showing simply doesn't make sense in the context of the class you are showing us here! Thus: you better step back and carefully look into "which units do I have" and "how to unit test exactly that unit X". You don't text "X" by testing "Y"!
I have the following code, where each url in listOne is tested with the method testItem:
#Parameters(name="{0}")
public static Collection<Object[]> data() throws Exception {
final Set<String> listOne = getListOne();
final Collection<Object[]> data = new ArrayList<>();
for (final String url : listOne) {
data.add(new Object[] { url });
}
return data;
}
#Test
public void testItem() {
driverOne.makeDecision(urlToTest);
assertTrue(driverOne.success(urlToTest);
}
What if I now wanted to add a second list, listTwo, and run a test method defined as follows on JUST the items of listTwo (but not listOne?)
#Test
public void testItemAlternate() {
driverTwo.makeDecision(urlToTest);
assertTrue(driverTwo.success(urlToTest));
}
That is, I want driverOne to make the decision for all URLs in listOne, and I want driverTwo to make the decision for all URLs in listTwo. What is the best way to translate this to code? Thanks.
Cited from: https://github.com/junit-team/junit/wiki/Parameterized-tests
The custom runner Parameterized implements parameterized tests. When running a parameterized test class, instances are created for the cross-product of the test methods and the test data elements.
Thus, I assume No, that's not possible.
If you want to do such a thing I guess that you either
(1) will need to construct two test classes one for each test to be executed with the first collection and one for each test to be executed with the second collection or
(2) will need to use another mechanism besides the #Parameters annotation, maybe hand-crafted.
You could include some test set identifier in your test set data itself, and then use the org.junit.Assume class to help:
#Test
public void testItem() {
org.junit.Assume.assumeTrue(testSetId.equals("TEST_SET_1"));
driverOne.makeDecision(urlToTest);
assertTrue(driverOne.success(urlToTest);
}
#Test
public void testItemAlternate() {
org.junit.Assume.assumeTrue(testSetId.equals("TEST_SET_2"));
driverTwo.makeDecision(urlToTest);
assertTrue(driverTwo.success(urlToTest));
}
As a completely different answer, there exists junit-dataprovider
To me, if-else if is too long and hard to read.
Is there some table-driven approach I can use to map an input value to call a specific method for that value?
For example:
for(String id:ids){
Test test = new Test();
if(id == "101") {
test.method1();
}else if(id=="102") {
test.method2();
}else if(id=="103"){
test.method3();
...
...
static Map<String,Method> methods = new HashMap<String,Method>();
...
static {
methods.put("101",Test.class.getMethod("method1",null));
methods.put("102",Test.class.getMethod("method2",null));
...
}
...
for( String id : ids ) {
Test test = new Test();
Method m = methods.get(id);
if (m != null) {
m.invoke(test,null);
}
}
Of course, all this really does is trade the pain of the if/else chain for the pain of initializing the hash with all the methods.
You can use reflection to call method. Construct the method name from the string and Use Method Class from java.lang.reflect namespace to invoke the method and pass parameters.
Something like this
Method theMethod = yourClass.getClass().getMethod("methodName", null);
method.invoke(yourClass, );
As it is, your code is not quite correct, as it's using == to check equality of strings.
You should use the equals method:
for(String id: ids){
Test test = new Test();
if(id.equals("101")) {
test.method1();
}else if(id.equals("102")) {
test.method2();
}else if(id.equals("102")){
test.method3();
// etc.
}
}
Other answers have suggested a creating a method map Map<String,Method> and using reflection. This will work and may be perfectly reasonable for your use, but loses some compile-time type safety checks.
You can get those checks back by defining an interface and populating a map with instances of that interface:
interface TestMethod {
void execute(Test test);
}
private static HashMap<String, TestMethod> methodMap = new HashMap<String, TestMethod>();
static {
methodMap.put("101", new TestMethod(){
#Override
public void execute(Test test) {
test.method1();
}
});
methodMap.put("102", new TestMethod(){
#Override
public void execute(Test test) {
test.method2();
}
});
methodMap.put("103", new TestMethod() {
#Override
public void execute(Test test) {
test.method3();
}
});
// etc.
}
and then your loop can be coded as
for (String id: ids) {
Test test = new Test();
methodMap.get(id).execute(test);
}
As it is here, this unfortunately makes the code longer and leaves it at least as difficult to read. But if you're using Java 8, you can use lambda expressions to populate the map, which would make the static initializer block look more like:
static {
methodMap.put("101", t -> t.method1();
methodMap.put("102", t -> t.method2();
methodMap.put("103", t -> t.method3();
// etc.
}
and then it might actually be worth doing.
However, if this sort of conditional based on some kind of string "code" is common in your application, and especially if you have multiple conditionals depending on the same codes, you might want a much broader redesign to encapsulate the different actions done for the different codes in a class hierarchy and use polymorphism instead of either if-else (or switch) or a method map lookup. You're still likely to need some conditionals to build the instances of your classes, but the code might be greatly improved.
I have a class like the following;
class ClassA {
private static File myDir;
// myDir is created at some stage
private static String findFile(final String fileName) {
for (final String actualBackupFileName : myDir.list()) {
if (actualBackupFileName.startsWith(removeExtensionFrom(backupFile))) {
return actualBackupFileName;
}
}
}
}
So, basically, I want to test this class by mocking out the File class so that when list() is called on it it returns a list of strings I define in my test class.
I've got the following but its not working at the minute, there's probably something obvious I'm doing wrong - I'm new to JMockit - any help is much appreciated!
#Mocked("list") File myDir;
#Test
public void testClassA() {
final String[] files = {"file1-bla.txt"};
new NonStrictExpectations() {{
new File(anyString).list();
returns(files);
}};
String returnedFileName = Deencapsulation.invoke(ClassA.class, "findFile","file1.txt");
// assert returnedFileName is equal to "file1-bla.txt"
}
When running the above test I get a NullPointerException for the myDir field in ClassA - so it looks like its not getting mocked properly?
You can use the setField method from the Deencapsulation class. Note example below:
Deencapsulation.setField(ClassA, "File", your_desired_value);
JMockit (or any other mocking tool) does not mock fields or variables, it mocks types (classes, interfaces, etc.) Where the instances of those types get stored inside the code under test is not relevant.
Example test for ClassA:
#Test
public void testClassA(#Mocked File myDir)
{
new Expectations() {{ myDir.list(); result = "file1-bla.txt"; }};
String returnedFileName = new ClassA().publicMethodThatCallsFindFile("file1.txt");
assertEquals("file1-bla.txt", returnedFileName);
}
The above should work. Note that testing private methods directly (or accessing private fields) is considered bad practice, so I avoided it here. Also, it's best to avoid mocking the File class. Instead, test only your public methods, and use actual files instead of mocking the filesystem.
try out this:
new Expectations {{
invoke(File.class, "list", null, null);
returns(files);
}}