Thought Cloud TDD #10: Think back

May 24th, 2011

We’re just about done with the TDD push on Thought Cloud. This is the last test before creating some UI.

In the last test, Mike published a thought to Russell. Let’s see if Russell can send one back to Mike.

[TestMethod]
public void RussellCanShareAThoughtWithMike()
{
    Cloud cloud = MikeSharesCloudWithRussell();
    Thought thought = cloud.NewThought();
    cloud.CentralThought = thought;
    thought.Text = "Lunch suggestions";

    Synchronize();

    Cloud sharedCloud = _russellsIdentity.SharedClouds.Single();
    Thought newThought = sharedCloud.NewThought();
    newThought.Text = "Mi Pueblo";
    Thought centralThought = sharedCloud.CentralThought;
    centralThought.LinkTo(newThought);

    Synchronize();

    IEnumerable<Thought> suggestions = thought.Neighbors.Where(n => n != thought);
    Assert.AreEqual(1, suggestions.Count());
    string suggestionText = suggestions.Single().Text;
    Assert.AreEqual("Mi Pueblo", suggestionText);
}

In this test, Mike shares a cloud with the central thought “Lunch suggestions” with Russell. Russell responds by creating a linked thought with the text “Mi Pueblo”. Does Mike see the response?

No! The test fails. Can you see why?

The reason is that we have subscribed to all clouds shared with us. We haven’t subscribed to the clouds that we created ourselves.

_mikesCommunity = new Community(new MemoryStorageStrategy())
    .AddCommunicationStrategy(sharedCommunication)
    .Register<Model.CorrespondenceModel>()
    .Subscribe(() => _mikesIdentity)
    .Subscribe(() => _mikesIdentity.SharedClouds)
    .Subscribe(() => _mikesIdentity.Clouds)
    ;

With this new subscription, the test passes. Mike created the cloud, so now he subscribes to it. When Russell adds a thought to it, that thought is published to Mike’s cloud.

Thought Cloud TDD #9: Penny for your thoughts

May 23rd, 2011

The Thought Cloud application just needs a few more tests. Then we are on to building the UI.

In the last test, we made it possible to share clouds with other users. But what we really want to do is share thoughts.

[TestMethod]
public void MikeCanShareThoughtsWithRussell()
{
    Cloud cloud = MikeSharesCloudWithRussell();
    Thought thought = cloud.NewThought();
    cloud.CentralThought = thought;
    thought.Text = "Lunch suggestions";

    Synchronize();

    Thought sharedThought = _russellsIdentity.SharedClouds.Single().CentralThought;
    string sharedText = sharedThought.Text;
    Assert.AreEqual("Lunch suggestions", sharedText);
}

In this test, Mike is sharing the cloud with Russell, and then adding a thought. Russell should be able to see the thought and the text that Michael put into it. The test, of course, fails.

Subscribe to related facts

The test fails because Russell thinks that CentralThought is still null. The Share was published to him, but not the CentralThought. Let’s publish that now.

fact Cloud {
key:
    unique;
    Identity creator;

mutable:
    publish Thought centralThought;
}

This keyword publishes the CentralThought property to the Cloud. So to receive it, Russell must subscribe to the cloud.

_russellsCommunity = new Community(new MemoryStorageStrategy())
    .AddCommunicationStrategy(sharedCommunication)
    .Register<Model.CorrespondenceModel>()
    .Subscribe(() => _russellsIdentity)
    .Subscribe(() => _russellsIdentity.SharedClouds)
    ;

With this subscription, Russell receives all facts published to the clouds that have been shared with him. This should be enough to let him see CentralThought. But the test still fails.

Publish successors to shared facts

The test fails now because Russell thinks that the central thought’s text is still null. He can’t see that Mike has changed it. So we somehow need to publish the thought’s text to the Cloud.

Text is a mutable field of a Thought, so it can be published to a Thought. It cannot be published directly to a Cloud. But a Thought can be published to a Cloud.

fact Thought {
key:
    unique;
    publish Cloud cloud;

...
}

This change makes the unit test pass. Why? Because of rule #5.

Rule #5: All successors of published facts are automatically published.

The Text property of the central thought is a successor of the Thought. The Thought is published to the Cloud. Therefore, a subscriber to the Cloud will also see the Text.

Thought Cloud TDD #8: Hey, you, get onto my cloud

May 23rd, 2011

This Thought Cloud application is going to be a lot of fun. It’s about to get really exciting.

