DbSetのAddをMockでTestする
書きなれないブログでタイトルからつまづいてますが。。。
EF6をServiceとMockを使っていい感じでテスト&モッキューできないかなと。
元ネタはなかじさんのブログ。
ここの記事にあるEFのTestをMoqを使ってやる記事があります。
この中の「Testing query scenarios」が、サンプルデータをServiceを通してGetAllBlogsで取得するとorder byされて返ってくるというTESTです。
[TestMethod] public void GetAllBlogs_orders_by_name() { var data = new List<Blog> { new Blog { Name = "BBB" }, new Blog { Name = "ZZZ" }, new Blog { Name = "AAA" }, }.AsQueryable(); var mockSet = new Mock<DbSet<Blog>>(); mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider); mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression); mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType); mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(0 => data.GetEnumerator()); var mockContext = new Mock<BloggingContext>(); mockContext.Setup(c => c.Blogs).Returns(mockSet.Object); var service = new BlogService(mockContext.Object); var blogs = service.GetAllBlogs(); Assert.AreEqual(3, blogs.Count); Assert.AreEqual("AAA", blogs[0].Name); Assert.AreEqual("BBB", blogs[1].Name); Assert.AreEqual("ZZZ", blogs[2].Name); }
次にServiceでAddBlogすると追加されるTestをおこなうために下記を追加。
ここでなぜか(当然?)4件にならない、ということで本題に入ります。
service.AddBlog("CCC", "http://blogs.msdn.com/adonet"); var blogs = service.GetAllBlogs(); Assert.AreEqual(3, blogs.Count); //4件にならない
ServiceでAddBlogしたときに追加されるようにしたいわけですが、見つけたのが下記のAnswerの箇所。
要するに、DbSetでAddされる時のMockがないので追加されないとのこと。
...Callbackがよくわからないのでちょっと勉強してから。
というわけで試行錯誤した結果一番最後の行ができたやつ。
//コピペのままではコンパイルされず //mockSet.Setup(m => m.Add(It.IsAny<Blog>())).Callback(blog => data.Add(blog)); //IQueryrableにAddはない //mockSet.Setup(m => m.Add(It.IsAny<Blog>())).Callback<Blog>(blog => data.Add(blog)); //AddがないのでConcatで配列を作り直す→なぜかダメ //mockSet.Setup(m => m.Add(It.IsAny<Blog>())).Callback((Blog blog) => data = data.Concat<Blog>(new[] { blog })) //ListとIQueryableを上で使い分ける mockSet.Setup(d => d.Add(It.IsAny<Blog>())).Callback<Blog>((s) => source.Add(s));
動いたソースを一応全部のっけておきます。
[Test] public void GetAllBlogs_with_mock() { var source = new List<Blog> { new Blog { Name = "BBB" }, new Blog { Name = "ZZZ" }, new Blog { Name = "AAA" }, }; var data = source.AsQueryable(); var mockSet = new Mock<DbSet<Blog>>(); mockSet.As<IQueryable<Blog>>().Setup(m => m.Provider).Returns(data.Provider); mockSet.As<IQueryable<Blog>>().Setup(m => m.Expression).Returns(data.Expression); mockSet.As<IQueryable<Blog>>().Setup(m => m.ElementType).Returns(data.ElementType); mockSet.As<IQueryable<Blog>>().Setup(m => m.GetEnumerator()).Returns(data.GetEnumerator()); //DbSetのAddをモックする //ListとIQueryableを上で使い分ける(これは仕方ない?ここをもうちょいきれいにしたい) mockSet.Setup(d => d.Add(It.IsAny<Blog>())).Callback<Blog>((s) => source.Add(s)); var mockContext = new Mock<BloggingContext>(); mockContext.Setup(c => c.Blogs).Returns(mockSet.Object); var service = new BlogService(mockContext.Object); var blogs = service.GetAllBlogs(); Assert.AreEqual(3, blogs.Count,"追加前"); Assert.AreEqual("AAA", blogs[0].Name); Assert.AreEqual("BBB", blogs[1].Name); Assert.AreEqual("ZZZ", blogs[2].Name); //もう一件追加する service.AddBlog("CCC", "http://blogs.msdn.com/adonet"); blogs = service.GetAllBlogs(); Assert.AreEqual(4, blogs.Count, "追加後"); Assert.AreEqual("AAA", blogs[0].Name); Assert.AreEqual("BBB", blogs[1].Name); Assert.AreEqual("CCC", blogs[2].Name); Assert.AreEqual("ZZZ", blogs[3].Name); }
で、下記に回答がありましたというオチです。
追伸、 もっきゅーもっきゅーって居酒屋のメニューにしか聞こえないのは自分だけ?