0

如何模拟 Phone 对象的对象。

代码如下,

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());
  }
}

谢谢

4

4 回答 4

2

你没有。模拟的规则之一是:永远不要模拟实体或值对象。如果你需要打破这个规则,那就意味着你可能存在设计缺陷。

如果需要模拟 a new,则需要将工厂传递给对象,然后模拟工厂。一个非常常见的例子是当您需要模拟 Date 对象时,这在另一个问题中得到了很好的解释:如何模拟 Date 类的默认构造函数(检查第一个答案)。

作为旁注,调用一个name看起来不正确的 Phone ...mmm 实例。

于 2012-09-05T09:50:55.903 回答
2

首先我要做一些假设。 user.getUsername()&user.getPass()没有副作用。对System.out.println你来说并不重要。

这样你的课就变成了:

public class Fortest {
    Phone name = new Phone();

    public String handleUser(User user) {
        String ph = name.getA();

        if(ph.equalsIgnoreCase("test")){
            return "done";
        }

        return  "failed";

    } 
}

所以你的测试有两个条件。要么phone.getA()是“测试”,你返回“完成”,要么不是,你返回“失败”。

那么如何设置set“ getA”。有一件事是肯定的,我们需要能够从测试中设置“名称”。为此,我们需要“注入”它(我们可以通过多种其他方式来实现,但我喜欢注入)。我会使用 Guice,很多人会使用 Spring。有些人会使用其他注入框架之一。但在测试中,我们大多数人都会使用手动注入。

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);
   }
}

现在测试相当简单:

public void whenTestModeIsEnabledThenReturnDone() {
     setPhoneIntoTestMode();
     String actual = subject.handleUser(null);
     assertEquals(actual, "done");
}

public void whenTestModeIsDisabledThenReturnFailed() {
     setPhoneIntoLiveMode();
     String actual = subject.handleUser(null);
     assertEquals(actual, "failed");
}

setPhoneIntoTestMode/的实现setPhoneIntoLiveMode将取决于复杂Phone程度。如果它比我们以某种方式(模拟、存根等)“伪造”它更复杂。这可能是您编写的一大段代码,也可能是使用像 Mocketo 这样的工具。

如果 Phone 对象很简单,并且有或可以有一个 " setA" 方法,那么就使用它。

我相信以后你会需要userdao. 到时候也会做同样的事情。注入并模拟/设置对象。

于 2012-09-05T11:35:00.880 回答
0

使用EasyMock可以很容易地模拟类。它在内部使用 cglib 来执行类模拟。EasyMock 可以同时模拟接口类(类模拟)。请参阅文档

所以,要让你的手机模拟,只需调用 createMock(Phone.class):

Phone phoneMock = createMock(Phone.class);

正如 Augusto 所说,尽管使用类模拟并不是很好的设计。更好的方法是针对接口进行编程并使用依赖注入框架。

于 2012-09-05T10:33:30.783 回答
0

因此,您需要执行以下选项之一来将模拟注入字段name并且userdao(我将假设您可以使用new Phone字段实例而不是在方法中创建的实例。

  1. 不要直接在代码中调用构造函数,而是通过 setter 使用字段注入。这将允许您的测试提供两个类的模拟实例。如果您必须在方法中创建一个新实例,请考虑使用可以模拟的工厂。

  2. 为这两个字段提供默认范围设置方法。这些方法仅用于测试目的。

  3. 使用 Refection 将字段设置为模拟实例。一个简单的方法是使用 Spring 的 ReflectionTestUtils。

一旦其中一个到位,您就可以提供模拟实例(可能使用 Mockito)来驱动您希望测试的行为。如果可行,我建议选项 1 是最好的,然后是选项 3。但是,选项 3 的缺点是测试依赖于私有字段的名称。

然后...

Phone phone = Mockito.mock(Phone.class);
Mockito.when(phone.getA()).thenReturn("blah");
objectUnderTest.setPhone(phone);

objectUnderTest.handleUser(...);
于 2012-09-05T11:29:57.637 回答