What good is a collaborative mind mapper if you can’t share thoughts with other people? It’s time to bring on the collaboration tests. The ModelTest creates two communities representing two user’s machines: Mike and Russell.

[TestInitialize]
public void Initialize()
{
    var sharedCommunication = new MemoryCommunicationStrategy();
    _mikesCommunity = new Community(new MemoryStorageStrategy())
        .AddCommunicationStrategy(sharedCommunication)
        .Register<Model.CorrespondenceModel>()
        .Subscribe(() => _mikesIdentity)
        ;
    _russellsCommunity = new Community(new MemoryStorageStrategy())
        .AddCommunicationStrategy(sharedCommunication)
        .Register<Model.CorrespondenceModel>()
        .Subscribe(() => _russellsIdentity)
        ;

    _mikesIdentity = _mikesCommunity.AddFact(new Identity("mike"));
    _russellsIdentity = _russellsCommunity.AddFact(new Identity("russell"));
}

The communities are joined by a shared communication strategy. That means that facts created in one community can flow into the other. In order to trigger this flow, we have to call Synchronize.

private void Synchronize()
{
    while (_mikesCommunity.Synchronize() || _russellsCommunity.Synchronize()) ;
}

The Synchronize method continues to push facts between the two communities until neither one has facts to share. We can use this to set up tests. The first test is that if Mike shares a cloud with Russell, Russell can see the cloud.

[TestMethod]
public void MikeCanShareCloudWithRussell()
{
    Cloud cloud = _mikesIdentity.NewCloud();
    Identity russell = _mikesCommunity.AddFact(new Identity("russell"));
    russell.NewShare(cloud);

    Synchronize();

    Assert.AreEqual(1, _russellsIdentity.SharedClouds.Count());
    Assert.AreEqual("mike", _russellsIdentity.SharedClouds.Single().Creator.AnonymousId);
}

Mike adds a fact to his community with the id of “russell”. This fact matches the one in Russell’s community, so it will be treated as the same fact when the two synchronize. He then shares his cloud with Russell. After they synchronize, Russell should see the shared cloud created by Mike.

A shared fact

To make this test pass, we need to define a fact that shares a cloud with an identity.

fact Share {
key:
    Identity recipient;
    Cloud cloud;
}

Then we need to query an identity for all of the shared clouds.

fact Identity {
...

query:

    Cloud* sharedClouds {
        Share s : s.recipient = this
        Cloud c : s.cloud = c
    }
}

That query combines two sets. First, the set of all shares to this recipient. Then, the set of all clouds thus shared. Finally, we need a method in our partial class to create a new share.

public partial class Identity
{
    ...

    public void NewShare(Cloud cloud)
    {
        Community.AddFact(new Share(this, cloud));
    }
}

With these changes, the test should pass. But it doesn’t. Why not?

Publish shared facts

If you look closely at the setup of each Community, you’ll see a subscription. Here it is again:

_russellsCommunity = new Community(new MemoryStorageStrategy())
    .AddCommunicationStrategy(sharedCommunication)
    .Register<Model.CorrespondenceModel>()
    .Subscribe(() => _russellsIdentity)
    ;

That line says that Russell’s community is only interested in facts that are published to Russell’s identity. So to get the Share over to the other community, we need to publish it to the Identity.

fact Share {
key:
    publish Identity recipient;
    Cloud cloud;
}

And now the test passes.

Rule #4: A community only receives published facts to which it subscribes.

Wait a minute! The test asserts that Russell can see that the cloud was created by Mike. How can Russell see Mike’s identity if it wasn’t published?

Before a fact is sent, all of its predecessors must first be sent. That means that in order to receive a Share, Russell must first receive the Cloud that it references. Before he can receive the Cloud, he must receive the Identity of the creator.

Rule #4.1: … and all of their predecessors.

A community doesn’t receive all facts. It only receives what it subscribes to. You need to manage the publications and subscriptions. Once you do, two Communities can collaborate through a shared synchronization strategy.

Thought Cloud TDD #7: Devoid of thought

May 20th, 2011

I’m writing Thought Cloud for a demo in a couple of weeks. Help me out.

During the last refactoring, we introduced a bug. There just isn’t a test to show it yet. The bug is related to rule number two:

Rule #2: Create one fact for each user action.

