问题
How to mock the object for the Phone object.
code bellow,
public class Fortest {
UserDao userdao = new UserDao();
Phone name = new Phone();
public String handleUser(User user) {
String returncode="failed";
//User usr = new User("bob");
String username=user.getUsername();
String pass=user.getPass();
System.out.println("username and password : "+username+" : "+pass);
Phone name = new Phone();
String ph = name.getA();
System.out.println("ph "+ph);
if(ph.equalsIgnoreCase("test")){
System.out.println("A "+ph);
returncode="done";
}
System.out.println("returning "+returncode);
return returncode;
//System.out.println("name "+name.toString());
//System.out.println(name.getA());
}
}
Thanks
回答1:
First I'm going to make some assumptions.
user.getUsername()
& user.getPass()
have no side affects.
The System.out.println
are not important to you.
Thus done your class becomes:
public class Fortest {
Phone name = new Phone();
public String handleUser(User user) {
String ph = name.getA();
if(ph.equalsIgnoreCase("test")){
return "done";
}
return "failed";
}
}
So your test has two conditions. Either phone.getA()
is "test" and you return "done" or it is not and you return "failed".
So how to set set "getA
". One thing is for sure, we will need to be able set "name" from the test. For that we need to "inject" it (we can do it a number of other ways, but I loves injection). I'd use Guice, many would use Spring. Some would use one of the other injection frameworks. But in the tests most of us would use manual injection.
public class Fortest {
Phone name;
Fortest(Phone name) {
this.name = name;
}
public String handleUser(User user) {
String ph = name.getA();
if(ph.equalsIgnoreCase("test")){
return "done";
}
return "failed";
}
}
public class TestFortest {
@Before
public void before() {
name = ; //...
subject = new Fortest(name);
}
}
Now the tests are fairly simply:
public void whenTestModeIsEnabledThenReturnDone() {
setPhoneIntoTestMode();
String actual = subject.handleUser(null);
assertEquals(actual, "done");
}
public void whenTestModeIsDisabledThenReturnFailed() {
setPhoneIntoLiveMode();
String actual = subject.handleUser(null);
assertEquals(actual, "failed");
}
The implementation of setPhoneIntoTestMode
/setPhoneIntoLiveMode
will depend on how complex Phone
is. If it is complex than we would look at "facking" it in some way (mocks, stubs, etc). This could be a chunk of code you write, it could be using a tool like Mocketo.
If the Phone object is simple, and has or can have a "setA
" method, then just use that.
I'm sure later you will need userdao
. The same thing will be done at that point. Inject and mock/setup the object.
回答2:
You don't. One of the rules of mocking is: you never mock entities or value objects. If you need to break this rule, it means that you probably have a design flaw.
If you need to mock a new
, you'll need to pass a factory to the object, and then you mock the factory. A very common example of this is when you need to mock Date objects, which is very well explained in this other question: How to mock the default constructor of the Date class (check the first answer).
As a side note, calling an instance of Phone name
...mmm that doesn't look right.
回答3:
Class mocking is very easy using EasyMock. It makes use of cglib internally to perform class mocking. EasyMock can both mock interfaces and classes (class mocking). See documentation.
So, to get your Phone mock, just call createMock(Phone.class):
Phone phoneMock = createMock(Phone.class);
As Augusto stated, it is not really good design to make use of class mocking though. A better way would be to program towards interfaces and use a dependency injection framework.
回答4:
So you need to do one of the following options to inject mocks into the fields name
and userdao
(I am going to assume that you can use the new Phone
field instance instead of the one created in the method.
Do not call constructors in your code directly but instead use field injection via setters. This will allow your test to provided mocked instances of the two classes. If you must create a new instance in the method then consider using a factory which can be mocked.
Provide default scope setter methods for the two fields. These methods would be in place for test purposes only.
Use Refection to set the fields to mocked instances. An easy way to do this is with Spring's ReflectionTestUtils.
Once one of these is in place you can provide mocked instances (maybe using Mockito) to drive the behavior you wish to test. I would suggest that option 1 is the best if fesible, then option 3. However, the disadvantage to options 3 is that the test is dependant of the names of private fields.
Then...
Phone phone = Mockito.mock(Phone.class);
Mockito.when(phone.getA()).thenReturn("blah");
objectUnderTest.setPhone(phone);
objectUnderTest.handleUser(...);
来源:https://stackoverflow.com/questions/12278593/junit-test-class-for-the-following-code