В рамках тестирования были написаны 5 модульных тестов.
Один из тестов был создан для сервиса FilmsService, а остальные для UsersService.
Тест для сервиса FilmsService проверяет, что метод ShowUserFilmRate получения рейтинга, поставленного пользователем для фильма возвращает -1 в случае, если пользователь еще не ставил оценку фильму.
Листинг 3. Тестирование метода ShowUserFilmRate
[Fact]
public void ShowUserFilmRate_UserDoesntHaveFilmRate_ReturnsMinus1()
{
var options = new DbContextOptionsBuilder<FilmsDbContext>()
.UseInMemoryDatabase(databaseName: "FilmsTest")
.Options;
using (var context = new FilmsDbContext(options))
{
var mockHelper = new Mock<IFilmsHelper>();
var mockBlob = new Mock<IBlobService>();
var mockEnv = new Mock<IHostingEnvironment>();
var service = new FilmsService(context, mockHelper.Object, mockBlob.Object, mockEnv.Object);
var rateModel = new RateModel()
{
Username = "test",
FilmId = 1
};
var result = service.GetUserFilmRate(rateModel);
Assert.Equal(-1, result);
}
}
Как видно из листинга 3 для «заглушки» вспомогательных сервисов используется библиотека Moq, которая позволяет создавать mock-объекты.
Тесты для сервиса UsersService проверяют следующее:
1. действительно ли создается новый пользователь в базе данных при попытке сделать это методом SignUp (тест SignUp_TestUserCreated_ReturnUserModel);
2. действительно ли создается новый пароль в базе данных при регистрации пользователя методом SignUp (тест SignUp_TestPasswordCreated_ReturnUserModel);
3. правильно ли возвращается логин пользователя при авторизации пользователя методом Login (тест Login_TestUsername_UsernameEqual);
4. возвращается ли результат null при попытке авторизации методом Login с неправильным паролем (тест Login_TestWrongPassword_ReturnNull).
Листинг тестов сервиса UsersService представлен в приложении Г.
Заключение
В результате работы было создано веб-приложение по типу SPA, которое позволяет просматривать, добавлять и оценивать фильмы.
В рамках работы была создана диаграмма Ганта. По результатам планирования разработка должна была занять 81 день.
В начале работы были написаны функциональные требования, в которых были разделены роли авторизованного пользователя и неавторизованного. Согласно этим требованиям были построены диаграмма вариантов использования, диаграммы последовательности и диаграммы деятельности. Затем была создана база данных, состоящая из 15 таблиц, которая позволила реализовать задуманную идею приложения. Затем была создана диаграмма классов, по которой был создан проект веб-приложения.
Для создания веб-приложения использовалась платформа ASP.Net Core. В процессе выбора фреймворка для клиентской части приложения было определено, что из рассмотренных вариантов для данной работы лучше всех подходит VueJs, так как с ним есть опыт работы и в нем предоставляется достаточное количество готовых компонентов.
Для лучшего понимания поставленных задач для разработки приложения были созданы макеты страниц, демонстрирующие необходимые для реализации функции.
В результате были написаны как серверная часть приложения, так и клиентская. Кроме того, были написаны модульные тесты, которые проверяют корректность работы ряда методов.
Веб-приложение было опубликовано на портале Microsoft Azure по ссылке https://filmsapp20181201063717.azurewebsites.net.
Список литературы
1. Metanit.com | Введение в Web API 2. [Электронный ресурс]. // 2012–2018. URL: https://metanit.com/sharp/aspnet_webapi/1.1.php (Дата обращения 30.11.2018).
2. Metanit.com | Введение в ASP.NET Core. [Электронный ресурс]. // 2012–2018. URL: https://metanit.com/sharp/aspnet5/1.1.php (Дата обращения 30.11.2018).
3. JavaScript.ru | Введение в JavaScript. [Электронный ресурс]. // 2007-2018 Илья Кантор. URL: https://learn.javascript.ru/intro (Дата обращения 30.11.2018).
4. Vue.js | Введение. [Электронный ресурс]. // 2018. URL: https://ru.vuejs.org/v2/guide/ (Дата обращения 30.11.2018).
5. Microsoft Azure | Платформа и службы облачных вычислений [Электронный ресурс]. // 2018. URL: https://azure.microsoft.com/(Дата обращения 30.11.2018).
Приложение А
В приложении представлена диаграмма Ганта.
Рисунок А.1. Диаграмма Ганта
Приложение Б
Таблица Б.1 – Описание базы данных
Поле | Значение | Тип данных | Разрешено значение NULL | Ограничения и комментарии |
Таблица Category – допустимые жанры фильма | ||||
categoryId | Идентификатор жанра | bigint | not NULL | - |
name | Название жанра | nvarchar(50) | not NULL | - |
Таблица ClientFilmAdded – фильмы, которые загрузил автор | ||||
filmId | Идентификатор фильма | bigint | not NULL | - |
username | Уникальное имя пользователя | nvarchar(50) | not NULL | |
status | Одобрен фильм или нет | bool | not NULL | false – если находится на рассмотрении; true – если одобрен; если фильм отклонен, он удаляется из базы данных |
Продолжение таблицы Б.1
Таблица ClientFilmRate – оценка фильма, установленная пользователем | ||||
filmId | Идентификатор фильма | bigint | not NULL | - |
username | Уникальное имя пользователя | nvarchar(50) | not NULL | - |
rate | Оценка фильму, оставленная пользователем | float | not NULL | - |
Продолжение таблицы Б.1
date | Дата последнего просмотра фильма | date | not NULL | - |
Country – доступные страны | ||||
countryId | Идентификатор страны | bigint | not NULL | - |
name | Название страны | nvarchar(50) | not NULL | - |
Film – данные о фильме | ||||
filmId | Идентификатор фильма | bigint | not NULL | - |
name | Название фильма | nvarchar(50) | not NULL | - |
agePermition | Возрастное ограничение | int | not NULL | Может принимать значения: 0, 6, 12, 16, 18, 21 |
authorLink | Ссылка для связи с автором (например, его соц. сеть) | nvarchar(150) | not NULL | - |
cameraMan | Идентификатор оператора (FilmParticipant) | bigint | not NULL | - |
Продолжение таблицы Б.1
editedBy | Идентификатор монтажера (FilmParticipant) | bigint | not NULL | - |
musicBy | Индентификатор композитора (FilmParticipant) | bigint | not NULL | - |
producer | Индентификатор продюсера (FilmParticipant) | bigint | not NULL | - |
scriptWriter | Индентификатор сценариста (FilmParticipant) | bigint | not NULL | - |
stageManager | Индентификатор режиссера (FilmParticipant) | bigint | not NULL | - |
minuteDuration | Длительность фильма в минутах | int | not NULL | - |
productionYear | Год производства | int | not NULL | - |
videoFile | Ссылка на видеофайл в облачном хранилище | nvarchar(128) | not NULL | - |
promoFile | Ссылка на файл с постером в облачном хранилище | nvarchar(128) | not NULL | - |
Продолжение таблицы Б.1
date | Дата добавления фильма | date | not NULL | - |
FilmActor – актеры фильма (FilmParticipant) | ||||
filmId | Идентификатор фильма | bigint | not NULL | - |
actorId | Идентификатор актера | bigint | not NULL | - |
FilmCategory – жанр фильма | ||||
filmId | Идентификатор фильма | bigint | not NULL | - |
categoryId | Идентификатор жанра | bigint | not NULL | - |
FilmCountry– страна фильма | ||||
filmId | Идентификатор фильма | bigint | not NULL | - |
countryId | Идентификатор страны | bigint | not NULL | - |
Продолжение таблицы Б.1
FilmParticipant– участник фильма | ||||
participantId | Идентификатор участника | bigint | not NULL | - |
firstName | Имя | nvarchar(50) | not NULL | - |
lastName | Фамилия | nvarchar(50) | not NULL | - |
fatherName | Отчество | nvarchar(50) | NULL | - |
User – пользователь | ||||
username | Уникальное имя пользователя | nvarchar(50) | not NULL | - |
firstName | Имя | nvarchar(50) | not NULL | - |
lastName | Фамилия | nvarchar(50) | not NULL | - |
fatherName | Отчество | nvarchar(50) | NULL | - |
birthDate | Дата рождения | date | not NULL | - |
connectionLink | Ссылка для связи (например, соц. сеть) | nvarchar(50) | not NULL | - |
Окончание таблицы Б.1
Электронная почта | nvarchar(50) | not NULL | Должна соответствовать правилам написания почтового адреса | |
role | Роль пользователя | int | not NULL | 1 – обычный пользователь, 2 – администратор |
sex | Пол пользователя | nvarchar(50) | not NULL | “м” – мужской, “ж” - женский |
UserPassword – пароль пользователя | ||||
username | Уникальное имя пользователя | nvarchar(50) | not NULL | - |
password | пароль | nvarchar(50) | not NULL | Не менее 8 символов, должен состоять из латинских букв и цифр |
Приложение В
Листинг В.1. Обработка сервисом запроса на добавление фильма
public bool AddFilm(FilmModel filmModel)
{
bool filmAdded = false;
var username = filmModel.Username;
var authorLink = dbContext.User.Find(username).ConnectionLink;
var videoReference = СreateReferenceForBlob(username, filmModel.FilmId, filmModel.Name, filmModel.Date);
blobService.UploadBlobVideo(videoReference, filmModel.VideoFileForm);
var film = new Film()
{
AgePermition = filmModel.AgePermition,
AuthorLink = authorLink,
CameraMan = filmsHepler.FindFilmParticipentId(filmModel.NameCameraManArray),
EditedBy = filmsHepler.FindFilmParticipentId(filmModel.NameEditedByArray),
MinuteDuration = filmModel.MinuteDuration,
MusicBy = filmsHepler.FindFilmParticipentId(filmModel.NameMusicByArray),
Name = filmModel.Name,
Producer = filmsHepler.FindFilmParticipentId(filmModel.NameProducerArray),
ProductionYear = filmModel.ProductionYear,
ScriptWriter = filmsHepler.FindFilmParticipentId(filmModel.NameScriptWriterArray),
StageManager = filmsHepler.FindFilmParticipentId(filmModel.NameStageManagerArray),
VideoFile = videoReference,
Date = DateTime.Now,
Rate = 0,
CountRated = 0
};
var filmCreated = dbContext.Add(film);
dbContext.SaveChanges();
if(filmCreated!= null)
{
bool categoriesCreated = true;
foreach(var categoryId in filmModel.CategoriesArray)
{
var filmCategory = new FilmCategory()
{
FilmId = filmCreated.Entity.Id,
CategoryId = categoryId
};
var categoryAdded = dbContext.FilmCategory.Add(filmCategory);
if(categoryAdded == null)
{
categoriesCreated = false;
}
}
bool actorsCreated = true;
foreach (var actor in filmModel.ActorsArray)
{
var filmActor = new FilmActor()
{
FilmId = filmCreated.Entity.Id,
ActorId = filmsHepler.FindFilmParticipentId(actor)
};
var actorAdded = dbContext.FilmActor.Add(filmActor);
if (actorAdded == null)
{
actorsCreated = false;
}
}
bool countriesCreated = true;
foreach (var country in filmModel.CountriesArray)
{
var filmCountry = new FilmCountry()
{
FilmId = filmCreated.Entity.Id,
CountryId = country
};
var countryAdded = dbContext.FilmCountry.Add(filmCountry);
if (countryAdded == null)
{
countriesCreated = false;
}
}
if(categoriesCreated && actorsCreated && countriesCreated)
{
dbContext.SaveChanges();
var clientFilmAdded = new ClientFilmAdded()
{
FilmId = filmCreated.Entity.Id,
Username = username,
Status = "0"
};
var clientFilmCreated = dbContext.ClientFilmAdded.Add(clientFilmAdded);
dbContext.SaveChanges();
if(clientFilmCreated!= null)
{
filmAdded = true;
}
}
else
{
var filmToRemove = dbContext.Film.Find(filmCreated.Entity.Id);
dbContext.Film.Remove(filmToRemove);
dbContext.SaveChanges();
}
}
return filmAdded;
}
Приложение Г
Листинг Г.1. Тестирование сервиса UsersService
public class UsersServiceTests
{
[Fact]
public void Login_TestUsername_UsernameEqual()
{
var options = new DbContextOptionsBuilder<FilmsDbContext>()
.UseInMemoryDatabase(databaseName: "FilmsTest")
.Options;
using (var context = new FilmsDbContext(options))
{
var testString = "test";
var user = new User()
{
Username = testString,
BirthDate = DateTime.Now,
ConnectionLink = testString,
FatherName = testString,
FirstName = testString,
LastName = testString,
Mail = testString,
Sex = "м",
Role = "1"
};
context.User.Add(user);
context.SaveChanges();
var userPassword = new UserPassword()
{
Username = testString,
Password = testString
};
context.UserPassword.Add(userPassword);
context.SaveChanges();
var loginModel = new LoginModel()
{
Username = testString,
Password = testString
};
var service = new UserService(context);
var result = service.Login(loginModel);
Assert.Equal(loginModel.Username, result.Username);
}
}
[Fact]
public void Login_TestWrongPassword_ReturnNull()
{
var options = new DbContextOptionsBuilder<FilmsDbContext>()
.UseInMemoryDatabase(databaseName: "FilmsTest")
.Options;
using (var context = new FilmsDbContext(options))
{
var testString = "test";
var user = new User()
{
Username = testString,
BirthDate = DateTime.Now,
ConnectionLink = testString,
FatherName = testString,
FirstName = testString,
LastName = testString,
Mail = testString,
Sex = "м",
Role = "1"
};
context.User.Add(user);
context.SaveChanges();
var userPassword = new UserPassword()
{
Username = testString,
Password = testString
};
context.UserPassword.Add(userPassword);
context.SaveChanges();
var loginModel = new LoginModel()
{
Username = testString,
Password = "test123pass"
};
var service = new UserService(context);
var result = service.Login(loginModel);
Assert.Null(result);
}
}
[Fact]
public void SignUp_TestUserCreated_ReturnUserModel()
{
var options = new DbContextOptionsBuilder<FilmsDbContext>()
.UseInMemoryDatabase(databaseName: "FilmsTest")
.Options;
using (var context = new FilmsDbContext(options))
{
var testString = "test";
var signUpModel = new SignUpModel()
{
Username = testString,
BirthDate = DateTime.Now,
ConnectionLink = testString,
FatherName = testString,
FirstName = testString,
LastName = testString,
Mail = testString,
Sex = "м",
Password = testString
};
var service = new UserService(context);
var userModel = service.SignUp(signUpModel);
var result = context.User.Find(signUpModel.Username);
Assert.NotNull(result);
}
}
[Fact]
public void SignUp_TestPasswordCreated_ReturnUserModel()
{
var options = new DbContextOptionsBuilder<FilmsDbContext>()
.UseInMemoryDatabase(databaseName: "FilmsTest")
.Options;
using (var context = new FilmsDbContext(options))
{
var testString = "test";
var signUpModel = new SignUpModel()
{
Username = testString,
BirthDate = DateTime.Now,
ConnectionLink = testString,
FatherName = testString,
FirstName = testString,
LastName = testString,
Mail = testString,
Sex = "м",
Password = testString
};
var service = new UserService(context);
var userModel = service.SignUp(signUpModel);
var result = context.UserPassword.Find(signUpModel.Username);
Assert.NotNull(result);
}
}
}