In the last refactoring, a single user action created three facts: a cloud, and thought, and a central thought assignment. (You can’t always see it, but an assignment to a mutable field is a new fact.) The problem with creating three facts for a single user action is that we cannot guarantee that all three facts are atomic. There may be situations – particularly when collaborating with another user – where we see only a subset of the facts.

Honor the default state

After each fact that we create, we must assume that the system is in a valid state. In this scenario, we have two intermediate states which must be considered valid:

  • The cloud has been created, but it has no thoughts.
  • The cloud has a thought, but no central thought.

These are the states that arise after the creation of the first and second facts. We must honor the default state of a cloud. Let’s drop the initialization steps from the unit test and see what happens:

[TestInitialize]
public void Initialize()
{
    _community = new Community(new MemoryStorageStrategy())
        .Register<Model.CorrespondenceModel>();

    _identity = _community.AddFact(new Identity("mike"));
    Cloud cloud = _community.AddFact(new Cloud(_identity));
    //Thought thought = _community.AddFact(new Thought(cloud));
    //cloud.CentralThought = thought;
    _cloudViewModel = new CloudViewModel(cloud);
}

Now all three of the tests in this suite fail. They all throw null reference exceptions. The first null reference is in the Thoughts property of the CloudViewModel. The cloud has no central thought. In this situation, we want to simulate a thought to make the tests pass.

public IEnumerable<ThoughtViewModel> Thoughts
{
    get
    {
        Thought centralThought = _cloud.CentralThought;
        if (centralThought == null)
        {
            return Enumerable.Repeat(new ThoughtViewModelSimulated(_cloud), 1)
                .OfType<ThoughtViewModel>();
        }
        else
        {
            return Enumerable.Repeat(new ThoughtViewModelActual(centralThought), 1).Union(
                from n in centralThought.Neighbors
                where n != centralThought
                select new ThoughtViewModelActual(n))
                .OfType<ThoughtViewModel>();
        }
    }
}

If the central thought has not been set, then we return a simulated thought view model. If it has, then we return actual thought view models. Those calls to OfType are there because an IEnumerable of a derived type can’t be converted to an IEnumerable of the base type. Covariance will solve this problem, once it is available in Silverlight.

When the user sets the thought text, the simulated thought view model needs a place to store it. So at that point, it creates a thought.

public class ThoughtViewModelSimulated : ThoughtViewModel
{
    private Cloud _cloud;

    public ThoughtViewModelSimulated(Cloud cloud)
    {
        _cloud = cloud;
    }

    public string Text
    {
        get { return "My thought"; }
        set
        {
            Thought thought = _cloud.NewThought();
            _cloud.CentralThought = thought;
            thought.Text = value;
        }
    }
}

The last null reference exception occurs in the NewThought command of CloudViewModel. It tries to link the new thought to the central thought, but the central thought is null. We must honor this situation, and create a new central thought when needed.

public ICommand NewThought
{
    get
    {
        return MakeCommand.Do(() =>
        {
            Thought thought = _cloud.NewThought();
            Thought centralThought = _cloud.CentralThought;
            if (centralThought == null)
            {
                centralThought = _cloud.NewThought();
                _cloud.CentralThought = centralThought;
            }
            centralThought.LinkTo(thought);
        });
    }
}

Now the CloudViewModel honors the default state of the Cloud fact. It is valid to have a Cloud with no central thought. Just as the ThoughtViewModel returns the simulated text “My thought” when the Text property has not yet been set, the CloudViewModel returns a simulated thought when no central thought has been created. When the user makes a change, the actual Thought is created. But not before.

Thought Cloud TDD #6: Get your thoughts in order

May 20th, 2011

I found it backwards that you have to create a thought before you create a cloud.

public ICommand AddCloud
{
    get
    {
        return MakeCommand
            .Do(delegate
            {
                Thought thought = _identity.NewThought();
                _identity.NewCloud(thought);
            });
    }
}

A thought is part of a cloud, so the cloud should be a predecessor. But since we made a thought part of the cloud’s key, the thought is a predecessor.

Rule #3: Facts that are part of the key (a.k.a predecessors) must be created first.

It makes more sense if we create a cloud first, and then add thoughts to it. A cloud should be a predecessor of a thought. Let’s make that change.

fact Thought {
key:
    unique;
    Cloud cloud;

...
}

Now we need to create a thought in a Cloud. Let’s move the NewThought method from Identity to Cloud where it belongs.

public partial class Cloud
{
    public Thought NewThought()
    {
        return Community.AddFact(new Thought(this));
    }
}

