,

A Testable Chat Client

This section explores a simple chat client app. First, a custom chat service that is used to send and receive messages is discussed. Next, you examine how to substitute types using mocking and look at mocking the chat service. You see how to test the functionality of a viewmodel without needing to create a view for it. Finally, you learn how to create coded UI tests using the UTF TestPanel in combination with the Microsoft Automation framework.

The code for this section is located in the ChatClientView and ChatClientViewModel classes in the downloadable sample code.

The chat client app relies on a chat service, represented by an IChatService interface. The IChatService specifies a method to send a message, presumably to a cloud service, and an event to indicate when the service receives a message. The interface is shown in the following excerpt:

public interface IChatService
{
    void SendMessage(string message);
    event EventHandler<ChatMessageEventArgs> MessageReceived;
}

A mock chat service is used during unit testing. Mock objects mimic the behavior of real objects in controlled ways. In this case, MockChatService acts as loopback; when the SendMessage method is called, the MessageReceived event is raised, as shown:

public class MockChatService : IChatService
{
    public bool MessageSent { get; private set; }
    public string LastMessage { get; private set; }

    public void SendMessage(string message)
    {
        MessageSent = true;
        LastMessage = message;
        OnMessageReceived(new ChatMessageEventArgs(message));
    }

    public event EventHandler<ChatMessageEventArgs> MessageReceived;

    public void OnMessageReceived(ChatMessageEventArgs e)
    {
        MessageReceived.Raise(this, e);
    }
}

The viewmodel for the chat client uses the chat service to send messages using a SendCommand (see Listing 24.1). When the SendCommand executes, the IChatService.SendMessage method receives the message.

The viewmodel subscribes to the IChatService.MessageReceived event. When a message is received, it is placed into an ObservableCollection of messages, which are then presented in the view.

LISTING 24.1. ChatClientViewModel Class (excerpt)


public class ChatClientViewModel : ViewModelBase
{
    readonly IChatService chatService;

    public ChatClientViewModel(IChatService chatService)
    {
        this.chatService = ArgumentValidator.AssertNotNull(
                                        chatService, "chatService");

        sendCommand = new DelegateCommand(
            delegate
            {
                if (string.IsNullOrEmpty(message))
                {
                    return;
                }
                chatService.SendMessage(message);
                Message = string.Empty;
            },
            delegate { return !string.IsNullOrEmpty(message); });

        chatService.MessageReceived
            += (sender, args) => messages.Add(args.Message);

        PropertyChanged += delegate { sendCommand.RaiseCanExecuteChanged(); };

        /* The rest of the constructor is shown later in the chapter. */
    }

    readonly ObservableCollection<string> messages
                    = new ObservableCollection<string>();

    public ObservableCollection<string> Messages
    {
        get
        {
            return messages;
        }
    }

    string message;
    public string Message
    {
        get
        {
            return message;
        }
        set
        {
            Assign(ref message, value);
        }
    }

    readonly DelegateCommand sendCommand;

    public ICommand SendCommand
    {
        get
        {
            return sendCommand;
        }
    }

    /* The rest of the class is shown later in the chapter. */
}


With the infrastructure in place, you can set about creating some unit tests for the viewmodel.


Note

An alternative approach, known as Test Driven Development (TDD), sees the creation of the unit tests first. If you are willing to take an interface first approach, or have a tool such as Resharper that allows you to quickly generate class members, you may choose to create the unit tests before you implement the class that is the subject of the unit tests.


Listing 24.2 shows various test methods for verifying that the viewmodel is able to send a message correctly and that it is able to respond correctly when it receives a message from the chat service.

LISTING 24.2. Chat Client ViewModel Tests


[TestClass]
public class ChatClientTests : WorkItemTest
{
    [TestMethod]
    public void ShouldSendMessage()
    {
        string testMessage = "Hello from unit test.";
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        viewModel.Message = testMessage;
        viewModel.SendCommand.Execute(null);
        Assert.AreEqual(chatService.LastMessage, testMessage);
    }

    [TestMethod]
    public void ShouldNotSendMessageIfEmpty()
    {
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        viewModel.Message = string.Empty;
        viewModel.SendCommand.Execute(null);
        Assert.IsFalse(chatService.MessageSent);
    }

    [TestMethod]
    public void CommandShouldBeDisabledIfMessageIfEmpty()
    {
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        viewModel.Message = string.Empty;
        Assert.IsFalse(viewModel.SendCommand.CanExecute(null));
    }
    [TestMethod]
    [Description(@"When the chat service receives a message,
        the client displays it.")]
    public void CommandShouldBeEnabledIfMessageNotEmpty()
    {
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        viewModel.Message = "Test";
        Assert.IsTrue(viewModel.SendCommand.CanExecute(null));
    }

    [TestMethod]
    public void ShouldReceiveMessage()
    {
        string testMessage = "Hello from unit test.";
        MockChatService chatService = new MockChatService();
        ChatClientViewModel viewModel = new ChatClientViewModel(chatService);
        chatService.OnMessageReceived(new ChatMessageEventArgs(testMessage));
        CollectionAssert.Contains(viewModel.Messages, testMessage);
    }
}


The result of running the unit tests is shown in Figure 24.5.

Image

FIGURE 24.5 Chat client test results.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.143.4.117