Logika pengontrol pengujian unit di ASP.NET Core
Oleh Steve Smith
Pengujian unit melibatkan pengujian bagian dari aplikasi dalam isolasi dari infrastruktur dan dependensinya. Saat logika pengontrol pengujian unit, hanya konten tindakan tunggal yang diuji, bukan perilaku dependensinya atau kerangka kerja itu sendiri.
Pengontrol pengujian unit
Siapkan pengujian unit tindakan pengontrol untuk fokus pada perilaku pengontrol. Pengujian unit pengontrol menghindari skenario seperti filter, perutean, dan pengikatan model. Pengujian yang mencakup interaksi di antara komponen yang secara kolektif menanggapi permintaan ditangani oleh pengujian integrasi. Untuk informasi selengkapnya tentang pengujian integrasi, lihat Pengujian integrasi di ASP.NET Core.
Jika Anda menulis filter dan rute kustom, uji unit dalam isolasi, bukan sebagai bagian dari pengujian pada tindakan pengontrol tertentu.
Untuk menunjukkan pengujian unit pengontrol, tinjau pengontrol berikut di aplikasi sampel.
Melihat atau mengunduh kode sampel (cara mengunduh)
Pengontrol Home menampilkan daftar sesi curah otak dan memungkinkan pembuatan sesi curah otak baru dengan permintaan POST:
public class HomeController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public HomeController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index()
{
var sessionList = await _sessionRepository.ListAsync();
var model = sessionList.Select(session => new StormSessionViewModel()
{
Id = session.Id,
DateCreated = session.DateCreated,
Name = session.Name,
IdeaCount = session.Ideas.Count
});
return View(model);
}
public class NewSessionModel
{
[Required]
public string SessionName { get; set; }
}
[HttpPost]
public async Task<IActionResult> Index(NewSessionModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
await _sessionRepository.AddAsync(new BrainstormSession()
{
DateCreated = DateTimeOffset.Now,
Name = model.SessionName
});
}
return RedirectToAction(actionName: nameof(Index));
}
}
Pengontrol sebelumnya:
- Mengikuti Prinsip Dependensi Eksplisit.
- Mengharapkan injeksi dependensi (DI) untuk memberikan instans .
IBrainstormSessionRepository
- Dapat diuji dengan layanan tiruan
IBrainstormSessionRepository
menggunakan kerangka kerja objek tiruan, seperti Moq. Objek yang ditiru adalah objek fabrikasi dengan serangkaian perilaku properti dan metode yang telah ditentukan yang digunakan untuk pengujian. Untuk informasi selengkapnya, lihat Pengenalan pengujian integrasi.
Metode HTTP GET Index
ini tidak memiliki perulangan atau percabangan dan hanya memanggil satu metode. Pengujian unit untuk tindakan ini:
- Mengejek
IBrainstormSessionRepository
layanan menggunakan metode .GetTestSessions
GetTestSessions
membuat dua sesi curah gagasan tiruan dengan tanggal dan nama sesi. Index
Menjalankan metode .- Membuat pernyataan pada hasil yang dikembalikan oleh metode :
- ViewResult dikembalikan.
- ViewDataDictionary.Model adalah
StormSessionViewModel
. - Ada dua sesi curah otak yang disimpan di
ViewDataDictionary.Model
.
[Fact]
public async Task Index_ReturnsAViewResult_WithAListOfBrainstormSessions()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<StormSessionViewModel>>(
viewResult.ViewData.Model);
Assert.Equal(2, model.Count());
}
private List<BrainstormSession> GetTestSessions()
{
var sessions = new List<BrainstormSession>();
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 2),
Id = 1,
Name = "Test One"
});
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 1),
Id = 2,
Name = "Test Two"
});
return sessions;
}
Metode Home pengontrol HTTP POST Index
menguji bahwa:
- Ketika ModelState.IsValid adalah
false
, metode tindakan mengembalikan Permintaan ViewResult Buruk 400 dengan data yang sesuai. - Kapan
ModelState.IsValid
adalahtrue
:- Metode
Add
pada repositori dipanggil. - RedirectToActionResult dikembalikan dengan argumen yang benar.
- Metode
Status model yang tidak valid diuji dengan menambahkan kesalahan menggunakan AddModelError seperti yang ditunjukkan pada pengujian pertama di bawah ini:
[Fact]
public async Task IndexPost_ReturnsBadRequestResult_WhenModelStateIsInvalid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
controller.ModelState.AddModelError("SessionName", "Required");
var newSession = new HomeController.NewSessionModel();
// Act
var result = await controller.Index(newSession);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
[Fact]
public async Task IndexPost_ReturnsARedirectAndAddsSession_WhenModelStateIsValid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.AddAsync(It.IsAny<BrainstormSession>()))
.Returns(Task.CompletedTask)
.Verifiable();
var controller = new HomeController(mockRepo.Object);
var newSession = new HomeController.NewSessionModel()
{
SessionName = "Test Name"
};
// Act
var result = await controller.Index(newSession);
// Assert
var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
Assert.Null(redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
mockRepo.Verify();
}
Saat ModelState tidak valid, hal yang sama ViewResult
dikembalikan seperti untuk permintaan GET. Pengujian tidak mencoba untuk lulus dalam model yang tidak valid. Melewati model yang tidak valid bukan pendekatan yang valid, karena pengikatan model tidak berjalan (meskipun pengujian integrasi memang menggunakan pengikatan model). Dalam hal ini, pengikatan model tidak diuji. Pengujian unit ini hanya menguji kode dalam metode tindakan.
Pengujian kedua memverifikasi bahwa ketika ModelState
valid:
BrainstormSession
Baru ditambahkan (melalui repositori).- Metode mengembalikan dengan properti yang
RedirectToActionResult
diharapkan.
Panggilan yang ditiru yang tidak dipanggil biasanya diabaikan, tetapi panggilan Verifiable
di akhir panggilan penyiapan memungkinkan validasi tiruan dalam pengujian. Ini dilakukan dengan panggilan ke mockRepo.Verify
, yang gagal dalam pengujian jika metode yang diharapkan tidak dipanggil.
Catatan
Pustaka Moq yang digunakan dalam sampel ini memungkinkan untuk mencampur tiruan yang dapat diverifikasi, atau "ketat", tiruan dengan tiruan yang tidak dapat diverifikasi (juga disebut tiruan atau stub "longgar"). Pelajari selengkapnya tentang menyesuaikan perilaku Mock dengan Moq.
SessionController dalam aplikasi sampel menampilkan informasi yang terkait dengan sesi curah gagasan tertentu. Pengontrol mencakup logika untuk menangani nilai yang tidak valid id
(ada dua return
skenario dalam contoh berikut untuk mencakup skenario ini). Pernyataan akhir return
mengembalikan yang baru StormSessionViewModel
ke tampilan (Controllers/SessionController.cs
):
public class SessionController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public SessionController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index(int? id)
{
if (!id.HasValue)
{
return RedirectToAction(actionName: nameof(Index),
controllerName: "Home");
}
var session = await _sessionRepository.GetByIdAsync(id.Value);
if (session == null)
{
return Content("Session not found.");
}
var viewModel = new StormSessionViewModel()
{
DateCreated = session.DateCreated,
Name = session.Name,
Id = session.Id
};
return View(viewModel);
}
}
Pengujian unit mencakup satu pengujian untuk setiap return
skenario dalam tindakan Pengontrol Index
sesi:
[Fact]
public async Task IndexReturnsARedirectToIndexHomeWhenIdIsNull()
{
// Arrange
var controller = new SessionController(sessionRepository: null);
// Act
var result = await controller.Index(id: null);
// Assert
var redirectToActionResult =
Assert.IsType<RedirectToActionResult>(result);
Assert.Equal("Home", redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
}
[Fact]
public async Task IndexReturnsContentWithSessionNotFoundWhenSessionNotFound()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var contentResult = Assert.IsType<ContentResult>(result);
Assert.Equal("Session not found.", contentResult.Content);
}
[Fact]
public async Task IndexReturnsViewResultWithStormSessionViewModel()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSessions().FirstOrDefault(
s => s.Id == testSessionId));
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsType<StormSessionViewModel>(
viewResult.ViewData.Model);
Assert.Equal("Test One", model.Name);
Assert.Equal(2, model.DateCreated.Day);
Assert.Equal(testSessionId, model.Id);
}
Pindah ke pengontrol Ideas, aplikasi mengekspos fungsionalitas sebagai API web pada api/ideas
rute:
- Daftar ide (
IdeaDTO
) yang terkait dengan sesi curah gagasan dikembalikan olehForSession
metode . - Metode ini
Create
menambahkan ide baru ke sesi.
[HttpGet("forsession/{sessionId}")]
public async Task<IActionResult> ForSession(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return Ok(result);
}
[HttpPost("create")]
public async Task<IActionResult> Create([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return Ok(session);
}
Hindari mengembalikan entitas domain bisnis secara langsung melalui panggilan API. Entitas domain:
- Sering kali menyertakan lebih banyak data daripada yang dibutuhkan klien.
- Tidak perlu menggabungkan model domain internal aplikasi dengan API yang diekspos secara publik.
Pemetaan antara entitas domain dan jenis yang dikembalikan ke klien dapat dilakukan:
- Secara manual dengan LINQ
Select
, seperti yang digunakan aplikasi sampel. Untuk informasi selengkapnya, lihat LINQ (Kueri Terintegrasi Bahasa). - Secara otomatis dengan pustaka, seperti AutoMapper.
Selanjutnya, aplikasi sampel menunjukkan pengujian unit untuk Create
metode API dan ForSession
pengontrol Ideas.
Aplikasi sampel berisi dua ForSession
pengujian. Pengujian pertama menentukan apakah ForSession
mengembalikan NotFoundObjectResult (HTTP Not Found) untuk sesi yang tidak valid:
[Fact]
public async Task ForSession_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var notFoundObjectResult = Assert.IsType<NotFoundObjectResult>(result);
Assert.Equal(testSessionId, notFoundObjectResult.Value);
}
Pengujian kedua ForSession
menentukan apakah ForSession
mengembalikan daftar ide sesi (<List<IdeaDTO>>
) untuk sesi yang valid. Pemeriksaan juga memeriksa ide pertama untuk mengonfirmasi propertinya Name
benar:
[Fact]
public async Task ForSession_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(okResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Untuk menguji perilaku Create
metode ketika ModelState
tidak valid, aplikasi sampel menambahkan kesalahan model ke pengontrol sebagai bagian dari pengujian. Jangan mencoba menguji validasi model atau pengikatan model dalam pengujian unit—cukup uji perilaku metode tindakan saat dihadapkan dengan yang tidak valid ModelState
:
[Fact]
public async Task Create_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.Create(model: null);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
}
Pengujian Create
kedua tergantung pada repositori yang mengembalikan null
, sehingga repositori tiruan dikonfigurasi untuk mengembalikan null
. Tidak perlu membuat database pengujian (dalam memori atau sebaliknya) dan membuat kueri yang mengembalikan hasil ini. Pengujian dapat dilakukan dalam satu pernyataan, seperti yang diilustrasikan kode sampel:
[Fact]
public async Task Create_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.Create(new NewIdeaModel());
// Assert
Assert.IsType<NotFoundObjectResult>(result);
}
Pengujian ketiga Create
, Create_ReturnsNewlyCreatedIdeaForSession
, memverifikasi bahwa metode repositori UpdateAsync
dipanggil. Tiruan dipanggil dengan Verifiable
, dan metode repositori Verify
yang ditiru dipanggil untuk mengonfirmasi metode yang dapat diverifikasi dijalankan. Bukan tanggung jawab pengujian unit untuk memastikan bahwa UpdateAsync
metode menyimpan data—yang dapat dilakukan dengan pengujian integrasi.
[Fact]
public async Task Create_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.Create(newIdea);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnSession = Assert.IsType<BrainstormSession>(okResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnSession.Ideas.Count());
Assert.Equal(testName, returnSession.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnSession.Ideas.LastOrDefault().Description);
}
Uji ActionResult<T>
ActionResult<T> (ActionResult<TValue>) dapat mengembalikan jenis yang berasal dari ActionResult
atau mengembalikan jenis tertentu.
Aplikasi sampel menyertakan metode yang mengembalikan List<IdeaDTO>
untuk sesi id
tertentu . Jika sesi id
tidak ada, pengontrol mengembalikan NotFound:
[HttpGet("forsessionactionresult/{sessionId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<ActionResult<List<IdeaDTO>>> ForSessionActionResult(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return result;
}
Dua pengujian ForSessionActionResult
pengontrol disertakan dalam ApiIdeasControllerTests
.
Pengujian pertama mengonfirmasi bahwa pengontrol mengembalikan ActionResult
tetapi bukan daftar ide yang tidak ada untuk sesi id
yang tidak ada :
- Jenisnya
ActionResult
adalahActionResult<List<IdeaDTO>>
. - Result adalah .NotFoundObjectResult
[Fact]
public async Task ForSessionActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var nonExistentSessionId = 999;
// Act
var result = await controller.ForSessionActionResult(nonExistentSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi id
yang valid , pengujian kedua mengonfirmasi bahwa metode mengembalikan:
- Dengan
ActionResult
List<IdeaDTO>
jenis. - ActionResult <T>. Nilai adalah
List<IdeaDTO>
jenis. - Item pertama dalam daftar adalah ide yang valid yang cocok dengan ide yang disimpan dalam sesi tiruan (diperoleh dengan memanggil
GetTestSession
).
[Fact]
public async Task ForSessionActionResult_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSessionActionResult(testSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(actionResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Aplikasi sampel juga menyertakan metode untuk membuat baru Idea
untuk sesi tertentu. Pengontrol mengembalikan:
- BadRequest untuk model yang tidak valid.
- NotFound jika sesi tidak ada.
- CreatedAtAction ketika sesi diperbarui dengan ide baru.
[HttpPost("createactionresult")]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
public async Task<ActionResult<BrainstormSession>> CreateActionResult([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return CreatedAtAction(nameof(CreateActionResult), new { id = session.Id }, session);
}
Tiga pengujian CreateActionResult
disertakan dalam ApiIdeasControllerTests
.
Teks pertama mengonfirmasi bahwa dikembalikan BadRequest untuk model yang tidak valid.
[Fact]
public async Task CreateActionResult_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.CreateActionResult(model: null);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
Pengujian kedua memeriksa bahwa sebuah NotFound dikembalikan jika sesi tidak ada.
[Fact]
public async Task CreateActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var nonExistentSessionId = 999;
string testName = "test name";
string testDescription = "test description";
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = nonExistentSessionId
};
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi id
yang valid , pengujian akhir mengonfirmasi bahwa:
- Metode mengembalikan
ActionResult
denganBrainstormSession
jenis. - ActionResult <T>. Hasilnya adalah CreatedAtActionResult.
CreatedAtActionResult
dianalogikan dengan respons 201 Dibuat denganLocation
header. - ActionResult <T>. Nilai adalah
BrainstormSession
jenis. - Panggilan tiruan untuk memperbarui sesi,
UpdateAsync(testSession)
, dipanggil. PanggilanVerifiable
metode diperiksa dengan menjalankanmockRepo.Verify()
dalam pernyataan. - Dua
Idea
objek dikembalikan untuk sesi tersebut. - Item terakhir (yang
Idea
ditambahkan oleh panggilan tiruan keUpdateAsync
) cocok dengan yangnewIdea
ditambahkan ke sesi dalam pengujian.
[Fact]
public async Task CreateActionResult_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
var createdAtActionResult = Assert.IsType<CreatedAtActionResult>(actionResult.Result);
var returnValue = Assert.IsType<BrainstormSession>(createdAtActionResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnValue.Ideas.Count());
Assert.Equal(testName, returnValue.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnValue.Ideas.LastOrDefault().Description);
}
Pengontrol memainkan peran terpusat dalam aplikasi MVC Core ASP.NET apa pun. Dengan demikian, Anda harus memiliki keyakinan bahwa pengontrol bereaksi seperti yang diinginkan. Pengujian otomatis dapat mendeteksi kesalahan sebelum aplikasi disebarkan ke lingkungan produksi.
Melihat atau mengunduh kode sampel (cara mengunduh)
Pengujian unit logika pengontrol
Pengujian unit melibatkan pengujian bagian dari aplikasi dalam isolasi dari infrastruktur dan dependensinya. Saat logika pengontrol pengujian unit, hanya konten tindakan tunggal yang diuji, bukan perilaku dependensinya atau kerangka kerja itu sendiri.
Siapkan pengujian unit tindakan pengontrol untuk fokus pada perilaku pengontrol. Pengujian unit pengontrol menghindari skenario seperti filter, perutean, dan pengikatan model. Pengujian yang mencakup interaksi di antara komponen yang secara kolektif menanggapi permintaan ditangani oleh pengujian integrasi. Untuk informasi selengkapnya tentang pengujian integrasi, lihat Pengujian integrasi di ASP.NET Core.
Jika Anda menulis filter dan rute kustom, uji unit dalam isolasi, bukan sebagai bagian dari pengujian pada tindakan pengontrol tertentu.
Untuk menunjukkan pengujian unit pengontrol, tinjau pengontrol berikut di aplikasi sampel. Pengontrol Home menampilkan daftar sesi curah otak dan memungkinkan pembuatan sesi curah otak baru dengan permintaan POST:
public class HomeController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public HomeController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index()
{
var sessionList = await _sessionRepository.ListAsync();
var model = sessionList.Select(session => new StormSessionViewModel()
{
Id = session.Id,
DateCreated = session.DateCreated,
Name = session.Name,
IdeaCount = session.Ideas.Count
});
return View(model);
}
public class NewSessionModel
{
[Required]
public string SessionName { get; set; }
}
[HttpPost]
public async Task<IActionResult> Index(NewSessionModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
else
{
await _sessionRepository.AddAsync(new BrainstormSession()
{
DateCreated = DateTimeOffset.Now,
Name = model.SessionName
});
}
return RedirectToAction(actionName: nameof(Index));
}
}
Pengontrol sebelumnya:
- Mengikuti Prinsip Dependensi Eksplisit.
- Mengharapkan injeksi dependensi (DI) untuk memberikan instans .
IBrainstormSessionRepository
- Dapat diuji dengan layanan tiruan
IBrainstormSessionRepository
menggunakan kerangka kerja objek tiruan, seperti Moq. Objek yang ditiru adalah objek fabrikasi dengan serangkaian perilaku properti dan metode yang telah ditentukan yang digunakan untuk pengujian. Untuk informasi selengkapnya, lihat Pengenalan pengujian integrasi.
Metode HTTP GET Index
ini tidak memiliki perulangan atau percabangan dan hanya memanggil satu metode. Pengujian unit untuk tindakan ini:
- Mengejek
IBrainstormSessionRepository
layanan menggunakan metode .GetTestSessions
GetTestSessions
membuat dua sesi curah gagasan tiruan dengan tanggal dan nama sesi. Index
Menjalankan metode .- Membuat pernyataan pada hasil yang dikembalikan oleh metode :
- ViewResult dikembalikan.
- ViewDataDictionary.Model adalah
StormSessionViewModel
. - Ada dua sesi curah otak yang disimpan di
ViewDataDictionary.Model
.
[Fact]
public async Task Index_ReturnsAViewResult_WithAListOfBrainstormSessions()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
// Act
var result = await controller.Index();
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsAssignableFrom<IEnumerable<StormSessionViewModel>>(
viewResult.ViewData.Model);
Assert.Equal(2, model.Count());
}
private List<BrainstormSession> GetTestSessions()
{
var sessions = new List<BrainstormSession>();
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 2),
Id = 1,
Name = "Test One"
});
sessions.Add(new BrainstormSession()
{
DateCreated = new DateTime(2016, 7, 1),
Id = 2,
Name = "Test Two"
});
return sessions;
}
Metode Home pengontrol HTTP POST Index
menguji bahwa:
- Ketika ModelState.IsValid adalah
false
, metode tindakan mengembalikan Permintaan ViewResult Buruk 400 dengan data yang sesuai. - Kapan
ModelState.IsValid
adalahtrue
:- Metode
Add
pada repositori dipanggil. - RedirectToActionResult dikembalikan dengan argumen yang benar.
- Metode
Status model yang tidak valid diuji dengan menambahkan kesalahan menggunakan AddModelError seperti yang ditunjukkan pada pengujian pertama di bawah ini:
[Fact]
public async Task IndexPost_ReturnsBadRequestResult_WhenModelStateIsInvalid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.ListAsync())
.ReturnsAsync(GetTestSessions());
var controller = new HomeController(mockRepo.Object);
controller.ModelState.AddModelError("SessionName", "Required");
var newSession = new HomeController.NewSessionModel();
// Act
var result = await controller.Index(newSession);
// Assert
var badRequestResult = Assert.IsType<BadRequestObjectResult>(result);
Assert.IsType<SerializableError>(badRequestResult.Value);
}
[Fact]
public async Task IndexPost_ReturnsARedirectAndAddsSession_WhenModelStateIsValid()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.AddAsync(It.IsAny<BrainstormSession>()))
.Returns(Task.CompletedTask)
.Verifiable();
var controller = new HomeController(mockRepo.Object);
var newSession = new HomeController.NewSessionModel()
{
SessionName = "Test Name"
};
// Act
var result = await controller.Index(newSession);
// Assert
var redirectToActionResult = Assert.IsType<RedirectToActionResult>(result);
Assert.Null(redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
mockRepo.Verify();
}
Saat ModelState tidak valid, hal yang sama ViewResult
dikembalikan seperti untuk permintaan GET. Pengujian tidak mencoba untuk lulus dalam model yang tidak valid. Melewati model yang tidak valid bukan pendekatan yang valid, karena pengikatan model tidak berjalan (meskipun pengujian integrasi memang menggunakan pengikatan model). Dalam hal ini, pengikatan model tidak diuji. Pengujian unit ini hanya menguji kode dalam metode tindakan.
Pengujian kedua memverifikasi bahwa ketika ModelState
valid:
BrainstormSession
Baru ditambahkan (melalui repositori).- Metode mengembalikan dengan properti yang
RedirectToActionResult
diharapkan.
Panggilan yang ditiru yang tidak dipanggil biasanya diabaikan, tetapi panggilan Verifiable
di akhir panggilan penyiapan memungkinkan validasi tiruan dalam pengujian. Ini dilakukan dengan panggilan ke mockRepo.Verify
, yang gagal dalam pengujian jika metode yang diharapkan tidak dipanggil.
Catatan
Pustaka Moq yang digunakan dalam sampel ini memungkinkan untuk mencampur tiruan yang dapat diverifikasi, atau "ketat", tiruan dengan tiruan yang tidak dapat diverifikasi (juga disebut tiruan atau stub "longgar"). Pelajari selengkapnya tentang menyesuaikan perilaku Mock dengan Moq.
SessionController dalam aplikasi sampel menampilkan informasi yang terkait dengan sesi curah gagasan tertentu. Pengontrol mencakup logika untuk menangani nilai yang tidak valid id
(ada dua return
skenario dalam contoh berikut untuk mencakup skenario ini). Pernyataan akhir return
mengembalikan yang baru StormSessionViewModel
ke tampilan (Controllers/SessionController.cs
):
public class SessionController : Controller
{
private readonly IBrainstormSessionRepository _sessionRepository;
public SessionController(IBrainstormSessionRepository sessionRepository)
{
_sessionRepository = sessionRepository;
}
public async Task<IActionResult> Index(int? id)
{
if (!id.HasValue)
{
return RedirectToAction(actionName: nameof(Index),
controllerName: "Home");
}
var session = await _sessionRepository.GetByIdAsync(id.Value);
if (session == null)
{
return Content("Session not found.");
}
var viewModel = new StormSessionViewModel()
{
DateCreated = session.DateCreated,
Name = session.Name,
Id = session.Id
};
return View(viewModel);
}
}
Pengujian unit mencakup satu pengujian untuk setiap return
skenario dalam tindakan Pengontrol Index
sesi:
[Fact]
public async Task IndexReturnsARedirectToIndexHomeWhenIdIsNull()
{
// Arrange
var controller = new SessionController(sessionRepository: null);
// Act
var result = await controller.Index(id: null);
// Assert
var redirectToActionResult =
Assert.IsType<RedirectToActionResult>(result);
Assert.Equal("Home", redirectToActionResult.ControllerName);
Assert.Equal("Index", redirectToActionResult.ActionName);
}
[Fact]
public async Task IndexReturnsContentWithSessionNotFoundWhenSessionNotFound()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var contentResult = Assert.IsType<ContentResult>(result);
Assert.Equal("Session not found.", contentResult.Content);
}
[Fact]
public async Task IndexReturnsViewResultWithStormSessionViewModel()
{
// Arrange
int testSessionId = 1;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSessions().FirstOrDefault(
s => s.Id == testSessionId));
var controller = new SessionController(mockRepo.Object);
// Act
var result = await controller.Index(testSessionId);
// Assert
var viewResult = Assert.IsType<ViewResult>(result);
var model = Assert.IsType<StormSessionViewModel>(
viewResult.ViewData.Model);
Assert.Equal("Test One", model.Name);
Assert.Equal(2, model.DateCreated.Day);
Assert.Equal(testSessionId, model.Id);
}
Pindah ke pengontrol Ideas, aplikasi mengekspos fungsionalitas sebagai API web pada api/ideas
rute:
- Daftar ide (
IdeaDTO
) yang terkait dengan sesi curah gagasan dikembalikan olehForSession
metode . - Metode ini
Create
menambahkan ide baru ke sesi.
[HttpGet("forsession/{sessionId}")]
public async Task<IActionResult> ForSession(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return Ok(result);
}
[HttpPost("create")]
public async Task<IActionResult> Create([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return Ok(session);
}
Hindari mengembalikan entitas domain bisnis secara langsung melalui panggilan API. Entitas domain:
- Sering kali menyertakan lebih banyak data daripada yang dibutuhkan klien.
- Tidak perlu menggabungkan model domain internal aplikasi dengan API yang diekspos secara publik.
Pemetaan antara entitas domain dan jenis yang dikembalikan ke klien dapat dilakukan:
- Secara manual dengan LINQ
Select
, seperti yang digunakan aplikasi sampel. Untuk informasi selengkapnya, lihat LINQ (Kueri Terintegrasi Bahasa). - Secara otomatis dengan pustaka, seperti AutoMapper.
Selanjutnya, aplikasi sampel menunjukkan pengujian unit untuk Create
metode API dan ForSession
pengontrol Ideas.
Aplikasi sampel berisi dua ForSession
pengujian. Pengujian pertama menentukan apakah ForSession
mengembalikan NotFoundObjectResult (HTTP Not Found) untuk sesi yang tidak valid:
[Fact]
public async Task ForSession_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var notFoundObjectResult = Assert.IsType<NotFoundObjectResult>(result);
Assert.Equal(testSessionId, notFoundObjectResult.Value);
}
Pengujian kedua ForSession
menentukan apakah ForSession
mengembalikan daftar ide sesi (<List<IdeaDTO>>
) untuk sesi yang valid. Pemeriksaan juga memeriksa ide pertama untuk mengonfirmasi propertinya Name
benar:
[Fact]
public async Task ForSession_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSession(testSessionId);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(okResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Untuk menguji perilaku Create
metode ketika ModelState
tidak valid, aplikasi sampel menambahkan kesalahan model ke pengontrol sebagai bagian dari pengujian. Jangan mencoba menguji validasi model atau pengikatan model dalam pengujian unit—cukup uji perilaku metode tindakan saat dihadapkan dengan yang tidak valid ModelState
:
[Fact]
public async Task Create_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.Create(model: null);
// Assert
Assert.IsType<BadRequestObjectResult>(result);
}
Pengujian Create
kedua tergantung pada repositori yang mengembalikan null
, sehingga repositori tiruan dikonfigurasi untuk mengembalikan null
. Tidak perlu membuat database pengujian (dalam memori atau sebaliknya) dan membuat kueri yang mengembalikan hasil ini. Pengujian dapat dilakukan dalam satu pernyataan, seperti yang diilustrasikan kode sampel:
[Fact]
public async Task Create_ReturnsHttpNotFound_ForInvalidSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync((BrainstormSession)null);
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.Create(new NewIdeaModel());
// Assert
Assert.IsType<NotFoundObjectResult>(result);
}
Pengujian ketiga Create
, Create_ReturnsNewlyCreatedIdeaForSession
, memverifikasi bahwa metode repositori UpdateAsync
dipanggil. Tiruan dipanggil dengan Verifiable
, dan metode repositori Verify
yang ditiru dipanggil untuk mengonfirmasi metode yang dapat diverifikasi dijalankan. Bukan tanggung jawab pengujian unit untuk memastikan bahwa UpdateAsync
metode menyimpan data—yang dapat dilakukan dengan pengujian integrasi.
[Fact]
public async Task Create_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.Create(newIdea);
// Assert
var okResult = Assert.IsType<OkObjectResult>(result);
var returnSession = Assert.IsType<BrainstormSession>(okResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnSession.Ideas.Count());
Assert.Equal(testName, returnSession.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnSession.Ideas.LastOrDefault().Description);
}
Uji ActionResult<T>
Di ASP.NET Core 2.1 atau yang lebih baru, ActionResult<T> (ActionResult<TValue>) memungkinkan Anda mengembalikan jenis yang berasal dari ActionResult
atau mengembalikan jenis tertentu.
Aplikasi sampel menyertakan metode yang mengembalikan List<IdeaDTO>
untuk sesi id
tertentu . Jika sesi id
tidak ada, pengontrol mengembalikan NotFound:
[HttpGet("forsessionactionresult/{sessionId}")]
[ProducesResponseType(200)]
[ProducesResponseType(404)]
public async Task<ActionResult<List<IdeaDTO>>> ForSessionActionResult(int sessionId)
{
var session = await _sessionRepository.GetByIdAsync(sessionId);
if (session == null)
{
return NotFound(sessionId);
}
var result = session.Ideas.Select(idea => new IdeaDTO()
{
Id = idea.Id,
Name = idea.Name,
Description = idea.Description,
DateCreated = idea.DateCreated
}).ToList();
return result;
}
Dua pengujian ForSessionActionResult
pengontrol disertakan dalam ApiIdeasControllerTests
.
Pengujian pertama mengonfirmasi bahwa pengontrol mengembalikan ActionResult
tetapi bukan daftar ide yang tidak ada untuk sesi id
yang tidak ada :
- Jenisnya
ActionResult
adalahActionResult<List<IdeaDTO>>
. - Result adalah .NotFoundObjectResult
[Fact]
public async Task ForSessionActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var nonExistentSessionId = 999;
// Act
var result = await controller.ForSessionActionResult(nonExistentSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi id
yang valid , pengujian kedua mengonfirmasi bahwa metode mengembalikan:
- Dengan
ActionResult
List<IdeaDTO>
jenis. - ActionResult <T>. Nilai adalah
List<IdeaDTO>
jenis. - Item pertama dalam daftar adalah ide yang valid yang cocok dengan ide yang disimpan dalam sesi tiruan (diperoleh dengan memanggil
GetTestSession
).
[Fact]
public async Task ForSessionActionResult_ReturnsIdeasForSession()
{
// Arrange
int testSessionId = 123;
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(GetTestSession());
var controller = new IdeasController(mockRepo.Object);
// Act
var result = await controller.ForSessionActionResult(testSessionId);
// Assert
var actionResult = Assert.IsType<ActionResult<List<IdeaDTO>>>(result);
var returnValue = Assert.IsType<List<IdeaDTO>>(actionResult.Value);
var idea = returnValue.FirstOrDefault();
Assert.Equal("One", idea.Name);
}
Aplikasi sampel juga menyertakan metode untuk membuat baru Idea
untuk sesi tertentu. Pengontrol mengembalikan:
- BadRequest untuk model yang tidak valid.
- NotFound jika sesi tidak ada.
- CreatedAtAction ketika sesi diperbarui dengan ide baru.
[HttpPost("createactionresult")]
[ProducesResponseType(201)]
[ProducesResponseType(400)]
[ProducesResponseType(404)]
public async Task<ActionResult<BrainstormSession>> CreateActionResult([FromBody]NewIdeaModel model)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var session = await _sessionRepository.GetByIdAsync(model.SessionId);
if (session == null)
{
return NotFound(model.SessionId);
}
var idea = new Idea()
{
DateCreated = DateTimeOffset.Now,
Description = model.Description,
Name = model.Name
};
session.AddIdea(idea);
await _sessionRepository.UpdateAsync(session);
return CreatedAtAction(nameof(CreateActionResult), new { id = session.Id }, session);
}
Tiga pengujian CreateActionResult
disertakan dalam ApiIdeasControllerTests
.
Teks pertama mengonfirmasi bahwa dikembalikan BadRequest untuk model yang tidak valid.
[Fact]
public async Task CreateActionResult_ReturnsBadRequest_GivenInvalidModel()
{
// Arrange & Act
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
controller.ModelState.AddModelError("error", "some error");
// Act
var result = await controller.CreateActionResult(model: null);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<BadRequestObjectResult>(actionResult.Result);
}
Pengujian kedua memeriksa bahwa sebuah NotFound dikembalikan jika sesi tidak ada.
[Fact]
public async Task CreateActionResult_ReturnsNotFoundObjectResultForNonexistentSession()
{
// Arrange
var nonExistentSessionId = 999;
string testName = "test name";
string testDescription = "test description";
var mockRepo = new Mock<IBrainstormSessionRepository>();
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = nonExistentSessionId
};
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
Assert.IsType<NotFoundObjectResult>(actionResult.Result);
}
Untuk sesi id
yang valid , pengujian akhir mengonfirmasi bahwa:
- Metode mengembalikan
ActionResult
denganBrainstormSession
jenis. - ActionResult <T>. Hasilnya adalah CreatedAtActionResult.
CreatedAtActionResult
dianalogikan dengan respons 201 Dibuat denganLocation
header. - ActionResult <T>. Nilai adalah
BrainstormSession
jenis. - Panggilan tiruan untuk memperbarui sesi,
UpdateAsync(testSession)
, dipanggil. PanggilanVerifiable
metode diperiksa dengan menjalankanmockRepo.Verify()
dalam pernyataan. - Dua
Idea
objek dikembalikan untuk sesi tersebut. - Item terakhir (yang
Idea
ditambahkan oleh panggilan tiruan keUpdateAsync
) cocok dengan yangnewIdea
ditambahkan ke sesi dalam pengujian.
[Fact]
public async Task CreateActionResult_ReturnsNewlyCreatedIdeaForSession()
{
// Arrange
int testSessionId = 123;
string testName = "test name";
string testDescription = "test description";
var testSession = GetTestSession();
var mockRepo = new Mock<IBrainstormSessionRepository>();
mockRepo.Setup(repo => repo.GetByIdAsync(testSessionId))
.ReturnsAsync(testSession);
var controller = new IdeasController(mockRepo.Object);
var newIdea = new NewIdeaModel()
{
Description = testDescription,
Name = testName,
SessionId = testSessionId
};
mockRepo.Setup(repo => repo.UpdateAsync(testSession))
.Returns(Task.CompletedTask)
.Verifiable();
// Act
var result = await controller.CreateActionResult(newIdea);
// Assert
var actionResult = Assert.IsType<ActionResult<BrainstormSession>>(result);
var createdAtActionResult = Assert.IsType<CreatedAtActionResult>(actionResult.Result);
var returnValue = Assert.IsType<BrainstormSession>(createdAtActionResult.Value);
mockRepo.Verify();
Assert.Equal(2, returnValue.Ideas.Count());
Assert.Equal(testName, returnValue.Ideas.LastOrDefault().Name);
Assert.Equal(testDescription, returnValue.Ideas.LastOrDefault().Description);
}
Sumber Daya Tambahan:
- Pengujian integrasi di ASP.NET Core
- Membuat dan menjalankan pengujian unit dengan Visual Studio
- MyTested.AspNetCore.Mvc - Pustaka Pengujian Fasih untuk ASP.NET Core MVC: Pustaka pengujian unit dengan jenis kuat, menyediakan antarmuka yang fasih untuk menguji aplikasi MVC dan API web. (Tidak dipertahankan atau didukung oleh Microsoft.)
- JustMockLite: Kerangka kerja tiruan untuk pengembang .NET. (Tidak dipertahankan atau didukung oleh Microsoft.)
ASP.NET Core