Now we can create the cloud first.  But hold on! The cloud needs a central thought. The central thought is in the cloud. That’s a circular reference. The predecessor rule makes it impossible to create circular references out of keys. This is an important feature of Correspondence.

But a circular reference can be constructed if one of the fields is mutable. So let’s change the central thought.

fact Cloud {
key:
    unique;
    Identity creator;

mutable:
    Thought centralThought;
}

Now that the central thought is mutable, a cloud can be constructed with no central thought. Then the thought is created, and finally it is assigned.

public ICommand AddCloud
{
    get
    {
        return MakeCommand
            .Do(delegate
            {
                Cloud cloud = _identity.NewCloud();
                Thought thought = cloud.NewThought();
                cloud.CentralThought = thought;
            });
    }
}

This makes more sense. Now thoughts belong to a cloud, not the other way around. After following the ripples out to the edges of the pond, we can compile and pass the unit tests. But we’ve introduced a bug. We just need to write a test to prove it.

Thought Cloud TDD #5: Backward thinking

May 19th, 2011

The Thought Cloud demo is coming along nicely. Some really interesting patters are starting to emerge.

The last test produced some code that offends me. Let’s look at it again.

public IEnumerable<CloudViewModel> Clouds
{
    get
    {
        return
            from c in _identity.Clouds
            select new CloudViewModel(_identity, null);
    }
}

We create one cloud view model for each cloud that the user owns. But takes an identity and a central thought (which, to make matters worse, we set to null). It should take a cloud! Let’s change the constructor.

public CloudViewModel(Cloud cloud)
{
    _cloud = cloud;
    _identity = cloud.Creator;
}

Now we pass in a cloud, and the view model gets the identity from there. But where does it get the central thought? Let’s add that to the cloud as well.

fact Cloud {
key:
    unique;
    Identity creator;
    Thought centralThought;
}

So the central thought is an immutable feature of the cloud. As the compiler points out, we need to construct this thought before we construct the cloud.

public partial class Identity
{
    ...

    public Cloud NewCloud(Thought thought)
    {
        return Community.AddFact(new Cloud(this, thought));
    }
}

public class HomeViewModel
{
    ...

    public ICommand AddCloud
    {
        get
        {
            return MakeCommand
                .Do(() =>
                {
                    Thought thought = _identity.NewThought();
                    _identity.NewCloud(thought);
                });
        }
    }
}

Create a thought before you create a cloud? Seems backwards. Smells like another refactoring.

Thought Cloud TDD #4: Cloud factory

May 19th, 2011

I’m building Thought Cloud as a Correspondence demo for a talk in a few weeks. I hope you’ll be able to attend.

We’ve started right in the meat of the application, where the user is adding thoughts to a cloud. But before they get there, they should be able manage their individual clouds. Let’s start a new test suite for those features.

[TestClass]
public class HomeViewModelTest
{
    private Community _community;
    private Identity _identity;
    private HomeViewModel _viewModel;

    [TestInitialize]
    public void Initialize()
    {
        _community = new Community(new MemoryStorageStrategy())
            .Register<CorrespondenceModel>();

        _identity = _community.AddFact(new Identity("mike"));
        _viewModel = new HomeViewModel(_identity);
    }

    [TestMethod]
    public void CanAddACloud()
    {
        _viewModel.AddCloud.Execute(null);
        Assert.AreEqual(1, _viewModel.Clouds);
    }
}

The first test is that we can add a cloud. Let’s start by defining a new cloud fact.

fact Cloud {
key:
    unique;
    Identity creator;
}

A cloud is just a unique fact created by a user. To create a new cloud, let’s add a method to the Identity partial class.

public partial class Identity
{
    ...

    public Cloud NewCloud()
    {
        return Community.AddFact(new Cloud(this));
    }
}

The unit test executes a command to create a new cloud. So we use MakeCommand to implement the ICommand interface.

public class HomeViewModel
{
    private readonly Identity _identity;

    public HomeViewModel(Identity identity)
    {
        _identity = identity;
    }

    public ICommand AddCloud
    {
        get
        {
            return MakeCommand
                .Do(() => _identity.NewCloud());
        }
    }
}

Now the view model needs to return a list of clouds. We add a query to Identity to get all of the clouds.

fact Identity {
    ...

query:
    ...
    Cloud* clouds {
        Cloud c : c.creator = this
    }
}

