The ChatRoom class is slimmer than the User class. It allows participants to join and sends chat messages to registered participants. When joining a ChatRoom, it keeps a reference on the IParticipant, tells that IParticipant that it has successfully joined then sends a ChatMessage to all participants announcing the newcomer.With those few pieces, we have a Mediator implementation. Before moving to the next section, let’s look at the Consumer instance of IChatRoom, which is another integration test. Let’s start with the skeleton of the class:

namespace Mediator;
public class ChatRoomTest
{
    [Fact]
    public void ChatRoom_participants_should_send_and_receive_messages()
    {
        // Arrange, Act, Assert blocks here
    }
    private (TestMessageWriter, User) CreateTestUser(string name)
    {
        var writer = new TestMessageWriter();
        var user = new User(writer, name);
        return (writer, user);
    }
    private class TestMessageWriter : IMessageWriter<ChatMessage>
    {
        public StringBuilder Output { get; } = new StringBuilder();
        public void Write(ChatMessage message)
        {
            Output.AppendLine($”[{message.Sender.Name}]: {message.Content}”);
        }
    }
}

In the preceding code, we have the following pieces:

  • The test case is an empty placeholder that we are about to look into.
  • The CreateTestUser method helps simplify the Arrange section of the test case, similar to before.
  • The TestMessageWriter implementation is similar to the previous example, accumulating messages in a StringBuilder instance.

As a reference, the IMessageWriter interface is the same as the previous project:

public interface IMessageWriter<TMessage>
{
    void Write(TMessage message);
}

Now, let’s explore the test case, starting with the Arrange block, where we create four users with their respective TestMessageWriter instances (names were also randomly generated):

// Arrange
var (kingChat, king) = CreateTestUser(“King”);
var (kelleyChat, kelley) = CreateTestUser(“Kelley”);
var (daveenChat, daveen) = CreateTestUser(“Daveen”);
var (rutterChat, _) = CreateTestUser(“Rutter”);
var chatroom = new ChatRoom();

Then, in the Act block, our test users join the chatroom instance and send messages:

// Act
chatroom.Join(king);
chatroom.Join(kelley);
king.Send(“Hey!”);
kelley.Send(“What’s up King?”);
chatroom.Join(daveen);
king.Send(“Everything is great, I joined the CrazyChatRoom!”);
daveen.Send(“Hey King!”);
king.Send(“Hey Daveen”);

Then in the Assert block, Rutter did not join the chatroom, so we expect no message:

// Assert
Assert.Empty(rutterChat.Output.ToString());

Since King is the first to join the channel, we expect him to receive all messages:

Assert.Equal(@”[King]: Has joined the channel
[Kelley]: Has joined the channel
[King]: Hey!
[Kelley]: What’s up King?
[Daveen]: Has joined the channel
[King]: Everything is great, I joined the CrazyChatRoom!
[Daveen]: Hey King!
[King]: Hey Daveen
“, kingChat.Output.ToString());

Kelley was the second user to join the chatroom, so the output contains almost all messages except the line saying [King]: Has joined the channel:

Assert.Equal(@”[Kelley]: Has joined the channel
[King]: Hey!
[Kelley]: What’s up King?
[Daveen]: Has joined the channel
[King]: Everything is great, I joined the CrazyChatRoom!
[Daveen]: Hey King!
[King]: Hey Daveen
“, kelleyChat.Output.ToString());

Daveen joined after King and Kelley exchanged a few words, so we expect the conversation to be shorter:

Assert.Equal(@”[Daveen]: Has joined the channel
[King]: Everything is great, I joined the CrazyChatRoom!
[Daveen]: Hey King!
[King]: Hey Daveen
“, daveenChat.Output.ToString());

To summarize the test case, we have four users. Three of them joined the same chatroom at a different time and chatted a little. The output is different for everyone since the time you join matters. All participants are loosely coupled, thanks to the Mediator pattern, allowing us to extend the system without impacting the existing pieces. Leveraging the Mediator pattern helps us create maintainable systems; many small pieces are easier to manage and test than a large component handling all the logic.