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 /// <param name="actionName">Name of the action.</param>
233 protected void PrepareController(Controller controller
, string controllerName
, string actionName
)
235 PrepareController(controller
, "", controllerName
, actionName
);
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="areaName">Name of the area (cannot be null).</param>
244 /// <param name="controllerName">Name of the controller.</param>
245 /// <param name="actionName">Name of the action.</param>
246 protected void PrepareController(Controller controller
, string areaName
, string controllerName
, string actionName
)
248 PrepareController(controller
, areaName
, controllerName
, actionName
, InitializeEngineContext
);
252 /// Prepares the controller giving it mock implementations
253 /// of the service it requires to function normally.
255 /// <param name="controller">The controller.</param>
256 /// <param name="areaName">Name of the area (cannot be null).</param>
257 /// <param name="controllerName">Name of the controller.</param>
258 /// <param name="actionName">Name of the action.</param>
259 /// <param name="contextInitializer">The context initializer.</param>
260 protected void PrepareController(Controller controller
, string areaName
, string controllerName
, string actionName
, ContextInitializer contextInitializer
)
262 if (controller
== null)
264 throw new ArgumentNullException("controller", "'controller' cannot be null");
266 if (areaName
== null)
268 throw new ArgumentNullException("areaName");
270 if (controllerName
== null)
272 throw new ArgumentNullException("controllerName");
274 if (actionName
== null)
276 throw new ArgumentNullException("actionName");
279 cookies
= new Dictionary
<string, HttpCookie
>(StringComparer
.InvariantCultureIgnoreCase
);
281 BuildEngineContext(areaName
, controllerName
, actionName
, contextInitializer
);
283 controllerContext
= services
.ControllerContextFactory
.Create(areaName
, controllerName
, actionName
, services
.ControllerDescriptorProvider
.BuildDescriptor(controller
));
285 controller
.SetEngineContext(Context
);
286 controller
.ControllerContext
= controllerContext
;
287 controller
.CreateStandardHelpers();
291 /// Constructs a mock context.
293 /// <param name="areaName">Name of the area.</param>
294 /// <param name="controllerName">Name of the controller.</param>
295 /// <param name="actionName">Name of the action.</param>
296 protected void BuildEngineContext(string areaName
, string controllerName
, string actionName
)
298 BuildEngineContext(areaName
, controllerName
, actionName
, InitializeEngineContext
);
302 /// Constructs a mock context.
304 /// <param name="areaName">Name of the area.</param>
305 /// <param name="controllerName">Name of the controller.</param>
306 /// <param name="actionName">Name of the action.</param>
307 /// <param name="contextInitializer">The context initializer.</param>
308 protected void BuildEngineContext(string areaName
, string controllerName
, string actionName
, ContextInitializer contextInitializer
)
310 UrlInfo info
= BuildUrlInfo(areaName
, controllerName
, actionName
);
311 services
= BuildServices();
312 request
= BuildRequest();
313 response
= BuildResponse();
314 trace
= BuildTrace();
315 context
= BuildRailsEngineContext(request
, response
, services
, trace
, info
);
316 contextInitializer(context
);
320 /// Builds the request.
322 /// <returns></returns>
323 protected virtual IMockRequest
BuildRequest()
325 return new MockRequest(cookies
);
329 /// Builds the services.
331 /// <returns></returns>
332 protected virtual MockServices
BuildServices()
334 return new MockServices();
338 /// Builds the response.
340 /// <returns></returns>
341 protected virtual IMockResponse
BuildResponse()
343 return new MockResponse(cookies
);
347 /// Builds the trace.
349 /// <returns></returns>
350 protected virtual ITrace
BuildTrace()
352 return new MockTrace();
356 /// Builds the a mock context. You can override this method to
357 /// create a special configured mock context.
359 /// <param name="request">The request.</param>
360 /// <param name="response">The response.</param>
361 /// <param name="services">The services.</param>
362 /// <param name="trace">The trace.</param>
363 /// <param name="urlInfo">The URL info.</param>
364 /// <returns></returns>
365 protected virtual MockEngineContext
BuildRailsEngineContext(IMockRequest request
, IMockResponse response
,
366 IMonoRailServices services
, ITrace trace
, UrlInfo urlInfo
)
368 MockEngineContext engine
= new MockEngineContext(request
, response
, services
, urlInfo
);
369 engine
.Trace
= trace
;
374 /// Builds the URL info that represents the contextual Url.
376 /// <param name="areaName">Name of the area.</param>
377 /// <param name="controllerName">Name of the controller.</param>
378 /// <param name="actionName">Name of the action.</param>
379 /// <returns></returns>
380 protected virtual UrlInfo
BuildUrlInfo(string areaName
, string controllerName
, string actionName
)
382 return new UrlInfo(domain
, domainPrefix
, virtualDir
, "http", port
,
383 Path
.Combine(Path
.Combine(areaName
, controllerName
), actionName
),
384 areaName
, controllerName
, actionName
, "rails", null);
388 /// Allows modifying of the engine context created by <see cref="BuildRailsEngineContext"/>
390 /// <param name="mockEngineContext">The engine context to modify</param>
391 protected virtual void InitializeEngineContext(MockEngineContext mockEngineContext
)
395 /// Determines whether a specified template was rendered -- to send an email.
397 /// <param name="templateName">Name of the template.</param>
399 /// <c>true</c> if was rendered; otherwise, <c>false</c>.
401 protected bool HasRenderedEmailTemplateNamed(string templateName
)
403 MockEngineContext
.RenderedEmailTemplate template
=
404 context
.RenderedEmailTemplates
.Find(
405 delegate(MockEngineContext
.RenderedEmailTemplate emailTemplate
)
407 return templateName
.Equals(emailTemplate
.Name
, StringComparison
.OrdinalIgnoreCase
);
410 return template
!= null;
414 /// Gets the fake email messages sent.
416 /// <value>The messages sent.</value>
417 protected Message
[] MessagesSent
419 get { return context.MessagesSent.ToArray(); }
423 /// Gets the rendered email templates.
425 /// <value>The rendered email templates.</value>
426 protected MockEngineContext
.RenderedEmailTemplate
[] RenderedEmailTemplates
428 get { return context.RenderedEmailTemplates.ToArray(); }