Finally, we can use linq to convert the collection of cloud facts into cloud view models.

public IEnumerable<CloudViewModel> Clouds
{
    get
    {
        return
            from c in _identity.Clouds
            select new CloudViewModel(_identity, null);
    }
}

That constructor is not making much sense anymore. It takes an identity and central thought, not a cloud. That gives us a clue about the next changes we need to make. We’ll refactor that next time.

Thought Cloud TDD #3: Train of thought

May 17th, 2011

I’m having fun building Thought Cloud. I hope you are, too.

One thought does not a cloud make. The user needs the ability to create new thoughts.

[TestMethod]
public void CanCreateANewThought()
{
    _cloudViewModel.NewThought.Execute(null);
    Assert.AreEqual(2, _cloudViewModel.Thoughts.Count());
}

To create a new thought, we need access to the Community. We initialized _community and used it to create our Identity and initial Thought. Rather than going back to _community each time we want to add a fact, we’ll let one of the existing facts do the work for us. Let’s have the Identity create a Thought. We can add methods to a fact by declaring a partial class.

public partial class Identity
{
    public Thought NewThought()
    {
        return Community.AddFact(new Thought());
    }
}

Every fact has a private Community property that it can use to create other facts. Remember rule #2: create one fact for each user action. This is how a fact performs actions.

To call this method, CloudViewModel needs an Identity. Let’s add that parameter.

public class CloudViewModel
{
    private Identity _identity;
    private Thought _centralThought; 

    public CloudViewModel(Identity identity, Thought centralThought)
    {
        _identity = identity;
        _centralThought = centralThought;
    } 

    ...
}

And now we can write the NewThought command. It is a property of type ICommand. The unit test executes the command the same way the user would when pressing a button. We’ll implement that interface using MakeCommand from Update Controls.

public ICommand NewThought
{
    get
    {
        return MakeCommand
            .Do(() => _identity.NewThought());
    }
}

Run the test and see if it passes. It doesn’t? Well, what have we missed?

Linked thoughts

Thoughts only form a cloud when you can join them together. The view model is only returning the central thought. We want it to return all related thoughts. So let’s define a link between thoughts.

fact Link {
key:
    Thought* thoughts;
}

A link is identified by the set of thoughts that it connects. By convention, we will only connect two thoughts with a link, but there is no way to enforce that convention in the Factual model. The key is simply a collection of thoughts. Let’s add a LinkTo method to a Thought in a partial class.

public partial class Thought
{
    public Link LinkTo(Thought otherThought)
    {
        return Community.AddFact(new Link(new List<Thought> { this, otherThought }));
    }
}

After we create the new thought, we link it with the central thought of the cloud.

public ICommand NewThought
{
    get
    {
        return MakeCommand.Do(() => _centralThought.LinkTo(_identity.NewThought()));
    }
}

Facts are not just records. They are domain objects with behavior. It’s just that they perform that behavior by creating new facts.

So does it pass yet? No? I guess we need to do one more thing.

Query for related facts

The cloud view model is currently programmed to return only the central thought. It needs to also return all thoughts immediately linked to it. We’ll do that with a query.

fact Thought {
key:
    unique; 

mutable:
    string text; 

query:
    Thought* neighbors {
        Link l : l.thoughts = this
        Thought t : l.thoughts = t
    }
}

The query gets all Links l such that one of the thoughts is this one, and then it gets all Thoughts t such that one of l’s thoughts is t. That’s just set notation for the immediate neighbors. We can return that set from the view model.

public IEnumerable<ThoughtViewModel> Thoughts
{
    get
    {
        return Enumerable.Repeat(new ThoughtViewModel(_centralThought), 1).Union(
            from n in _centralThought.Neighbors
            select new ThoughtViewModel(n));
    }
}

That big fancy linq statement creates a collection having only the central thought view model. Then it unions that with the set of neighbor view models. So we should get two, right? Alas No! The test still fails!

Careful what you query for

If you look closely at the failure reason, this time it failed because we returned too many thoughts. We expected 2, but returned 3. The problem is that the query includes all thoughts of all related links. In other words, the query returns the central thought itself. Let’s filter this out.

public IEnumerable<ThoughtViewModel> Thoughts
{
    get
    {
        return Enumerable.Repeat(new ThoughtViewModel(_centralThought), 1).Union(
            from n in _centralThought.Neighbors
            where n != _centralThought
            select new ThoughtViewModel(n));
    }
}

