如何TDD需要来自远程方输入的两步流程?

时间:2013-11-18 23:03:21

标签: tdd

我有一个由两个步骤组成的过程:

  • (步骤1)首先从远程方请求令牌;

  • (步骤2)第二,它使用令牌触发远程作业。

TDDing(1)非常简单,但是如何进行TDD(step2)?如果我将测试编写为不同的函数,我不知道在两个测试之间存储令牌的位置(在每次测试之前清理DB)。如果我使用灯具,(步骤2)将始终失败,因为远程作业将不接受我的令牌。

到目前为止我的解决方案是:

  • test1()(当然)测试(步骤1);

  • test2(),其中包含test1()的全部代码以及测试代码(步骤2)。

虽然解决方案的工作我不喜欢它,所以我正在寻找更好的东西。

2 个答案:

答案 0 :(得分:1)

你说你有一个包含两个步骤的过程。正如您已经确定了这些不连续的步骤,您应该将它们封装到单独的方法中,例如

(注意:以下代码非常简单。如果您在问题中包含代码,这将有所帮助)

而不是:

void TriggerRemoteJob()
{
  // Step 1 - Code to request a token from a remote party.

  // Step 2 - Code to use the token to trigger a remote job.
}

你可以:

Token GetToken(string remotePartyId)
{
 return RemoteParty.GetToken(remotePartyId); 
}

int TriggerRemoteJob(Token token)
{
 return RemoteJobManager.TriggerJob(token);
}

“RemoteParty”和“RemoteJobManager”都是可以使用依赖注入注入到类中的依赖项。

采用这种方法,您可以使用TDD开发“GetToken”和“TriggerJob”。使用TDD时,您应该模拟外部依赖项,并仅测试您正在开发的方法。因此,在测试“TriggerRemoteJob”时,您将 模拟 令牌。

在评论中你说:

“我想尽可能真实地测试这个过程。”

我建议您使用BDD方法进行开发(在StackOverflow上搜索BDD,这个主题有很多很棒的答案)。这种方法迫使您开发“从内到外”,在此处您将恢复使用TDD作为BDD流程的一部分。这种方法将运行解决方案中的所有层,以便您“尽可能真实地”测试流程,同时编写使用模拟的测试(通过TDD)。

答案 1 :(得分:0)

因为你要去to TDD a 2-steps process which requires input from a remote party,所以你一定要嘲笑remote party

我建议您使用MOQNSubstitute作为模拟框架。

我看到没有必要编写两个测试,因为两个调用非常依赖。

我宁愿只编写一个测试来测试你的两步流程

[TestClass]
public class ClientTest
{
    /// <summary>
    /// This test can test only the fact of invocation only.
    /// </summary>
    [TestMethod]
    public void DoSmth_RequestsTokenFromRemoteParty()
    {
        // act
        target.DoSmth();
        // assert
        remotePartyMock.Verify(it => it.RequestTtoken());
    }

    /// <summary>
    /// It is enough to have this test only.
    /// It contains token setup and following verification of triggering job with correct tocken.
    /// </summary>
    [TestMethod]
    public void DoSmth_RequestsTokenAndTriggersRemoteJob()
    {
        // arrange
        var expectedTocken = Guid.NewGuid();
        remotePartyMock.Setup(it => it.RequestTtoken()).Returns(expectedTocken);
        // act
        target.DoSmth();
        // assert
        remotePartyMock.Verify(it => it.TriggerRemoteJob(expectedTocken));
    }

    private Mock<IRemoteParty> remotePartyMock;
    private Client target;

    public void Init()
    {
        remotePartyMock = new Mock<IRemoteParty>();
        target = new Client(remotePartyMock.Object);
    }
}

#region Client & IRemoteParty

public class Client
{
    private readonly IRemoteParty remoteParty;

    /// <summary>
    /// There is a constructor injection.
    /// </summary>
    public Client(IRemoteParty remoteParty)
    {
        this.remoteParty = remoteParty;
    }

    public void DoSmth()
    {
        var tocken = remoteParty.RequestTtoken();
        remoteParty.TriggerRemoteJob(tocken);
    }
}

public interface IRemoteParty
{
    Guid RequestTtoken();

    void TriggerRemoteJob(Guid token);
}

#endregion