如何单元测试使用线程的方法?

时间:2012-10-03 10:25:03

标签: c# java unit-testing junit nunit

如何为使用线程的方法编写单元测试。

在下面的例子中如何测试someMethod方法?

    public class SomeClass {

    private final SomeOtherClass someOtherClassInstance = IoC.getInstance(SomeOtherClass.class);

    private final ExecutorService executorService = Executors.newCachedThreadPool();

    public void someMethod() {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                someOtherClassInstance.someOtherMethod();
            }
        });
    }
}

为此目的,java和.net中是否有任何解决方案?

3 个答案:

答案 0 :(得分:4)

你的问题是'什么是单元测试'(UT)。 UT只是自动化测试的一种形式。在这种情况下,您的问题意味着代码调用操作系统来启动一个线程,因此根据定义,测试它的测试不是单元测试,但可能是集成测试。

为什么我用似乎语义的东西打扰你,理解测试的意图使编写测试和构造代码变得如此容易。那里的工作代码量大于可测试代码的数量。

那么,如何将此代码更改为可单元测试(我将跳过'为什么要打扰的东西')......

C#中的单元测试测试单个类型(您的类)......重要的是(根据定义)没有别的。所以你的代码需要反映这一点。你要测试的是,当我打电话给ABC时,它会做这件事。这些东西包括启动一个线程。所以你想测试一下这个方法来启动一个被调用的线程。这里的基本原则是你的应用程序需要一个线程,这就是你的断言。

那怎么样?为线程创建创建代理,也许是工厂。然后你可以在单元测试中断言它被调用以及它是如何被处理的。听起来很难,但是当你养成习惯时,这很容易。

BTW,Resharper为OS类型(例如线程)创建代理。在他们的帮助中查看委派成员的内容。如果你不使用Resharper,你应该是。

是的,我知道这不是你跳过的答案,但请相信我这是你需要的答案。

我觉得考虑类别中的测试很有用:

  • 单元测试
  • 集成测试(或可能是子系统测试)
  • 功能测试(例如UI /应用程序/ UAT测试)

答案 1 :(得分:2)

如果有另一个线程,则必须等到调用该方法。您必须决定在考虑未调用方法之前应等待多长时间,没有可以为您执行此操作的工具。

当然,你可以模拟ExecutorService在当前线程中运行并避免问题。

答案 2 :(得分:2)

jMock团队在'jMock Cookbook: Test Multithreaded Code'中描述了一种杰出的Java方法。在与测试相同的线程上执行“线程”功能的概念也可以应用于C#代码(请参阅this blog post)。 C#测试看起来像这样:

[Test]
public void TestMultiThreadingCodeSynchronously()
{
    // Arrange
    SomeOtherClass someOtherClassMock = MockRepository.GenerateMock<SomeOtherClass>();
    DeterministicTaskScheduler taskScheduler = new DeterministicTaskScheduler();
    SomeClass systemUnderTest = new SomeClass(taskScheduler, someOtherClassMock);

    // Act
    systemUnderTest.SomeMethod();

    // Now execute the new task on
    // the current thread.
    taskScheduler.RunTasksUntilIdle();

    // Assert
    someOtherClassMock.AssertWasCalled(x=>x.SomeOtherMethod());
}

您的“受测试系统”将如下所示:

public class SomeClass
{
    private readonly TaskScheduler taskScheduler;
    private readonly SomeOtherClass instance;

    public SomeClass(
        TaskScheduler taskScheduler, 
        SomeOtherClass instance)
    {
        this.taskScheduler = taskScheduler;
        this.instance = instance;
    }

    public void SomeMethod()
    {
        Task.Factory.StartNew(
          instance.SomeOtherMethod, 
          new CancellationToken(), 
          TaskCreationOptions.None, 
          taskScheduler);
    }
}

this blog post中详细描述了C#解决方案。