|
4 | 4 | <head>
|
5 | 5 | <meta charset="utf-8">
|
6 | 6 | <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
|
7 |
| - <title> </title> |
| 7 | + <title>ViewBag, ViewData & TempData </title> |
8 | 8 | <meta name="viewport" content="width=device-width">
|
9 |
| - <meta name="title" content=" "> |
| 9 | + <meta name="title" content="ViewBag, ViewData & TempData "> |
10 | 10 | <meta name="generator" content="docfx 2.5.0.0">
|
11 | 11 | {% seo %}
|
12 | 12 |
|
|
63 | 63 | <div class="article row grid-right">
|
64 | 64 | <div class="col-md-10">
|
65 | 65 | <article class="content wrap" id="_content" data-uid="">
|
| 66 | + <h1 id="viewbag-viewdata--tempdata" sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="1" sourceendlinenumber="1">ViewBag, ViewData & TempData</h1> |
66 | 67 |
|
67 |
| - |
| 68 | +<p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="3" sourceendlinenumber="3">The <strong>"Music Store"</strong> web application does uses only the <strong>"ViewBag"</strong> so we will write some tests for it. The <strong>"ViewData</strong>" and the <strong>"TempData"</strong> use similar syntax.</p> |
| 69 | +<h2 id="testing-with-viewbag-entry" sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="5" sourceendlinenumber="5">Testing with ViewBag entry</h2> |
| 70 | +<p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="7" sourceendlinenumber="7">Let's test something simple - the HTTP Get overload of the <strong>"Login"</strong> action in the <strong>"AccountController"</strong>:</p> |
| 71 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="9" sourceendlinenumber="15"><code class="lang-c#">public IActionResult Login(string returnUrl = null) |
| 72 | +{ |
| 73 | + ViewBag.ReturnUrl = returnUrl; |
| 74 | + return View(); |
| 75 | +} |
| 76 | +</code></pre><p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="17" sourceendlinenumber="17">We need a new dependency (again?!) - <strong>"MyTested.AspNetCore.Mvc.ViewData"</strong>:</p> |
| 77 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="19" sourceendlinenumber="38"><code class="lang-json">"dependencies": { |
| 78 | + "dotnet-test-xunit": "2.2.0-*", |
| 79 | + "xunit": "2.2.0-*", |
| 80 | + "Moq": "4.6.38-*", |
| 81 | + "MyTested.AspNetCore.Mvc.Authentication": "1.0.0", |
| 82 | + "MyTested.AspNetCore.Mvc.Caching": "1.0.0", |
| 83 | + "MyTested.AspNetCore.Mvc.Controllers": "1.0.0", |
| 84 | + "MyTested.AspNetCore.Mvc.DependencyInjection": "1.0.0", |
| 85 | + "MyTested.AspNetCore.Mvc.EntityFrameworkCore": "1.0.0", |
| 86 | + "MyTested.AspNetCore.Mvc.Http": "1.0.0", |
| 87 | + "MyTested.AspNetCore.Mvc.ModelState": "1.0.0", |
| 88 | + "MyTested.AspNetCore.Mvc.Models": "1.0.0", |
| 89 | + "MyTested.AspNetCore.Mvc.Options": "1.0.0", |
| 90 | + "MyTested.AspNetCore.Mvc.Session": "1.0.0", |
| 91 | + "MyTested.AspNetCore.Mvc.ViewActionResults": "1.0.0", |
| 92 | + "MyTested.AspNetCore.Mvc.ViewData": "1.0.0", // <--- |
| 93 | + "MusicStore": "*" |
| 94 | +}, |
| 95 | +</code></pre><p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="40" sourceendlinenumber="40">We need to add the <strong>"ViewData"</strong> features, because the <strong>"ViewBag"</strong> is actually a <a href="https://github.com/aspnet/Mvc/blob/dev/src/Microsoft.AspNetCore.Mvc.ViewFeatures/Controller.cs#L91" sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="40" sourceendlinenumber="40">dynamic version of it</a>.</p> |
| 96 | +<p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="42" sourceendlinenumber="42">I hope you remember how we tested session and cache. Well, the <strong>"ViewBag"</strong> (<strong>"ViewData"</strong> and <strong>"TempData"</strong> too) is no different:</p> |
| 97 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="44" sourceendlinenumber="60"><code class="lang-c#">[Fact] |
| 98 | +public void LoginShouldHaveReturnUrlInTheViewBag() |
| 99 | +{ |
| 100 | + var returnUrl = "Test/Return/Url"; |
| 101 | + |
| 102 | + MyController<AccountController> |
| 103 | + .Instance() |
| 104 | + .Calling(c => c.Login(returnUrl)) |
| 105 | + .ShouldHave() |
| 106 | + .ViewBag(viewBag => viewBag // <--- |
| 107 | + .ContainingEntry("ReturnUrl", returnUrl)) |
| 108 | + .AndAlso() |
| 109 | + .ShouldReturn() |
| 110 | + .View(); |
| 111 | +} |
| 112 | +</code></pre><h2 id="testing-with-multiple-viewbag-entries" sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="62" sourceendlinenumber="62">Testing with multiple ViewBag entries</h2> |
| 113 | +<p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="64" sourceendlinenumber="64">Let's write another one - for the HTTP Get overload of the <strong>"Create"</strong> action in <strong>"StoreManagerController"</strong>:</p> |
| 114 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="66" sourceendlinenumber="73"><code class="lang-c#">public IActionResult Create() |
| 115 | +{ |
| 116 | + ViewBag.GenreId = new SelectList(DbContext.Genres, "GenreId", "Name"); |
| 117 | + ViewBag.ArtistId = new SelectList(DbContext.Artists, "ArtistId", "Name"); |
| 118 | + return View(); |
| 119 | +} |
| 120 | +</code></pre><p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="75" sourceendlinenumber="75">And our test:</p> |
| 121 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="77" sourceendlinenumber="113"><code class="lang-c#">[Fact] |
| 122 | +public void CreateShouldHaveValidEntriesInViewBag() |
| 123 | +{ |
| 124 | + var genres = new[] |
| 125 | + { |
| 126 | + new Genre { GenreId = 1, Name = "Rock" }, |
| 127 | + new Genre { GenreId = 2, Name = "Rap" } |
| 128 | + }; |
| 129 | + |
| 130 | + var artists = new[] |
| 131 | + { |
| 132 | + new Artist { ArtistId = 1, Name = "Tupac" }, |
| 133 | + new Artist { ArtistId = 2, Name = "Biggie" } |
| 134 | + }; |
| 135 | + |
| 136 | + MyController<StoreManagerController> |
| 137 | + .Instance() |
| 138 | + .WithDbContext(db => db |
| 139 | + .WithEntities(entities => |
| 140 | + { |
| 141 | + entities.AddRange(genres); |
| 142 | + entities.AddRange(artists); |
| 143 | + })) |
| 144 | + .Calling(c => c.Create()) |
| 145 | + .ShouldHave() |
| 146 | + .ViewBag(viewBag => viewBag // <--- |
| 147 | + .ContainingEntries(new |
| 148 | + { |
| 149 | + GenreId = new SelectList(From.Services<MusicStoreContext>().Genres, "GenreId", "Name"), |
| 150 | + ArtistId = new SelectList(From.Services<MusicStoreContext>().Artists, "ArtistId", "Name") |
| 151 | + })) |
| 152 | + .AndAlso() |
| 153 | + .ShouldReturn() |
| 154 | + .View(); |
| 155 | +} |
| 156 | +</code></pre><p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="115" sourceendlinenumber="115">The <strong>"ContainingEntries"</strong> call is equivalent to this one:</p> |
| 157 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="117" sourceendlinenumber="123"><code class="lang-c#">.ContainingEntries(new Dictionary<string, object> |
| 158 | +{ |
| 159 | + ["GenreId"] = new SelectList(From.Services<MusicStoreContext>().Genres, "GenreId", "Name"), |
| 160 | + ["ArtistId"] = new SelectList(From.Services<MusicStoreContext>().Artists, "ArtistId", "Name") |
| 161 | +})) |
| 162 | +</code></pre><p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="125" sourceendlinenumber="125">Both methods will validate whether the total number of entries in the <strong>"ViewBag"</strong> is equal to the total number you provide to the test.</p> |
| 163 | +<p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="127" sourceendlinenumber="127">If you do not want the total number validation, just use:</p> |
| 164 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="129" sourceendlinenumber="133"><code class="lang-c#">.ViewBag(viewBag => viewBag // <--- |
| 165 | + .ContainingEntry("GenreId", new SelectList(From.Services<MusicStoreContext>().Genres, "GenreId", "Name")) |
| 166 | + .ContainingEntry("ArtistId", new SelectList(From.Services<MusicStoreContext>().Artists, "ArtistId", "Name"))) |
| 167 | +</code></pre><h2 id="viewdata-and-tempdata" sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="135" sourceendlinenumber="135">ViewData and TempData</h2> |
| 168 | +<p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="137" sourceendlinenumber="137"><strong>"ViewData"</strong> have the same API:</p> |
| 169 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="139" sourceendlinenumber="149"><code class="lang-c#">MyController<SomeController> |
| 170 | + .Instance() |
| 171 | + .Calling(c => c.SomeAction()) |
| 172 | + .ShouldHave() |
| 173 | + .ViewData(viewData => viewData // <--- |
| 174 | + .ContainingEntry("SomeKey", someValue)) |
| 175 | + .AndAlso() |
| 176 | + .ShouldReturn() |
| 177 | + .View(); |
| 178 | +</code></pre><p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="151" sourceendlinenumber="151"><strong>"TempData"</strong> too but you will need the <strong>"MyTested.AspNetCore.Mvc.TempData"</strong> package:</p> |
| 179 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="153" sourceendlinenumber="163"><code class="lang-c#">MyController<SomeController> |
| 180 | + .Instance() |
| 181 | + .Calling(c => c.SomeAction()) |
| 182 | + .ShouldHave() |
| 183 | + .TempData(tempData => tempData // <--- |
| 184 | + .ContainingEntry("SomeKey", someValue)) |
| 185 | + .AndAlso() |
| 186 | + .ShouldReturn() |
| 187 | + .View(); |
| 188 | +</code></pre><p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="165" sourceendlinenumber="165">Additionally, you can populate the <strong>"TempData"</strong> dictionary before the action call:</p> |
| 189 | +<pre sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="167" sourceendlinenumber="175"><code class="lang-c#">MyController<SomeController> |
| 190 | + .Instance() |
| 191 | + .WithTempData(tempData => tempData |
| 192 | + .WithEntry("SomeKey", someValue)) |
| 193 | + .Calling(c => c.SomeAction()) |
| 194 | + .ShouldReturn() |
| 195 | + .View(); |
| 196 | +</code></pre><h2 id="section-summary" sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="177" sourceendlinenumber="177">Section summary</h2> |
| 197 | +<p sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="179" sourceendlinenumber="179">We saw how easy it is to test with <strong>"ViewBag"</strong>, <strong>"ViewData"</strong> and <strong>"TempData"</strong>. Their fluent assertion API is very similar to the <strong>"Session"</strong> and <strong>"Cache"</strong> ones. But enough about controllers, let's move on to <a href="/tutorial/viewcomponents.html" sourcefile="tutorial/viewbagviewdatatempdata.md" sourcestartlinenumber="179" sourceendlinenumber="179">View Components</a>!</p> |
| 198 | + |
68 | 199 | </article>
|
69 | 200 | </div>
|
70 | 201 |
|
|
0 commit comments