简体   繁体   中英

On which level should I test my logic

I have a TextGenerator class, which generates random text using MarkovChain class. Logic to create next word from MarkovChain lives in ChainNavigator class:

public class TextGenerator
{
    public List<string> MakeText(int requiredTextLength, string sourceText)
    {
        var chain = new MarkovChain(sourceText);
        var chainNavigator = new ChainNavigator(chain);            
        var nextWord = chainNavigator.GetNextWord(/*params here*/);
    }
}

internal class ChainNavigator
{
    internal string GetNextWord(/*params here*/) { }
}

MarkovChain is generated from source text. Source text's last word would not have any 'next state', as it does not have any words after it. When generating long text, ChainNavigator would reach last word and would not know what to return.

I want to test that TextGenerator starts new sentence, when it reaches last word, and this test can be written in 2 places.

On one hand, it makes sense to test this in TextGenerator as it's my external interface:

[TestClass]
public class TextGeneratorTest
{
    [TestMethod]
    public void ShouldAppendADot_WhenEndOfChainReached()
    {
        var generator = new TextGenerator();
        var sourceText = "free men can remain free or sell their freedom";
        var firstWord = "their";
        var requiredTextLength = 2;

        var text = generator.MakeText(requiredTextLength, sourceText, firstWord);

        Assert.AreEqual("freedom.", text[1]);
    }
}

On the other hand, the actual tested logic belongs to ChainNavigator class and it would make sense to test it here:

[TestMethod]
public void AppendADot_WhenEndOfChainReached()
{
    var sourceText = "free men can remain free or sell their freedom";
    var chain = new Chain(sourceText);
    var navigator = new ChainNavigator(chain);

    var nextWord = navigator.GetNextWord("their", 1);

    Assert.AreEqual("freedom.", nextWord);
}

Doing it in both places looks like a duplication. Where is it better to do it?

Your confusion is actually a common one. The source of this is the term "unit" in "unit test". A lot of people will tell you, that the "unit" is something like a single class or even just a single method. People have been telling this for decades now, but it is mostly wrong. The misconception stems from the fact that you rarely see real applications being tested in books, articles, and blogs. Since it hard to show the general principles of unit testing with a full blown application, examples will usually be limited to a very small number of classes. Even Kent Beck is using the famous Money class example, which is mostly limited to a single class, in his book.

Test at the highest level that is not bound to external details. In your limited example TestGenerator might just be the perfect level. It lets you test your business logic fully, without the test breaking when you change the inner structure. Should you ever decide to split the ChainNavigator into multiple classes or join the ChainNavigator with MarkovChain , your test would not need to know and would not break.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM