Другие статьи по юнит-тестированию:

Проекты с примерами из статьи:

Зачем нужны моки
Юнит-тестирование класса, работа которого не зависит от поведения других классов — несложная задача. Мы можем написать что-то вроде:

assertEquals(4, calculator.add(2, 2));

Но приложение web mvc, как правило, состоит из нескольких независимых слоев:
Архитектура MVC-приложения

Каждый из слоев содержит классы, поведение которых зависит от классов нижележащего уровня. По правилам, в такой архитектуре классы вышележащего слоя имеют доступ только к классам прилегающего нижнего слоя и не используют классы более глубоких слоёв.

Для юнит-тестирования имеющего зависимости класса потребуется эмулировать поведение нижележащего класса без инстанцирования реального объекта. Для этой цели служат мок-фреймворки. Мок (англ. mock — пародия, подделка) позволяет создать объект-заглушку по интерфейсу зависимости, настроить требуемое поведение и проконтролировать все произведенные с ним операции.

Использование моков для тестирования web mvc приложения покажем на примере простого приложения текстового чата. Для .NET используем ASP.NET MVC, Unity, MSTest и мок-фреймворк Moq совместно с UnityAutoMock; для Java: Spring MVC, JUnit, Mockito и MockMvc.

Пример настройки и проверки поведения (Java)
Для начала рассмотрим задачу получения пользователем списка отправленных ему новых сообщений. Команда должна получить из БД список сообщений по идентификатору пользователя, сформировать из них MVC-модель и сохранить идентификатор последнего сообщения в репозитории пользователя. Моки потребуются для интерфейсов уровня базы данных. В MessagesView сэмулируем метод, возвращающий новые сообщения пользователя:

public interface MessagesView {
    List getMessages(Integer userId);
}

В UserRepository сэмулируем метод setLastViewedMessageId, сохраняющий идентификатор последнего сообщения:

public interface UserRepository {
    Integer create(User user);
    void update(User user);
    void setLastViewedMessageId(Integer userId, Long messageId);
}

Тестовый класс:

// аннотация позволит Mockito проинициализировать моки внутри тестового класса
@RunWith(MockitoJUnitRunner.class)
public class GetNewestMessagesCommandTest {

    @Mock // мок для MessagesView
    private MessagesView messagesViewMock; 
    @Mock // мок для UserRepository
    private UserRepository userRepositoryMock;
    @InjectMocks // тестируемый класс - в него будут внедрены моки вместо реальных объектов
    private GetNewestMessagesCommand command;

    @Test
    public void testExecute() {
        // создадим сообщения, которые должен вернуть messagesViewMock
        final Integer userId = 1;
        final Message message1 = new Message();
        message1.setText("text 1");
        message1.setId(1l);
        message1.setUserSenderId(111);
        message1.setUserRecipientId(userId);
        final Message message2 = new Message();
        message2.setText("text 2");
        message2.setId(2l);
        message2.setUserSenderId(333);
        message2.setUserRecipientId(userId);
        // настраиваем mock для метода messagesView.getMessages(): 
        // если входной параметр равен userId, то вернуть список наших сообщений
        when(messagesViewMock.getMessages(userId)).thenReturn(Arrays.asList(message1, message2));

        // вызываем тестируемый метод, внутри происходит обращение к messagesViewMock и userRepositoryMock
        List actualMessages = command.execute(userId);
        // проверяем результат работы команды
        assertEquals(2, actualMessages.size());
        assertEquals(message1.getText(), actualMessages.get(0).getText());
        assertEquals(message1.getUserSenderId(), actualMessages.get(0).getUserSenderId());
        assertEquals(message1.getUserRecipientId(), actualMessages.get(0).getUserRecipientId());
        assertEquals(message2.getText(), actualMessages.get(1).getText());
        assertEquals(message2.getUserSenderId(), actualMessages.get(1).getUserSenderId());
        assertEquals(message2.getUserRecipientId(), actualMessages.get(1).getUserRecipientId());
        // проверяем, что команда вызвала у userRepository нужный метод с правильными аргументами
        verify(userRepositoryMock).setLastViewedMessageId(userId, message2.getId());
    }

}

Тестируемый класс:

@Component
public class GetNewestMessagesCommand {

    @Autowired
    private MessagesView messagesView;
    @Autowired
    private UserRepository userRepository;

    public List execute(Integer userId) {
        List dbMessages = messagesView.getMessages(userId);
        List userMessages = new ArrayList();
        for (Message message : dbMessages) {
            MessageModel userMessage = new MessageModel();
            userMessage.setText(message.getText());
            userMessage.setUserSenderId(message.getUserSenderId());
            userMessage.setUserRecipientId(message.getUserRecipientId());
            userMessages.add(userMessage);
        }
        userRepository.setLastViewedMessageId(userId, dbMessages.get(dbMessages.size() - 1).getId());
        return userMessages;
    }
}

Пример настройки и проверки поведения (C#)
Аналогичная задача на C# с использованием мок-фреймворка Moq:

    public interface IMessagesView
    {
        IEnumerable GetMessages(Int32 userId);
    }

    public interface IUserRepository
    {
        Int32 Create(User user);
        void Update(User user);
        void SetLastViewedMessageId(Int32 userId, Int64 messageId);
    }

Тест:

    [TestClass]
    public class GetNewestMessagesCommandTest
    {
        [TestMethod]
        public void TestExecute()
        {
            // создадим сообщения, которые должен вернуть messagesViewMock
            const int userId = 1;
            var message1 = new Message {Text = "text 1", Id = 1L, UserSenderId = 111, UserRecipientId = userId};
            var message2 = new Message {Text = "text 2", Id = 2L, UserSenderId = 333, UserRecipientId = userId};
            // настраиваем mock для метода messagesView.getMessages(): 
            // если входной параметр равен userId, то вернуть список наших сообщений
            var messagesViewMock = new Mock();
            messagesViewMock.Setup(v => v.GetMessages(userId)) // условие срабатывания мока
                .Returns(new [] {message1, message2}) // что вернуть
                .Verifiable(); // включить проверяемость этого вызова
            var userRepositoryMock = new Mock();

            // вызываем тестируемый метод, внутри происходит обращение к messagesViewMock и userRepositoryMock
            var command = new GetNewestMessagesCommand(userRepositoryMock.Object, messagesViewMock.Object);
            var actualMessages = command.Execute(userId).ToArray();
            Assert.AreEqual(2, actualMessages.Count());
            Assert.AreEqual(message1.Text, actualMessages[0].Text);
            Assert.AreEqual(message1.UserSenderId, actualMessages[0].UserSenderId);
            Assert.AreEqual(message1.UserRecipientId, actualMessages[0].UserRecipientId);
            Assert.AreEqual(message2.Text, actualMessages[1].Text);
            Assert.AreEqual(message2.UserSenderId, actualMessages[1].UserSenderId);
            Assert.AreEqual(message2.UserRecipientId, actualMessages[1].UserRecipientId);
            // проверяем обращения к messagesView
            messagesViewMock.Verify();
            // проверяем, что команда вызвала у userRepository нужный метод с правильными аргументами
            userRepositoryMock.Verify(r => r.SetLastViewedMessageId(userId, message2.Id));
        }
    }

Тестируемый класс:

    public interface IGetNewestMessagesCommand
    {
        IEnumerable Execute(Int32 userId);
    }

    public class GetNewestMessagesCommand : IGetNewestMessagesCommand
    {
        private readonly IUserRepository userRepository;
        private readonly IMessagesView messagesView;

        public GetNewestMessagesCommand(IUserRepository userRepository, IMessagesView messagesView)
        {
            this.userRepository = userRepository;
            this.messagesView = messagesView;
        }

        public IEnumerable Execute(Int32 userId)
        {
            var dbMessages = messagesView.GetMessages(userId).ToList();
            var userMessages = dbMessages.Select(message => new MessageModel
                {
                    Text = message.Text,
                    UserSenderId = message.UserSenderId,
                    UserRecipientId = message.UserRecipientId
                });
            userRepository.SetLastViewedMessageId(userId, dbMessages.Last().Id);
            return userMessages;
        }
    }

Перехват переданного в мок аргумента (Java)
В первом примере мы проверили полученный от тестируемого класса объект. Немного более сложная задача проверить объект, который был передан тестируемым классом в мок. Для этого воспользуемся классом ArgumentCaptor на примере команды создания нового сообщения:

@RunWith(MockitoJUnitRunner.class)
public class CreateMessageCommandTest {

    @Mock
    private MessagesRepository messagesRepositoryMock;
    @InjectMocks
    private CreateMessageCommand command;

    @Test
    public void testExecute() {
        // инициализация исходных данных для теста
        String expectedText = "Hello, world!";
        Integer expectedUserSenderId = 111;
        Integer expectedUserRecipientId = 222;
        Long expectedMessageId = 999l;
        // настройка перехватчика аргумента, который будет передан в мок
        ArgumentCaptor messageCaptor = ArgumentCaptor.forClass(Message.class);
        when(messagesRepositoryMock.createMessage(messageCaptor.capture())).thenReturn(expectedMessageId);
        
        // вызываем тестируемый метод с исходными данными
        Long actualMessageId = command.execute(expectedText, expectedUserSenderId, expectedUserRecipientId);
        assertEquals(expectedMessageId, actualMessageId);
        // получаем аргумент, с которым был вызван метод messagesRepository.createMessage()
        Message actualMessage = messageCaptor.getValue();
        // проверяем, правильно ли команда собрала объект для сохранения в БД 
        assertEquals(expectedText, actualMessage.getText());
        assertEquals(expectedUserSenderId, actualMessage.getUserSenderId());
        assertEquals(expectedUserRecipientId, actualMessage.getUserRecipientId());
        assertNotNull(actualMessage.getMessageDate());
    }
}

Тестируемый класс:

@Component
public class CreateMessageCommand {
    
    @Autowired
    private MessagesRepository messagesRepository;
    
    public Long execute(String text, Integer userSenderId, Integer userRecipientId) {
        Message message = new Message();
        message.setText(text);
        message.setUserSenderId(userSenderId);
        message.setUserRecipientId(userRecipientId);
        message.setMessageDate(new Date());
        return messagesRepository.createMessage(message);
    }
}

Перехват переданного в мок аргумента (C#)
В Moq для этой задачи есть метод Callback(), принимающий делегат:

    [TestClass]
    public class CreateMessageCommandTest
    {
        [TestMethod]
        public void TestExecute()
        {
            // инициализация исходных данных для теста
            const string expectedText = "Hello, world!";
            const int expectedUserSenderId = 111;
            const int expectedUserRecipientId = 222;
            const long expectedMessageId = 999L;
            Message actualMessage = null;
            var messagesRepositoryMock = new Mock();
            messagesRepositoryMock.Setup(r => r.CreateMessage(It.IsAny()))
                .Callback(m => actualMessage = m) // callback сохранит актуальный аргумент в локальную переменную
                .Returns(expectedMessageId);
            
            var command = new CreateMessageCommand(messagesRepositoryMock.Object);
            // вызываем тестируемый метод с исходными данными
            var actualMessageId = command.Execute(expectedText, expectedUserSenderId, expectedUserRecipientId);
            Assert.AreEqual(expectedMessageId, actualMessageId);
            // проверяем, правильно ли команда собрала объект для сохранения в БД 
            Assert.IsNotNull(actualMessage);
            Assert.AreEqual(expectedText, actualMessage.Text);
            Assert.AreEqual(expectedUserSenderId, actualMessage.UserSenderId);
            Assert.AreEqual(expectedUserRecipientId, actualMessage.UserRecipientId);
            Assert.IsNotNull(actualMessage.MessageDate);
        }
    }

Тестируемый класс:

    public interface ICreateMessageCommand
    {
        Int64 Execute(String text, Int32 userSenderId, Int32 userRecipientId);
    }

    public class CreateMessageCommand : ICreateMessageCommand
    {
        private readonly IMessagesRepository messagesRepository;

        public CreateMessageCommand(IMessagesRepository messagesRepository)
        {
            this.messagesRepository = messagesRepository;
        }

        public Int64 Execute(String text, Int32 userSenderId, Int32 userRecipientId)
        {
            return messagesRepository.CreateMessage(new Message
                {
                    Text = text,
                    UserSenderId = userSenderId,
                    UserRecipientId = userRecipientId,
                    MessageDate = DateTime.Now
                });
        } 
    }

Проверка свойств аргумента при настройке мока (Java)
Другая актуальная задача при использовании моков — при настройке проверить свойства передаваемого в заглушку аргумента. В этом случае надо воспользоваться сравнителем argThat и реализовать потомок класса ArgumentMatcher. Рассмотрим этот механизм на примере команды создания нового пользователя.

@RunWith(MockitoJUnitRunner.class)
public class CreateUserCommandTest {

    @Mock // мок для UserRepository
    private UserRepository userRepositoryMock;
    @InjectMocks // тестируемый класс - в него будут внедрены моки вместо реальных объектов
    private CreateUserCommand command;

    @Test
    public void testExecute() {
        // создаем объект, который контроллер передаст команде
        final UserModel userModel = new UserModel();
        userModel.setName("Ivanov");
        userModel.setEmail("ivanov@test.com");
        Integer expectedUserId = 2;
        // в анонимном классе проверим сохраненный в БД объект
        when(userRepositoryMock.create(argThat(new ArgumentMatcher() {
            @Override
            public boolean matches(Object argument) {
                User user = (User) argument;
                return user.getId() == null && user.getName().equals(userModel.getName())
                        && user.getEmail().equals(userModel.getEmail()) && user.getCreateDate() != null;
            }
        }))).thenReturn(expectedUserId);
        Integer actualUserId = command.execute(userModel);
        assertEquals(expectedUserId, actualUserId);
    }
}

Тестируемый класс:

@Component
public class CreateUserCommand {

    @Autowired
    private UserRepository userRepository;

    public Integer execute(UserModel userModel) {
        User user = new User();
        user.setName(userModel.getName());
        user.setEmail(userModel.getEmail());
        user.setCreateDate(new Date());
        return userRepository.create(user);
    }
}

Проверка свойств аргумента при настройке мока (C#)
Здесь можно воспользоваться сравнителем It.Is, принимающий делегат для проверки входных параметров:

    [TestClass]
    public class CreateUserCommandTest
    {
        [TestMethod]
        public void TestExecute()
        {
            // создаем объект, который контроллер передаст команде
            var userModel = new UserModel {Name = "Ivanov", Email = "ivanov@test.com"};
            const int expectedUserId = 2;
            var userRepositoryMock = new Mock();
            userRepositoryMock.Setup(r => r.Create(It.Is(
                    // проверка свойств переданного в мок аргумента
                    u => u.Id.Equals(0) && u.Name.Equals(userModel.Name) && u.Email.Equals(userModel.Email) && u.CreateDate != null
                )))
                .Returns(expectedUserId)
                .Verifiable(); // этот метод позволяет в дальнейшем проверить вызов мока
            var command = new CreateUserCommand(userRepositoryMock.Object);
            var actualUserId = command.Execute(userModel);
            Assert.AreEqual(expectedUserId, actualUserId);
            userRepositoryMock.Verify(); // проверим запланированные вызовы 
        }
    }

Тестируемый класс:

    public interface ICreateUserCommand
    {
        Int32 Execute(UserModel userModel);
    }

    public class CreateUserCommand : ICreateUserCommand
    {
        private readonly IUserRepository userRepository;

        public CreateUserCommand(IUserRepository userRepository)
        {
            this.userRepository = userRepository;
        }

        public Int32 Execute(UserModel userModel)
        {
            var user = new User {Name = userModel.Name, Email = userModel.Email, CreateDate = DateTime.Now};
            return userRepository.Create(user);
        }
    }

Тесты MVC-контроллеров

Тест POST-запроса (Java)
Для теста MVC-контроллеров удобно использовать класс MockMvc, который включен в поставку Spring MVC, начиная с версии 3.2.3. В следующем примере тестируется метод, создающий новое сообщение при помощи команды CreateMessageCommand. Создадим модель сообщения, сериализуем в формате JSON и сделаем POST в экшн контроллера. Затем проверим, что команда была вызвана с правильными аргументами. В настройке createMessageCommandMock используются специальные сравнители anyString() и anyInt(), так как мы не хотим указывать конкретные значения аргументов. Правильность переданных аргументов будет проверена в конце теста методом verify(). Примечание: сравнитель eq() требуется, если в нескольких аргументах мока для одного аргумента требуется сравнить конкретное значение, а для другого аргумента допустить любое значение any…().

@RunWith(MockitoJUnitRunner.class)
public class MessageControllerTest {

    @Mock
    private CreateMessageCommand createMessageCommandMock;
    @Mock
    private GetNewestMessagesCommand getNewestMessagesCommandMock;
    @InjectMocks
    private MessageController controller;

    private MockMvc mockMvc;

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void testCreateMessage() throws Exception {
        // инициализация исходных данных
        MessageModel message = new MessageModel();
        message.setText("Привет!");
        message.setUserSenderId(1);
        message.setUserRecipientId(2);
        final Long messageId = 999l;
        // настройка мока команды createMessageCommand
        when(createMessageCommandMock.execute(anyString(), anyInt(), anyInt())).thenReturn(messageId);
        // эмулируем HTTP запрос к контроллеру
        mockMvc.perform(put("/message") // PUT по адресу /message
                .contentType(TestUtil.APPLICATION_JSON_UTF8) // настройка заголовка запроса
                .content(TestUtil.convertObjectToJsonBytes(message))) // контент - наше сообщение в формате JSON
                .andExpect(status().isCreated()) // ожидаем в ответ HTTP статус 201
                .andExpect(content().string(messageId.toString())); // в ответе должен быть идентификатор нового сообщения
        
        // проверяем, что контроллер вызвал команду с правильными аргументами
        verify(createMessageCommandMock).execute(message.getText(), message.getUserSenderId(), message.getUserRecipientId());
    }
}

Тестируемый метод:

    @RequestMapping(value = "/message", method = RequestMethod.PUT)
    public ResponseEntity createMessage(@RequestBody MessageModel message) {
        Long messageId = createMessageCommand.execute(message.getText(), message.getUserSenderId(), message.getUserRecipientId());
        return new ResponseEntity(messageId, HttpStatus.CREATED);
    }

Тест POST-запроса (C#)
Для разрешения зависимостей здесь используется контейнер Microsoft Unity 3.5, для автоматической замены зависимостей моками используется библиотека UnityAutoMoq 2.1.1. Аналогами сравнителей anyString() и anyInt() в Moq являются It.IsAny(), It.IsAny(). При вызове метода контроллера сериализация в JSON не потребуется, вызовем экшн как метод объекта.

    [TestClass]
    public class MessageControllerTest
    {
        private Mock createMessageCommandMock;
        private Mock getNewestMessagesCommandMock;
        private MessageController controller;

        [TestInitialize]
        public void Init()
        {
            // контейнер автоматически проинициализирует зависимости моками
            var container = new UnityAutoMoqContainer();
            // получаем инициализированный контроллер
            controller = container.Resolve();
            // получаем моки, которыми инициализирован контроллер
            createMessageCommandMock = container.GetMock();
            getNewestMessagesCommandMock = container.GetMock();
        }

        [TestMethod]
        public void TestCreateMessage()
        {
            // инициализация исходных данных
            var message = new MessageModel {Text = "Привет!", UserSenderId = 1, UserRecipientId = 2};
            const long messageId = 999L;
            // настройка мока команды createMessageCommand
            createMessageCommandMock.Setup(c => c.Execute(It.IsAny(), It.IsAny(), It.IsAny()))
                                    .Returns(messageId)
                                    .Verifiable();
            // вызов метода контроллера с приведением типа результата
            var result = controller.CreateMessage(message) as ContentResult;
            // проверка возвращенного результата
            Assert.IsNotNull(result);
            Assert.AreEqual(messageId.ToString(CultureInfo.InvariantCulture), result.Content);
            // проверяем, что контроллер вызвал команду с правильными аргументами
            createMessageCommandMock.Verify(c => c.Execute(message.Text, message.UserSenderId, message.UserRecipientId));
        }
    }
}

Тестируемый метод:

    public class MessageController : Controller
    {
        private readonly ICreateMessageCommand createMessageCommand;
        private readonly IGetNewestMessagesCommand getNewestMessagesCommand;

        public MessageController(ICreateMessageCommand createMessageCommand,
                                 IGetNewestMessagesCommand getNewestMessagesCommand)
        {
            this.createMessageCommand = createMessageCommand;
            this.getNewestMessagesCommand = getNewestMessagesCommand;
        }

        [HttpPost]
        public ActionResult CreateMessage(MessageModel message)
        {
            var messageId = createMessageCommand.Execute(message.Text, message.UserSenderId, message.UserRecipientId);
            return Content(messageId.ToString(CultureInfo.InvariantCulture));
        }
    }

Тест результата в формате JSON (Java)
Далее рассмотрим случай проверки возвращаемого контроллером контента. Тестируемый метод должен возвратить список новых сообщений для пользователя в формате JSON:

    @Test
    public void testNewMessages() throws Exception {
        // инициализация исходных данных
        final Integer userId = 111;
        MessageModel message1 = new MessageModel();
        message1.setText("текст 1");
        message1.setUserRecipientId(userId);
        message1.setUserSenderId(222);
        MessageModel message2 = new MessageModel();
        message2.setText("текст 2");
        message2.setUserRecipientId(userId);
        message2.setUserSenderId(333);
        // настройка мока команды getNewestMessagesCommand
        when(getNewestMessagesCommandMock.execute(userId)).thenReturn(Arrays.asList(message1, message2));
        // эмулируем HTTP запрос к контроллеру
        mockMvc.perform(get("/message/{userId}", userId)) // GET по адресу /message/111
                .andExpect(status().isOk()) // ожидаем в ответ HTTP статус 200
                .andExpect(content().contentType(TestUtil.APPLICATION_JSON_UTF8)) // в ответе должен быть заголовок с типом контента
                // далее проверяем контент, который вернул контроллер
                .andExpect(jsonPath("$", hasSize(2)))
                .andExpect(jsonPath("$[0].userRecipientId", is(message1.getUserRecipientId())))
                .andExpect(jsonPath("$[0].userSenderId", is(message1.getUserSenderId())))
                .andExpect(jsonPath("$[0].text", is(message1.getText())))
                .andExpect(jsonPath("$[1].userRecipientId", is(message2.getUserRecipientId())))
                .andExpect(jsonPath("$[1].userSenderId", is(message2.getUserSenderId())))
                .andExpect(jsonPath("$[1].text", is(message2.getText())));
        
        // проверяем, что контроллер вызвал команду с правильными аргументами
        verify(getNewestMessagesCommandMock).execute(userId);
    }

Тестируемый метод:

    @RequestMapping(value = "/message/{userId}", method = RequestMethod.GET)
    public @ResponseBody List newMessages(@PathVariable Integer userId) {
        return getNewestMessagesCommand.execute(userId);
    }

Тест результата в формате JSON (C#)
При вызове тестируемого метода проверим формат результата, а данные для проверки получим приведением типа.

        [TestMethod]
        public void TestNewMessages()
        {
            // инициализация исходных данных
            const int userId = 111;
            var message1 = new MessageModel {Text = "текст 1", UserRecipientId = userId, UserSenderId = 222};
            var message2 = new MessageModel {Text = "текст 2", UserRecipientId = userId, UserSenderId = 333};
            // настройка мока команды getNewestMessagesCommand
            getNewestMessagesCommandMock.Setup(c => c.Execute(userId))
                .Returns(new[] {message1, message2})
                .Verifiable();
            // вызов метода контроллера с приведением типа
            var result = controller.NewMessages(userId) as JsonResult;
            Assert.IsNotNull(result);
            // актуальные сообщения получаем приведением типа
            var actualMessages = result.Data as MessageModel[];
            // проверка возвращенного контента
            Assert.AreEqual(2, actualMessages.Length);
            Assert.AreEqual(message1.Text, actualMessages[0].Text);
            Assert.AreEqual(message1.UserSenderId, actualMessages[0].UserSenderId);
            Assert.AreEqual(message1.UserRecipientId, actualMessages[0].UserRecipientId);
            Assert.AreEqual(message2.Text, actualMessages[1].Text);
            Assert.AreEqual(message2.UserSenderId, actualMessages[1].UserSenderId);
            Assert.AreEqual(message2.UserRecipientId, actualMessages[1].UserRecipientId);
            // проверяем, что контроллер вызвал команду с правильными аргументами
            getNewestMessagesCommandMock.Verify();
        }

Тестируемый метод:

        [HttpGet]
        public ActionResult NewMessages(Int32 userId)
        {
            var messages = getNewestMessagesCommand.Execute(userId);
            return Json(messages);
        }

Тест GET-запроса HTML-страницы с проверкой модели (Java)
Для тестирования метода, который возвращает связанный с моделью вид, есть возможность проверки имени файла вида и атрибутов модели:

@RunWith(MockitoJUnitRunner.class)
public class HomeControllerTest {

    private MockMvc mockMvc;
    private final HomeController controller = new HomeController();

    @Before
    public void setUp() {
        mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
    }

    @Test
    public void testIndex() throws Exception {
        mockMvc.perform(get("/home"))
                .andExpect(status().isOk())
                .andExpect(view().name("index")) // проверка имени view, котрый вернул action
                .andExpect(model().attribute("foo", "bar")); // проверка атрибута модели
    }
}

Тестируемый метод:

@Controller
public class HomeController {

    @RequestMapping(value = "/home", method = RequestMethod.GET)
    public String index(Model model) {
        model.addAttribute("foo", "bar");
        return "index";
    }
}

Тест GET-запроса HTML-страницы с проверкой модели (C#)

    [TestClass]
    public class HomeControllerTest
    {
         [TestMethod]
         public void TestIndex()
         {
             var controller = new HomeController();
             var result = controller.Index() as ViewResult;
             Assert.IsNotNull(result);
             Assert.AreEqual(string.Empty, result.ViewName);
             var model = result.ViewData.Model as IndexModel;
             Assert.IsNotNull(model);
             Assert.AreEqual("Bar", model.Foo);
         }
    }

Тестируемый метод:

    public class HomeController : Controller
    {
        public ActionResult Index()
        {
            return View(new IndexModel { Foo = "Bar"});
        }
    }