2

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

在下面的示例中如何测试 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中有没有为此目的的解决方案?

4

3 回答 3

7

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

为什么我用看起来像语义的东西来打扰你是因为理解测试的意图使得编写测试和构建代码变得如此容易。那里的工作代码量大于那里的可测试代码量。

那么,如何将这段代码更改为可单元测试(我将跳过“为什么要麻烦这些东西”)......

C# 中的单元测试测试单一类型(您的类)......重要的是(根据定义)没有别的。所以你的代码需要反映这一点。您要测试的是,当我调用 ABC 时,它会执行此操作。这些东西包括启动一个线程。因此,您要测试该方法以启动一个调用的线程。这里的基础是您的应用程序需要一个线程,这就是您的断言。

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

顺便说一句,Resharper 为操作系统类型(例如线程)创建代理。在他们的帮助中查看委派成员的资料。如果您不使用 Resharper,您应该使用。

是的,我知道这不是您想要的答案,但请相信我,这是您需要的答案。

我发现考虑以下类别的测试很有用:

  • 单元测试
  • 集成测试(或者可能是子系统测试)
  • 功能测试(例如 UI/应用程序/UAT 测试)
于 2012-10-03T10:36:09.983 回答
3

jMock 团队在“jMock Cookbook:测试多线程代码”中描述了一种出色的 Java 方法。在与测试相同的线程上执行“线程”功能的概念也可以应用于 C# 代码(请参阅此博客文章)。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);
    }
}

此博客文章中详细描述了 C# 解决方案。

于 2014-01-16T16:27:04.817 回答
2

如果有另一个线程,您必须等到该方法被调用。您必须决定在考虑未调用该方法之前应该等待多长时间,没有工具可以为您做到这一点。

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

于 2012-10-03T11:18:50.387 回答