如何使用模拟对象编写单元测试?

时间:2010-12-09 08:11:05

标签: java unit-testing mocking

我读的模拟示例越多,我就越感到困惑......

我有classA方法eat()调用FatDude类eatThemAll()

public class classA {

   FatDude dude = new FatDude();

   public String eat() {
        String result = dude.eatThemAll();
   }
}

public class FatDude {
   public String eatThemAll() {
       return "never full";
   }
}

现在我想通过模拟FatDude类来测试classA eat()方法。

public class MockFatDude extends FatDude {
   //override
   public String eatThemAll() {
      return "very full";
   }
}
-------------  test --------------
public class DoTest {

    public void runTest() {
         classA cl = new ClassA();
         String out = cl.eat();
         assertEqual(out, "very full");
    }
}

这个DoTest runTest()当然不会使用MockFatDude类。我可以想到的一种方法是更改​​代码以将FatDude传递给ClassA的eat()方法,如:

public class classA {

       public String eat(FatDude dude) {
            String result = dude.eatThemAll();
       }
   }

然后将我的测试方法更改为:

public class DoTest {

        public void runTest() {
             classA cl = new ClassA();
             String out = cl.eat(new MockFatDude());
             assertEqual(out, "very full");
        }
    }

但正如您所看到的,我必须更改源代码以满足我的需求。 这是正确的方法吗?如果我不允许更改源代码怎么办? 我知道如果我应用TDD概念,改变源代码是可以的,但我想听听 一些意见或建议,如果我上面所示的是正确的方法。

2 个答案:

答案 0 :(得分:4)

Mocking和Dependency Inversion Principle(DIP)齐头并进,在大多数语言中,Mocks通过接口解耦类,效果最佳。

在您的实例中,这将无需您更改代码即可运行:(编辑:我的意思是,将来,如果您以这种方式设计应用程序,则无需更改代码以模拟依赖项:))

  • 摘要界面IDude
  • 具体类FatDude(和MockFatDude)应该实现IDude接口
  • 提供一种机制,让IDude实例“设置”或注入classA - 依赖注入(构造函数或get / sets);或服务定位器模式最有效(替换具体的类别)

另请注意,许多模拟框架实际上允许您“动态”构建Mock具体类(参见MoQ等人),因此您可以直接在单元测试中创建MockFatDude的功能。

答案 1 :(得分:2)

是。由于你的单元测试,你直接偶然发现了一些好的设计。如果你仔细观察,你会发现你删除了classA和FatDude之间的耦合。现在,FatDude可以成为在需要时传递的行为的接口。 ClassA不需要知道它正在使用什么样的FatDude,或者如何构建FatDude(使用芝士汉堡?)。

您的解决方案正是我所做的。更改代码以适应TDD没有任何问题,只要您了解进行此类更改的原因和缺点/缺点。