适当的单元测试:测试没有[可访问]状态或返回值的方法

时间:2009-01-17 02:27:15

标签: unit-testing

我对单元测试有些新意。关于正确测试的一件事(现在)让我感到困惑。

例如,如果主方法没有状态且只有控制台输出,那么如何测试它?像这样,myServer方法&国家是私人的?

 public static void main(String[] args)
 {
     Server myServer = new Server()
     if(myServer.start())
          System.out.println("started");
     else
          System.out.println("failed"); 
 }

我不想更改我的代码并公开我的服务器方法&声明他们是公开的。

注意,我不是在问如何测试myServer.start(),我问的是如何测试main()本身。

请告诉我。

谢谢你们, JBU

6 个答案:

答案 0 :(得分:8)

主要方法应该非常薄,只是让球滚动。因此通常没有经过测试 例如a .net Winforms app主要方法可能包含

static void Main()
{
  Application.EnableVisualStyles();
  Application.SetCompatibleTextRenderingDefault(false);
  Application.Run(new MainWindow());
}

如果你的主要方法做了很多事情,可以考虑提取一个方法(如果需要的话,提到一个新的方法)。例如如下所示...(没有JavaIDE方便.. foll .net代码应该很容易翻译)

static void Main()
{
  new MainApp().Run();
}

现在如果Run可以测试..主要也被覆盖。让我们看看MainApp。将启动服务器记录结果的2个任务重构为2个方法。

public class MainApp
{
  private Server m_Server;
  public MainApp():this(new Server())
  {}
  public MainApp(Server s)
  {  m_Server = s;      }

  public void Run()
  {  Log(LaunchServer());  }

  private string LaunchServer()
  {  return (m_Server.start() ? "started" : "failed");        }

  protected virtual void Log(string sMessage)
  {  System.Console.WriteLine(sMessage);        }
}

现在让我们看看测试代码......我将使用“子类和覆盖”技巧来缓存结果,如下所示。 (你也可以根据口味使用Mocks)

public class FakeApp : MainApp
{
  private string sLastLoggedMessage;

  public FakeApp(Server s) : base(s) { }

  public string getLastLoggedMessage()
  {  return sLastLoggedMessage;        }

  protected override void Log(string sMessage)
  {  sLastLoggedMessage = sMessage;        }
}

测试现在是微不足道的

[TestFixture]
public class TestMainApp
{
  [Test]
  public void TestRun()
  {
    Server s = new Server();
    FakeApp f = new FakeApp(s);

    f.Run();

    Assert.AreEqual("started", f.getLastLoggedMessage());
  }
}

如果您无法根据需要强制Server.start()成功或失败。您可以创建一个FakeServer ..子类并覆盖start()以返回固定值。

如果方法有效,可以对其进行测试。如果没有,它应该不复存在。重构它。 HTH

答案 1 :(得分:4)

您(通常)不会对main()方法进行单元测试。在您的情况下,您可以对Server类(可能还有其他类)进行单元测试,即测试它的公共接口。

答案 2 :(得分:1)

由于您的方法只有一个方向的信息流 - 对协作者对象(在此示例中为:Server对象),并且不以其他方式返回值或抛出异常,您可以提供模拟协作者对象(模拟服务器)在你的例子中),它将配置对从main()方法接收的方法调用及其参数的期望。

您可以使用其中一个模拟对象框架,例如: EasyMock的。

在单元测试期间,当main()方法对其进行的调用与配置的期望不匹配时,模拟对象将抛出一个正确的异常。

当然,为了提供一个模拟对象,你必须在对象实例化方面稍微修改一下方法 - 使“Server”成为一个接口,而不是一个具体的类,并使用一个框架(如Spring)而不是手动实例化您的Server对象,例如而不是:

Server myServer = new Server();

你有这个:

Server myServer = applicationContext.getBean("server");

您将为生产应用程序和单元测试创​​建单独的Spring应用程序上下文配置,不同之处在于将使用XML文件配置生产的应用程序上下文,而对于单元测试将以编程方式创建并使用mock填充测试类的setUp()方法中的对象。

答案 3 :(得分:0)

如果您希望将此案例以及其他许多案件清除,我可以建议Pragmatic Unit Testing系列吗?

答案 4 :(得分:0)

一般来说,main()不是“单位”,并且难以设计单元测试。按照惯例,main()应该返回一个int来表示错误状态。也许这就是你所需要的一切?

答案 5 :(得分:0)

将代码包装在非主方法中并测试; - )

严肃地说,其他人都说了什么

但是如果你必须...进行单元测试运行DOS shell中的控制台应用程序(请参阅Process对象),并将STDOUT重定向到文件,然后读取输出文件的内容以查看它是否显示为“started” “或”失败“,无论你期望什么