1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
7 // http://www.apache.org/licenses/LICENSE-2.0
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle
.MonoRail
.TestSupport
18 using System
.Collections
.Generic
;
19 using System
.Collections
.Specialized
;
22 using Castle
.Components
.Common
.EmailSender
;
23 using Castle
.MonoRail
.Framework
;
24 using Castle
.MonoRail
.Framework
.Test
;
26 public delegate void ContextInitializer(MockEngineContext context
);
29 /// Base class that set ups the necessary infrastructure
30 /// to test controllers without the need
31 /// for an ASP.Net Runtime.
35 /// The following code is an example of a controller test:
39 /// public class LoginControllerTestCase : BaseControllerTest
41 /// private LoginController controller;
44 /// public void Init()
46 /// controller = new LoginController();
47 /// PrepareController(controller);
51 /// public void Authenticate_Should_Use_The_AuthenticationService()
53 /// // set up a mock authentication service before
55 /// controller.Authenticate("username", "my password", false);
57 /// Assert.AreEqual(3, controller.PropertyBag.Count);
58 /// Assert.AreEqual("username", controller.PropertyBag["username"]);
59 /// Assert.AreEqual("my password", controller.PropertyBag["password"]);
60 /// Assert.AreEqual(false, controller.PropertyBag["autoLogin"]);
66 /// The following is a more sophisticate test for an action that sends emails.
71 /// public void Register_Should_Add_Registration_Using_The_Repository()
73 /// Registration reg = new Registration("John Doe", "johndoe@gmail.com");
75 /// using(mockRepository.Record())
77 /// registrationRepositoryMock.Add(reg);
80 /// using(mockRepository.Playback())
82 /// controller.Register(reg); // This action sends two emails
84 /// Assert.IsTrue(HasRenderedEmailTemplateNamed("emailToManager"));
85 /// Assert.IsTrue(HasRenderedEmailTemplateNamed("emailToParticipant"));
87 /// Assert.AreEqual("manager@gmail.com", MessagesSent[0].To);
88 /// Assert.AreEqual("johndoe@gmail.com", MessagesSent[1].To);
90 /// Assert.AreEqual("Registration\\Success", controller.SelectedViewName);
98 /// You must invoke <see cref="PrepareController(Controller)"/> -- or a different overload -
99 /// before making invocations to the controller.
101 public abstract class BaseControllerTest
103 private readonly string domain
;
104 private readonly string domainPrefix
;
105 private readonly int port
;
106 private MockEngineContext context
;
107 private IMockRequest request
;
108 private IMockResponse response
;
109 private MockServices services
;
110 private ITrace trace
;
111 private IDictionary
<string, HttpCookie
> cookies
;
112 private IControllerContext controllerContext
;
113 protected string virtualDir
= "";
116 /// Initializes a new instance of the <see cref="BaseControllerTest"/> class.
118 protected BaseControllerTest() : this("app.com", "www", 80)
123 /// Initializes a new instance of the <see cref="BaseControllerTest"/> class.
125 /// <param name="domain">The domain.</param>
126 /// <param name="domainPrefix">The domain prefix.</param>
127 /// <param name="port">The port.</param>
128 protected BaseControllerTest(string domain
, string domainPrefix
, int port
)
130 this.domain
= domain
;
131 this.domainPrefix
= domainPrefix
;
136 /// Override to perform any pre-test set up
138 protected virtual void OnSetUp()
143 /// Gets the cookies.
145 /// <value>The cookies.</value>
146 public IDictionary
<string, HttpCookie
> Cookies
148 get { return cookies; }
152 /// Gets the context.
154 /// <value>The context.</value>
155 protected IEngineContext Context
157 get { return context; }
161 /// Gets the request.
163 /// <value>The request.</value>
164 public IMockRequest Request
166 get { return request; }
170 /// Gets the response.
172 /// <value>The response.</value>
173 public IMockResponse Response
175 get { return response; }
181 /// <value>The trace.</value>
184 get { return trace; }
188 /// Gets the controller context.
190 /// <value>The controller context.</value>
191 public IControllerContext ControllerContext
193 get { return controllerContext; }
197 /// Gets the services.
199 /// <value>The services.</value>
200 public MockServices Services
202 get { return services; }
206 /// Prepares the controller giving it mock implementations
207 /// of the service it requires to function normally.
209 /// <param name="controller">The controller.</param>
210 protected void PrepareController(Controller controller
)
212 PrepareController(controller
, InitializeEngineContext
);
216 /// Prepares the controller giving it mock implementations
217 /// of the service it requires to function normally.
219 /// <param name="controller">The controller.</param>
220 /// <param name="contextInitializer">The context initializer.</param>
221 protected void PrepareController(Controller controller
, ContextInitializer contextInitializer
)
223 PrepareController(controller
, "", "Controller", "Action", contextInitializer
);
227 /// Prepares the controller giving it mock implementations
228 /// of the service it requires to function normally.
230 /// <param name="controller">The controller.</param>
231 /// <param name="controllerName">Name of the controller.</param>
232 protected void PrepareController(Controller controller
, string controllerName
)
234 PrepareController(controller
, controllerName
, "Action");
239 /// Prepares the controller giving it mock implementations
240 /// of the service it requires to function normally.
242 /// <param name="controller">The controller.</param>
243 /// <param name="controllerName">Name of the controller.</param>
244 /// <param name="actionName">Name of the action.</param>
245 protected void PrepareController(Controller controller
, string controllerName
, string actionName
)
247 PrepareController(controller
, "", controllerName
, actionName
);
251 /// Prepares the controller giving it mock implementations
252 /// of the service it requires to function normally.
254 /// <param name="controller">The controller.</param>
255 /// <param name="areaName">Name of the area (cannot be null).</param>
256 /// <param name="controllerName">Name of the controller.</param>
257 /// <param name="actionName">Name of the action.</param>
258 protected void PrepareController(Controller controller
, string areaName
, string controllerName
, string actionName
)
260 PrepareController(controller
, areaName
, controllerName
, actionName
, InitializeEngineContext
);
264 /// Prepares the controller giving it mock implementations
265 /// of the service it requires to function normally.
267 /// <param name="controller">The controller.</param>
268 /// <param name="areaName">Name of the area (cannot be null).</param>
269 /// <param name="controllerName">Name of the controller.</param>
270 /// <param name="actionName">Name of the action.</param>
271 /// <param name="contextInitializer">The context initializer.</param>
272 protected void PrepareController(Controller controller
, string areaName
, string controllerName
, string actionName
, ContextInitializer contextInitializer
)
274 if (controller
== null)
276 throw new ArgumentNullException("controller", "'controller' cannot be null");
278 if (areaName
== null)
280 throw new ArgumentNullException("areaName");
282 if (controllerName
== null)
284 throw new ArgumentNullException("controllerName");
286 if (actionName
== null)
288 throw new ArgumentNullException("actionName");
291 cookies
= new Dictionary
<string, HttpCookie
>(StringComparer
.InvariantCultureIgnoreCase
);
293 BuildEngineContext(areaName
, controllerName
, actionName
, contextInitializer
);
295 controllerContext
= services
.ControllerContextFactory
.Create(areaName
, controllerName
, actionName
, services
.ControllerDescriptorProvider
.BuildDescriptor(controller
));
297 controller
.Contextualize(Context
, controllerContext
);
298 controller
.CreateStandardHelpers();
300 controller
.Initialize();
304 /// Constructs a mock context.
306 /// <param name="areaName">Name of the area.</param>
307 /// <param name="controllerName">Name of the controller.</param>
308 /// <param name="actionName">Name of the action.</param>
309 protected void BuildEngineContext(string areaName
, string controllerName
, string actionName
)
311 BuildEngineContext(areaName
, controllerName
, actionName
, InitializeEngineContext
);
315 /// Constructs a mock context.
317 /// <param name="areaName">Name of the area.</param>
318 /// <param name="controllerName">Name of the controller.</param>
319 /// <param name="actionName">Name of the action.</param>
320 /// <param name="contextInitializer">The context initializer.</param>
321 protected void BuildEngineContext(string areaName
, string controllerName
, string actionName
, ContextInitializer contextInitializer
)
323 UrlInfo info
= BuildUrlInfo(areaName
, controllerName
, actionName
);
324 services
= BuildServices();
325 request
= BuildRequest();
326 response
= BuildResponse(info
);
327 trace
= BuildTrace();
328 context
= BuildRailsEngineContext(request
, response
, services
, trace
, info
);
329 contextInitializer(context
);
333 /// Builds the request.
335 /// <returns></returns>
336 protected virtual IMockRequest
BuildRequest()
338 return new MockRequest(cookies
);
342 /// Builds the services.
344 /// <returns></returns>
345 protected virtual MockServices
BuildServices()
347 return new MockServices();
351 /// Builds the response.
353 /// <returns></returns>
354 protected virtual IMockResponse
BuildResponse(UrlInfo info
)
356 return new MockResponse(cookies
, info
);
360 /// Builds the trace.
362 /// <returns></returns>
363 protected virtual ITrace
BuildTrace()
365 return new MockTrace();
369 /// Builds the a mock context. You can override this method to
370 /// create a special configured mock context.
372 /// <param name="request">The request.</param>
373 /// <param name="response">The response.</param>
374 /// <param name="services">The services.</param>
375 /// <param name="trace">The trace.</param>
376 /// <param name="urlInfo">The URL info.</param>
377 /// <returns></returns>
378 protected virtual MockEngineContext
BuildRailsEngineContext(IMockRequest request
, IMockResponse response
,
379 IMonoRailServices services
, ITrace trace
, UrlInfo urlInfo
)
381 MockEngineContext engine
= new MockEngineContext(request
, response
, services
, urlInfo
);
382 engine
.Trace
= trace
;
387 /// Builds the URL info that represents the contextual Url.
389 /// <param name="areaName">Name of the area.</param>
390 /// <param name="controllerName">Name of the controller.</param>
391 /// <param name="actionName">Name of the action.</param>
392 /// <returns></returns>
393 protected virtual UrlInfo
BuildUrlInfo(string areaName
, string controllerName
, string actionName
)
395 return new UrlInfo(domain
, domainPrefix
, virtualDir
, "http", port
,
396 Path
.Combine(Path
.Combine(areaName
, controllerName
), actionName
),
397 areaName
, controllerName
, actionName
, ".castle", null);
401 /// Allows modifying of the engine context created by <see cref="BuildRailsEngineContext"/>
403 /// <param name="mockEngineContext">The engine context to modify</param>
404 protected virtual void InitializeEngineContext(MockEngineContext mockEngineContext
)
408 /// Determines whether a specified template was rendered -- to send an email.
410 /// <param name="templateName">Name of the template.</param>
412 /// <c>true</c> if was rendered; otherwise, <c>false</c>.
414 protected bool HasRenderedEmailTemplateNamed(string templateName
)
416 MockEngineContext
.RenderedEmailTemplate template
=
417 context
.RenderedEmailTemplates
.Find(
418 delegate(MockEngineContext
.RenderedEmailTemplate emailTemplate
)
420 return templateName
.Equals(emailTemplate
.Name
, StringComparison
.OrdinalIgnoreCase
);
423 return template
!= null;
427 /// Gets the fake email messages sent.
429 /// <value>The messages sent.</value>
430 protected Message
[] MessagesSent
432 get { return context.MessagesSent.ToArray(); }
436 /// Gets the rendered email templates.
438 /// <value>The rendered email templates.</value>
439 protected MockEngineContext
.RenderedEmailTemplate
[] RenderedEmailTemplates
441 get { return context.RenderedEmailTemplates.ToArray(); }