And now does it pass? Yes!

A fact can relate other facts, just like an associative table in a relational database. In order to find the related facts, just query. Factual uses set notation to define queries, but they work just like relational joins.

Thought Cloud TDD #2: A new thought

May 16th, 2011

I’m working on Thought Cloud for an upcoming demo. Join me, won’t you?

Last time we wrote a failing test about changing the text of the initial thought. Now let’s make it pass.

The view model doesn’t have a model backing it, so it can’t store the thought text. Even though we are doing the simplest thing to make the test pass, we must still obey the rules of the Correspondence architecture:

Rule #1: Never store state in the view model.

The view model layer exists only to interpret models for the view. If you have any state to store, it must find its way to a model. In this case, it must be stored in a Thought fact. So we define one in the Factual file model.fact.

fact Thought {
key:
    unique;

mutable:
    string text;
}

Be sure to hit “Transform all templates” whenever changing this file. Then build just to be sure the syntax is correct.

Every Thought is unique. It has a unique key (a GUID), so that every time you create a new Thought, you get a new instance. The key is immutable. The text, however, is mutable. It is not part of the key. So this can store the state to make our unit test pass. We just need to create a new Thought to give to the view model.

Thought thought = _community.AddFact(new Thought());
_cloudViewModel = new CloudViewModel(thought);
public class CloudViewModel
{
    private Thought _centralThought;

    public CloudViewModel(Thought centralThought)
    {
        _centralThought = centralThought;
    }

    public IEnumerable<ThoughtViewModel> Thoughts
    {
        get
        {
            yield return new ThoughtViewModel(_centralThought);
        }
    }
}
public class ThoughtViewModel
{
    private Thought _thought;

    public ThoughtViewModel(Thought thought)
    {
        _thought = thought;
    }

    public string Text
    {
        get { return _thought.Text.Value ?? "My thought"; }
        set { _thought.Text = value; }
    }
}

Now the tests pass. Notice that we aren’t setting the initial text of the Thought. We rely upon the fact that a mutable string is initially null, and interpret that as the initial value in the view model. This honors the second rule of Correspondence architecture.

Rule #2: Create one fact for each user action.

We could have assigned Text to “My thought” right after creating it, but that would have actually created two facts. Assigning a mutable field actually creates a new fact behind the scenes. If we received the Thought from a peer, there is no guarantee that we would receive both facts at the same time. So we must honor the state in which we have uninitialized text as valid.

Thought Cloud TDD #1: Initial thoughts

May 16th, 2011

I’m building a demo for Correspondence. Grab the Thought Cloud code and follow along.

The UnitTest project starts out with a model test, but I want to build this thing view model first. So let’s initialize a new test class.

[TestClass]
public class ViewModelTest : SilverlightTest
{
    private Community _community;
    private Identity _identity;
    private CloudViewModel _cloudViewModel;

    [TestInitialize]
    public void Initialize()
    {
        _community = new Community(new MemoryStorageStrategy())
            .Register<Model.CorrespondenceModel>();

        _identity = _community.AddFact(new Identity("mike"));
        _cloudViewModel = new CloudViewModel();
    }
}

The Community uses the memory storage strategy, which is ideal for unit testing. The real application will use isolated storage, but memory will be reset with each test run. We also register the model, and create the initial fact, and create a new view model.

When the user first starts the application, they should see a thought bubble that says “My thought”. (Actually, first they must log in, but that is uninteresting to me right now.)

[TestMethod]
public void InitialThoughtIsMyThought()
{
    ThoughtViewModel thought = _cloudViewModel.Thoughts.Single();
    Assert.AreEqual("My thought", thought.Text);
}

To make this pass, we do the simplest thing possible.

public class CloudViewModel
{
    public IEnumerable<ThoughtViewModel> Thoughts
    {
        get
        {
            yield return new ThoughtViewModel();
        }
    }
}
public class ThoughtViewModel
{
    public string Text
    {
        get
        {
            return "My thought";
        }
    }
}

Test passes. On to the next. The user can edit this initial thought to change the text.

[TestMethod]
public void CanChangeThoughtText()
{
    ThoughtViewModel thought = _cloudViewModel.Thoughts.Single();
    thought.Text = "New thought";
    Assert.AreEqual("New thought", thought.Text);
}

The simplest thing possible won’t make this pass anymore. By forcing ourselves to write simple methods, we ensure that there are no missing tests. We’ll make this test pass next time.