• No results found

1. Starta projektet och försök kompilera. Du får nu kompileringsfel varför?

N/A
N/A
Protected

Academic year: 2022

Share "1. Starta projektet och försök kompilera. Du får nu kompileringsfel varför?"

Copied!
9
0
0

Loading.... (view fulltext now)

Full text

(1)

Lektion 10 – Övningar

Jag har lagt upp ett repo ”Lektion10” som innehåller lösningarna på dessa övningar. Commiten med tag v0.0 innehåller projektet innan några uppgifter är commitade. Efter du klonat/forkat: ”git checkout v0.0” för att få initial-version (detta checkar ut den commit som är taggad av mig med v0.0). Skapa sedan en egen branch med det GUI du använder eller ”git checkout –b

namn_pa_din_branch”. Sedan är det bara att börja jobba.

(Lägg också märke till att alla commits har en hash – du kan köra ”git checkout [COMMITHASH]” där [COMMITHASH] är hashen för en commit för att updatera till en specifik commit. Du behöver inte heller skriva in hela commit hashen när du gör på detta sätt – de första 7 tecknen räcker. För att se hash för commits kan du t.ex. skriva ”git log –pretty=oneline” (två st. ’-’ framför pretty). Du kan även se hashen på github.com eller i github for windows – Hur?)

Separat Model Projekt

För större projekt är det vanligt att man separerar ut Model-delen till ett eget klassbibliotek. Detta bibliotek kan sedan åternanvändas i andra projekt – t.ex. ifall man har tjänster eller liknande som arbetar mot samma data/modell.

I Lektion10s Solution är applikationen uppdelad i Lektion10.Web som innehåller Controllers, Views och ViewModels, etc. Samt Lektion10.Model som innehåller Model-klasser.

1. Starta projektet och försök kompilera. Du får nu kompileringsfel – varför?

Du saknar tillgång till namespaces i Lektion10.Model. Hur gör vi för att få tillgång till dessa?

Hur gjorde vi för att få tillgång till t.ex. Ninject i Lektion 9? (Vi installerade Ninject med NuGet – det behöver/kan vi inte göra med Lektion10.Model – men NuGet gjorde även ytterligare en sak automatiskt åt oss – vad var det?)

Moq

Just nu använder vi ett FakeRepository för att simulera ett riktigt repository (fram till dess att vi skapat en databas). Men är det eg. intressant att lägga tid på att skapa en verklighetstrogen simulation för att kunna komma igång med Controller/View-utvecklingen? Nej!

Det räcker med att vår simulering implementerar samma interface som vår riktiga repository och att det svarar med korrekta objekt då vi anropar metoder i det.

Vi kommer nu installera ett så kallat Mocking-ramverk som används för just denna typ av simulering.

2. Installera Moq med NuGet.

Vi skall nu byta ut vårat FakeRepository mot ett Mockat Repository mha Moq.

3. I Lektion10.Web.Infrastructure.NinjectControllerFactory.AddBindings()-metoden: Skapa ett nytt Mock-objekt av typen IProductRepository med ”Mock<IProductRepository> mock = new Mock<IProductRepository>();”

(2)

4. Vårat Mock-repository bör svara på propertyn ”Products” ifrån IProductRepository- interfacet. Det görs med följande rad:

mock.Setup(m => m.Products).Returns(new List<Product> {

new Product { ProductID = 1, Name = "Football", Price = 25 }, new Product { ProductID = 2, Name = "Surf Board", Price = 179 }, new Product { ProductID = 3, Name = "Running Shoes", Price = 95 }, new Product { ProductID = 4, Name = "Goggles", Price = 25 }, new Product { ProductID = 5, Name = "Snowboard", Price = 179 }, new Product { ProductID = 6, Name = "Ski Boots", Price = 95 }, new Product { ProductID = 7, Name = "Camp Stove", Price = 25 }, new Product { ProductID = 8, Name = "Tent", Price = 179 },

new Product { ProductID = 9, Name = "Hiking Boots", Price = 95 }, }.AsQueryable());

Ovanstående säger att Propertyn Products på vårt mock-object skall returnera en lista av Product-objekt.

5. Vidare behöver vi kunna svara på .Get-metoden från interfacet. Det löser vi med följande rad:

mock.Setup(m => m.Get(It.IsAny<int>())).Returns(new Product { ProductID = 1, Name = "Football", Price = 25 });

Ovanstående säger att metoden Get skall returnera det angivna Product-objekt oavsett vad metoden får för integer som in-parameter.

6. Interfacet har även metoder för Save och Edit – dessa returnerar dock void, så vi behöver inte ange något returvärde för dem, det räcker att det går att anropa dem (Vilket kommer fungera automatiskt på vårt mock-objekt).

Däremot behöver vi binda IProductRepository till vårat mock-objekt. Detta göra med:

ninjectKernel.Bind<IProductRepository>().ToConstant(mock.Object);

Detta kommando har vi sett innan och innebär alltså att Ninject kommer ersätta IProductRepository med mock.Object i konstruktorn för vår ProductController.

Plocka bort raden som binder IProductRepository till FakeRepository.

(Vi behöver även ange att NinjectControllerFactory skall vara den ControllerFactory som ramverket skall använda för att skapa controllers – detta är redan gjort och görs i Global.asax/Application_Start()

Testa nu att köra applikationen. Du bör kunna använda alla funktionen i applikationen (Däremot kommer inte dina editeringar eller nya objekt sparas – och ifall du tittar på t.ex.

(3)

Details så kommer det alltid vara samma objekt – detta är förväntat pga vad vi angivit i mock.Setp i Uppgift 4+5.

Ovanstående metod skulle kunna användas ifall det är olika teams/utvecklare som utvecklar Repository-klassen och ProductController-klasse. De/Den som utvecklar ProductController behöver varken vänta på att Repository-klassen skall bli klar eller försöka skapa ett realistiskt FakeRepository, utan kan köra igång med utveckling av Controller direkt.

Detta är dock inte det intressantaste användningsområdet för Mocking. Det intressantaste med mocks är att vi kan skapa enhetstester även i de fall komplexa beroende förekommer.

När vi testar t.ex. [Httppost]Edit(..)-metoden i ProductController, är det intressant att veta att objektet verkligen sparats i repositoryn då? Nej! Det är bara intressant att rätt metod i repositoryt anropats. Att spara funkar som det skall bör istället testas då enhetstester för repositoryt skrivs (Kom ihåg – testa rätt saker – ProductController ansvarar inte för att spara objekt – det är repository- metodernas ansvar).

Moq kan hjälpa oss med detta.

Förutsatt att mock-objektet mockar ett IProductRepository, så kontrollerar följande att .Save- metoden andropas endast en gång med argumentet, p (Som är ett Product-objekt).

mock.Verify(r => r.Save(p), Times.Once());

Följande kontrollerar att .Save-metoden aldrig anropas med något argument:

mock.Verify(r => r.Save(It.IsAny<Product>()), Times.Never());

Förutsatt att vi har ett ProductController-objekt (target), kan vi kan även simulera ett Validation-fel innan t.ex. .Edit-metoden körs. Med följande (Detta kommer göra att ModelState.IsValid är false på ProductController-objektet.

target.ModelState.AddModelError("error", "error");

Med ovanstående kan vi skriva vettiga Unit Tests för Edit-metoden i ProductController.

7. Skriv två stycken Unit Tests för ProductController.

Det första testet skall testa att [HttpPost]Edit-metoden andropar .Save en gång på repository med rätt objekt ifall Edit ges ett korrekt objekt.

Det andra testet skall testa att [HttpPost]Edit-metoden aldrig anropoar .Save på repository med något objekt ifall Edit ges ett inkorrekt objekt.

(Kom håg – vi kan använda Dependancy Injection för att skapa ett ProductController-objekt med ett valfritt objekt som implementerar IProductRepository inskutet i konstruktorn).

(4)

Entity Framework Code First

Förra lektionen så testade vi Entity Framework Code First. – Jag hade lite problem med det, vilket berodde på att jag hade 5 instanser av SQL Server och ingen av dem var döpt enligt Default-värden, vilket gjorde att Entity Framework fick lite hicka när den letade efter standard servern för SQL.

För att undvika detta kan man ställa in vilken server och databas som skall vara standard för Entity Framework Code First själv. Detta skall vi göra nu – och det vi behöver veta är vad vår instans av SQL Server heter. Vi behöver även ange i en konfigurationsfil var Entity Framework skall skapa databaser.

8. Ta redo på vad din databas instans heter.

I Windows, klicka på: Start -> Alla Program -> Microsoft SQL Server -> Configuration Tools ->

SQL Server Configuration Manager.

I SQL Server Configuration Manager: Klicka på SQL Server Services.

Här hittar du en eller flera Instanser av “SQL Server ([namn])”. Dessa är dina instanser av SQL Server. Ifall någon av dem heter ”MSSQLSERVER” så är det default-instansen (en instans du inte behöver namnge). Vi är ute efter en namngiven instans – vilket är de andra. Ifall du har defaultvärden så bör du hitta en instans som heter ”SQLEXPRESS” och har state ”running”.

Kom ihåg det här namnet – vi kommer använda det i nästa uppgift.

(Om du inte hittar SQL Server Configuration Manager – Kontrollera ifall du har SQL Server Express installerat – om inte, installera det – annars sök på nätet och installera SQL Server Configuration Manager).

(Om du inte har state ”running” på din instans så är din SQL Server inte igång – sök på nätet om hur man startar en SQL Server).

Nu vet vi vad vår SQL Server instans heter – vi behöver tala om för Entity Framework var det kan hitta den.

9. Konfigurering av Entity Framework görs främst i Web.config (Den som är i roten av din Webbapplikation).

Lägg till följande i Web.config (Detta behöver ligga någonstans innanför <configuration>- taggarna – det finns redan en konfigurering av Entity Framework i Web.config, ersätt den.

Om du har två konfigureringar av samma sak får du i bästa fall en varning och i värsta fall runtime kraschar av applikationen):

(5)

<entityFramework>

<defaultConnectionFactory type="System.Data.Entity.Infrastructure.SqlConnectionFactory, EntityFramework">

<parameters>

<parameter value="Data Source=.\SQLEXPRESS;Initial Catalog=Lektion10;Integrated Sec urity=True" />

</parameters>

</defaultConnectionFactory>

</entityFramework>

defaultConnectionFactory- och parameter-taggarna skall ligga på en rad.

Det finns redan en

Ovanstående säger åt Entity Framework att den skall anvvända databas-instansen

”SQLEXPRESS” som default (Detta namn byter du ut mot det namn du hittade i föregående uppgift ifall det var annorlunda). Initial Catalog säger vad databasen vi vill använda heter.

Integrated security = True innebär att vi använder Windows Credentials för att ansluta till databasen (Ifall du använder någon annan databas än SQL Server/OLEDB så bör du välja False och ange ett användarnamn och lösenord som har rättigheter för databasen – sök på nätet).

Nu vet Entity Framework var den skall skapa databaser om vi kör med Code First.

10. Skapa en database med Code First – Det enda du behöver göra är att ändra binding i NinjectController Factory till ProductRepository i AddBindings-katalogen och sedan köra applikationen.

Lägg till en ny produkt.

(Kom ihåg: Det enda som krävs för att skapa en databas med Code First är att vi angett dataklasser och ett context-objekt – se övningar Lektion 9 om du är osäker).

11. Vi kan inspektera databasen genom att öppna Server Explorer i Visual Studio (View -> Server Explorer).

I Server Explorer: Ifall du inte har någon Data Connection, skapa en (Högerklicka på Data Connection och välj Add Connection – välj sedan server instans (Samma som i uppgift 8) och vilken databas (Heter ngt i stil med Lektion10.Model.Context.EFDbContext ) och klicka add.

Expandera din Data Connection – Expandera sedan Tables och välj New Query.

(6)

Välj ”Products” i Add Table Katalogen och klicka Close.

I mitten av din flik ser du ett SQL-Query – Skriv in ”SELECT * FROM Products” där. Klicka sedan på ”Execute SQL”-knappen eller använd Ctrl+R – i ditt resultat bör du nu se den produkt du lade till i Uppgift 10.

Vår data är i databasen!

Ändra modellen med EF Code First -> DataInitialization

12. Testa att lägga på en ny string-property på Product “ManufacturerSKU” (Skall representera tillverkarens id på produkten). Testa kör – du får runtime-fel (modellen har ändrats, men databasen ser likadan ut).

Problemet är att vår databas inte ser ut som vår Datatyp Product längre. Entity Framework är som standard inställd på att skapa en databas ifall det inte finns någon sedan innan. Vi kan ändra denna inställning till att EF skall skapa en databas ifall det inte finns någon eller köra en drop and recreate ifall vår modell inte stämmer överrens med hur databasen ser ut.

Samtidigt som vi gör den här ändringen så vill vi också seeda databasen med lite ursprungsdata då vi skapar databasen – så att vi alltid har lite testdata att titta på.

För att kunna göra detta så behöver vi skapa en egen databas Initializer och sedan ange att det är denna som skall användas av applikationen.

13. För att skapa en Initializer behöver vi en klass av följande typ:

public class Lektion10Initializer : DropCreateDatabaseIfModelChanges<EFDbContext>

Ovanstående säger att vår klass skall ärva från

DropCreateDatabaseIfModelChanges<EFDbContext> som är en befintlig generic initializer.

Denna initializer kommer droppa och återskapa databasen om vår modell ändras (dvs. precis det vi vill göra – vi skulle kunna använda denna initializer direkt, men vi vill också kunna seeda data, därför skapar vi en egen initializer).

(7)

Andra Initializers är CreateDatabaseOnlyIfNotExists (default), AlwaysRecreateDatabase.

Denna klass är redan skapad – den ligger I EFDbContext.cs (kommer du ihåg var denna fil låg?)

Som du ser så är även en metod Seed implementerad i vår initializer – det denna metod gör är helt enkelt att den skapar en lista över Product och sparar dessa i det context som skickas in. Detta komma vara vår testdata, som alltid kommer återskapas då vi omskapar databasen.

Titta i metoden .Seed(..) och se till att du förstår vad metoden gör.

14. För att ange vilken initializer som skall användas så går vi till metoden Application_Start i Global.asax och skriver in följande:

Database.SetInitializer<EFDbContext>(new Lektion10Initializer());

(Här hade vi alltså kunnat ange t.ex. ”new

DropCreateDatabaseIfModelChanges<EFDbContext>()” istället för Lektion10Initializer ifall vi inte hade velat seeda vår databas med data).

Om du får ett exception ”Cannot drop database ... because it is currently in use” så beror det på att du har en connection uppe mot databasen i Server Explorer – högerklicka på databasen i Server Explorer och välj ”Close Connection” (Du kan senare ta refresh på denna för att öppna en connection igen).

VIKTIGT! Lägg märke till att detta är ett potentiellt skadligt förfarande. Det fungerar fint att jobba såhär i vår utvecklings-miljö då vi inte har någon användning för skarp data. Men i test-miljö vill vi jobba med faktisk data – vi vill inte förstöra befintlig data. Ännu värre är ifall vi skulle publicera denna applikation till skarp miljö med en förändrad modell – första gången applikationen körs kommer samtlig befintlig data förstöras!

För att undvika detta kommer vi att konfigurera applikationen annorlunda för release – vi kommer prata om detta senare i kursen.

För att lösa en uppdaterad modell för de skarpa databaserna och databaser i testmiljö kan man antingen skriva SQL-script för att uppdatera databaserna utan att förstöra data, eller använda DataMigration-verktyg för att automatiskt generera SQL-script för uppdatering av databaserna.

Vi kommer prata om utveklingsmiljö/testmiljö/skarp miljö senare under kursen.

för att stänga av dbinitilaize kan man skriva in följande i entity-framework-delen av Web.config:

<contexts>

<context type="Lektion10.EFDbContext, Lektion10.Model" disableDatabaseInitialization="true" />

</contexts>

Foreign Keys i Code First

(8)

Än så länge har vi tittat på en väldigt enkel modell, med en isolerad datatyp. I verkligheten är det nästan aldrig så enkelt – datatyperna är relaterade till varandra. Vi skall nu ändra Category i Product till att vara en egen datatyp och testa uppdatera modellen efter detta.

15. Ändra Category-propertyn i Product till att vara av typen Category (Datatypen finns definierad i samma namespace som Product – kika hur den ser ut). Lägg även till en ny int- property ”CategoryID”

(Stavningen är viktg för bägge dessa properties – ID-propertyn måste sluta med ID och Category måste vara samma som det datatypen Category heter – detta för att EF automatiskt skall upp täcka att datatyperna är relaterade).

16. Plocka bort Required-dekorationen på Category i Product (Vi kommer inte editera denna idag – du kommer se exempel på detta imorgon).

17. Plocka även bort labels, fält och validationmessage för Category i Edit.cshtml, Create.cshtml, Delete.cshtml, och Index.cshtml – lämna kvar dem i Details.cshtml (Vi hade kunnat lösa dessa med en ViewModel och en DropDownListFor(..) – men vi lämnar det tills imorgon).

18. Ändra vår seed-data så att den sätter Category-objekt på Product. (Kommer du ihåg var vi har vår seedade data? – se uppgift 13.)

Du behöver även ändra datan som skapas i vårt FakeRepository, så att de produkter som skapas, skapas med ett Category-objekt och inte en sträng.

Kör applikationen – titta sedan i databasen – Plötsligt har vi en ny table, category och CategoryID i Product är satt till rätt ID.

Om du får ett exception ”Cannot drop database ... because it is currently in use” så beror det på att du har en connection uppe mot databasen i Server Explorer – högerklicka på

databasen i Server Explorer och välj ”Close Connection” (Du kan senare ta refresh på denna för att öppna en connection igen).

19. Om du kör applikationen och kikar på details för en produkt så kommer du se att det inte finns något angivet för Category – detta beror på att Foreign key objekt inte laddas med per default (Detta är en bra sak – det gör att vi inte skapar enorma queries mot databasen utan att vara medvetna om det). För att få med Category så måste vi explicit be om det. Detta gör vi med .Include(..)

Ändra definitionen på .Get(..) i ProductRepository till följande:

return context.Products.Include("Category").FirstOrDefault(p => p.ProductID == id);

Ovanstående säger till EF att inkludera data för det relaterade objektet ”Category”.

Ändra visning av Category i Details.cshtml till följande:

(9)

<div class="display-label">Category</div>

<div class="display-field">

@Html.DisplayFor(model => model.Category.Name)

</div>

Kör sedan applikationen och titta på details – nu ser du Category för vald produkt.

Läsanvisningar i Kursbok, etc.

Moq: finn i Kap. 6 i kursboken

Vad gäller ”Entity Framework Code First” kan du söka efter tutorials på nätet.

Building an MVC App with Code First: http://msdn.microsoft.com/en-us/data/gg685467.aspx - Här hittar du en video som går igenom delar av det vi pratat om idag. (Den är bara 11 minuter och väl värd att se – Julie Lerman är en av världens främsta experter på Entity Framework (Det är ett väldigt djupt ramverk) och ifall du vill veta mer om EF är hennes artiklar en bra utgångspunkt).

Lägg märke till att hon inte skapar initialzer för databasen och seedar den med grunddata som vi gjorde under övningarna.

Conventions for EF Code First: http://blogs.msdn.com/b/efdesign/archive/2010/06/01/conventions- for-code-first.aspx - diskuterar lite mer grundläggande hur man skapar Foreign Keys, etc.

.Include Method: http://msdn.microsoft.com/en-us/library/bb738708.aspx - kika på exemplen längst ned för lite mer avancerade includes.

References

Related documents

Syftet med denna studie är att undersöka om barn i enlighet med FN:s konvention om barns rättigheter har möjlighet till att fritt delta i det kulturella och konstnärliga livet

Föreskrifterna kopplade till LARO har varit och är fortfarande under kontinuerlig utveckling med syfte att skapa bästa möjliga vård och behandling för den enskilde, samtidigt

Under spelets gång måste du bestämma dig för om du vill skriva siffran eller om du väljer att stå över... Skriv in den siffra som tärningen

En röd tråd genom dessa aktörers resonemang är att NMR:s fascism förvisso är avskyvärd men att det faktum att de är fascistiska och står upp för en fascistisk

Samtidigt kommer vi att ändra planlösningen och det yttre utseendet för att få en så effektiv och hemtrevlig bostad som möjlig, anpassad för en eller två personer..

Lärare 1 arbetar på ett sätt som inte är så vanligt framförallt inte i matematik vilket han bland annat undervisar i. Han arbetar med ett sociokulturellt synsätt grundat i

Vill du spara aktuellt objekt med ett nytt namn, vilket innebär att du skapar en kopia av ursprungs objektet, visar du fl iken Arkiv och väljer Spara som (File, Save As).

Vad behöver maten kosta för att lantbruket skall överleva... Vilka faktorer påverkar vad