Skip to content

Commit 5f0bd60

Browse files
committed
Merge pull request #20 from ivaylokenov/development
Version 0.1
2 parents c5c5a3c + 50d69db commit 5f0bd60

File tree

8 files changed

+224
-8
lines changed

8 files changed

+224
-8
lines changed

MyWebApi.Tests/BuildersTests/ControllerBuilderTests.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@ public void CallingShouldPopulateCorrectActionNameAndActionResultWithAsyncAction
3434
[Test]
3535
public void CallingShouldPopulateModelStateWhenThereAreModelErrors()
3636
{
37-
var requestBody = TestObjectFactory.GetRequestModelWithErrors();
37+
var requestModel = TestObjectFactory.GetRequestModelWithErrors();
3838

3939
var actionResultTestBuilder = MyWebApi
4040
.Controller<WebApiController>()
41-
.Calling(c => c.OkResultActionWithRequestBody(1, requestBody));
41+
.Calling(c => c.OkResultActionWithRequestBody(1, requestModel));
4242

4343
var modelState = actionResultTestBuilder.Controller.ModelState;
4444

@@ -51,11 +51,11 @@ public void CallingShouldPopulateModelStateWhenThereAreModelErrors()
5151
[Test]
5252
public void CallingShouldHaveValidModelStateWhenThereAreNoModelErrors()
5353
{
54-
var requestBody = TestObjectFactory.GetValidRequestModel();
54+
var requestModel = TestObjectFactory.GetValidRequestModel();
5555

5656
var actionResultTestBuilder = MyWebApi
5757
.Controller<WebApiController>()
58-
.Calling(c => c.OkResultActionWithRequestBody(1, requestBody));
58+
.Calling(c => c.OkResultActionWithRequestBody(1, requestModel));
5959

6060
var modelState = actionResultTestBuilder.Controller.ModelState;
6161

MyWebApi.Tests/BuildersTests/ResponseModelsTests/ResponseModelErrorDetailsTestBuilderTests.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,11 @@ public void ThatEqualsShouldNotThrowExceptionWhenProvidedMessageIsValid()
1919
.ShouldHaveModelStateFor<RequestModel>()
2020
.ContainingNoModelStateErrorFor(m => m.NonRequiredString)
2121
.ContainingModelStateErrorFor(m => m.RequiredString).ThatEquals("The RequiredString field is required.")
22+
.And()
2223
.ContainingModelStateErrorFor(m => m.RequiredString)
24+
.And()
2325
.ContainingNoModelStateErrorFor(m => m.NotValidateInteger)
26+
.And()
2427
.ContainingModelStateError("RequiredString")
2528
.ContainingModelStateErrorFor(m => m.Integer).ThatEquals(string.Format("The field Integer must be between {0} and {1}.", 1, int.MaxValue))
2629
.ContainingModelStateError("RequiredString")
@@ -41,6 +44,7 @@ public void ThatEqualsShouldThrowExceptionWhenProvidedMessageIsValid()
4144
.Calling(c => c.ModelStateCheck(requestModelWithErrors))
4245
.ShouldHaveModelStateFor<RequestModel>()
4346
.ContainingNoModelStateErrorFor(m => m.NonRequiredString)
47+
.And()
4448
.ContainingModelStateErrorFor(m => m.RequiredString).ThatEquals("RequiredString field is required.")
4549
.ContainingModelStateErrorFor(m => m.Integer).ThatEquals(string.Format("Integer must be between {0} and {1}.", 1, int.MaxValue));
4650
}

MyWebApi/Builders/Actions/ShouldHaveModelState.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,23 @@ public partial class ActionResultTestBuilder<TActionResult>
1818
/// <returns>Response model test builder.</returns>
1919
public IResponseModelErrorTestBuilder<TRequestModel> ShouldHaveModelStateFor<TRequestModel>()
2020
{
21-
return new ResponseModelErrorTestBuilder<TRequestModel>(Controller, ActionName);
21+
return new ResponseModelErrorTestBuilder<TRequestModel>(this.Controller, this.ActionName);
2222
}
2323

2424
/// <summary>
2525
/// Checks whether the tested action's provided model state is valid.
2626
/// </summary>
2727
public void ShouldHaveValidModelState()
2828
{
29-
CheckValidModelState();
29+
this.CheckValidModelState();
3030
}
3131

3232
/// <summary>
3333
/// Checks whether the tested action's provided model state is not valid.
3434
/// </summary>
3535
public void ShouldHaveInvalidModelState()
3636
{
37-
if (Controller.ModelState.Count == 0)
37+
if (this.Controller.ModelState.Count == 0)
3838
{
3939
throw new ResponseModelErrorAssertionException(string.Format(
4040
"When calling {0} action in {1} expected to have invalid model state, but was in fact valid.",

MyWebApi/Builders/Contracts/IResponseModelErrorDetailsTestBuilder.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,11 @@ IResponseModelErrorDetailsTestBuilder<TResponseModel> ContainingModelStateErrorF
6161
/// <returns>Response model error details test builder.</returns>
6262
IResponseModelErrorTestBuilder<TResponseModel> ContainingNoModelStateErrorFor<TMember>(
6363
Expression<Func<TResponseModel, TMember>> memberWithNoError);
64+
65+
/// <summary>
66+
/// And method for better readability when chaining error message tests.
67+
/// </summary>
68+
/// <returns>Response model error details test builder.</returns>
69+
IResponseModelErrorTestBuilder<TResponseModel> And();
6470
}
6571
}

MyWebApi/Builders/Contracts/IResponseModelErrorTestBuilder{TResponseModel}.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,11 @@ public interface IResponseModelErrorTestBuilder<TResponseModel> : IResponseModel
3232
/// <returns>This instance in order to support method chaining.</returns>
3333
IResponseModelErrorTestBuilder<TResponseModel> ContainingNoModelStateErrorFor<TMember>(
3434
Expression<Func<TResponseModel, TMember>> memberWithNoError);
35+
36+
/// <summary>
37+
/// And method for better readability when chaining error message tests.
38+
/// </summary>
39+
/// <returns>Response model error details test builder.</returns>
40+
IResponseModelErrorTestBuilder<TResponseModel> And();
3541
}
3642
}

MyWebApi/Builders/ResponseModels/ResponseModelErrorDetailsTestBuilder.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,15 @@ public IResponseModelErrorTestBuilder<TResponseModel> ContainingNoModelStateErro
143143
return this.responseModelErrorTestBuilder.ContainingNoModelStateErrorFor(memberWithNoError);
144144
}
145145

146+
/// <summary>
147+
/// And method for better readability when chaining error message tests.
148+
/// </summary>
149+
/// <returns>Response model error details test builder.</returns>
150+
public IResponseModelErrorTestBuilder<TResponseModel> And()
151+
{
152+
return this.responseModelErrorTestBuilder;
153+
}
154+
146155
private void ThrowNewResponseModelErrorAssertionException(string messageFormat, string operation)
147156
{
148157
throw new ResponseModelErrorAssertionException(string.Format(

MyWebApi/Builders/ResponseModels/ResponseModelErrorTestBuilder{TResponseModel}.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,15 @@ public IResponseModelErrorTestBuilder<TResponseModel> ContainingNoModelStateErro
8484
return this;
8585
}
8686

87+
/// <summary>
88+
/// And method for better readability when chaining error message tests.
89+
/// </summary>
90+
/// <returns>Response model error details test builder.</returns>
91+
public IResponseModelErrorTestBuilder<TResponseModel> And()
92+
{
93+
return this;
94+
}
95+
8796
private void ThrowNewResponseModelErrorAssertionException(string messageFormat, string errorKey)
8897
{
8998
throw new ResponseModelErrorAssertionException(string.Format(

README.md

Lines changed: 183 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,183 @@
1-
# MyWebApi
1+
# MyWebApi - ASP.NET Web API Fluent Testing Framework
2+
------------------------------------
3+
4+
MyWebApi is unit testing framework providing easy fluent interface to test the ASP.NET Web API framework. Inspired by [TestStack.FluentMVCTesting](https://github.com/TestStack/TestStack.FluentMVCTesting) and [ChaiJS](https://github.com/chaijs/chai)
5+
6+
[![Build status](https://ci.appveyor.com/api/projects/status/738pm1kuuv7yw1t5?svg=true)](https://ci.appveyor.com/project/ivaylokenov/mywebapi)
7+
8+
## How to use
9+
10+
### Controller instantiation
11+
12+
You have a couple of options from which you can setup the controller you want to test. The framework gives you static `MyWebApi` class from which the test builder starts:
13+
14+
```c#
15+
// instantiates controller with parameterless constructor
16+
MyWebApi
17+
.Controller<WebApiController>();
18+
19+
// instantiates controller with constructor function to resolve dependencies
20+
MyWebApi
21+
.Controller(() => new WebApiController(mockedInjectedService));
22+
```
23+
24+
### Calling actions
25+
26+
You can call any action using lambda expression. All parameter values will be resolved and model state validation will be performed on them:
27+
28+
```c#
29+
// calls action with no parameters
30+
MyWebApi
31+
.Controller<WebApiController>()
32+
.Calling(c => c.SomeAction());
33+
34+
// calls action with parameters
35+
MyWebApi
36+
.Controller<WebApiController>()
37+
.Calling(c => c.SomeAction(requestModel));
38+
39+
// calls async action
40+
MyWebApi
41+
.Controller<WebApiController>()
42+
.CallingAsync(c => c.SomeActionAsync());
43+
```
44+
45+
### Model state validation
46+
47+
You can test whether model state is valid/invalid or contains any specific error:
48+
49+
```c#
50+
// tests whether model state is valid
51+
MyWebApi
52+
.Controller<WebApiController>()
53+
.Calling(c => c.SomeAction(requestModel))
54+
.ShouldHaveValidModelState();
55+
56+
// tests whether model state is not valid
57+
MyWebApi
58+
.Controller<WebApiController>()
59+
.Calling(c => c.SomeAction(requestModel))
60+
.ShouldHaveInvalidModelState();
61+
62+
// tests whether model state error exists (or does not exist) for specific key (not recommended because of magic string)
63+
MyWebApi
64+
.Controller<WebApiController>()
65+
.Calling(c => c.SomeAction(requestModel))
66+
.ShouldHaveModelStateFor<RequestModel>()
67+
.ContainingModelStateError("propertyName")
68+
.And() // calling And method is not necessary but provides better readability
69+
.ContainingNoModelStateError("anotherPropertyName");
70+
71+
// tests whether model state error exists by using lambda expression
72+
MyWebApi
73+
.Controller<WebApiController>()
74+
.Calling(c => c.SomeAction(requestModel))
75+
.ShouldHaveModelStateFor<RequestModel>()
76+
.ContainingModelStateErrorFor(m => m.SomeProperty)
77+
.And()
78+
.ContainingNoModelStateErrorFor(m => m.AnotherProperty);
79+
80+
// tests the error message for specific property
81+
MyWebApi
82+
.Controller<WebApiController>()
83+
.Calling(c => c.SomeAction(requestModel))
84+
.ShouldHaveModelStateFor<RequestModel>()
85+
.ContainingModelStateErrorFor(m => m.SomeProperty).ThatEquals("Error message") // error message must be equal to the provided string
86+
.And()
87+
.ContainingModelStateErrorFor(m => m.SecondProperty).BeginningWith("Error") // error message must begin with the provided string
88+
.And()
89+
.ContainingModelStateErrorFor(m => m.ThirdProperty).EndingWith("message") // error message must end with the provided string
90+
.And()
91+
.ContainingModelStateErrorFor(m => m.SecondProperty).Containing("ror mes"); // error message must contain the provided string
92+
```
93+
94+
### Action results
95+
96+
You can test for specific return values or the default IHttpActionResult types:
97+
98+
#### Generic result
99+
100+
Useful where the action does not return IHttpActionResult
101+
102+
```c#
103+
// tests whether the action returns certain type by providing generic parameter
104+
MyWebApi
105+
.Controller<WebApiController>()
106+
.Calling(c => c.SomeAction())
107+
.ShouldReturn<ResponseModel>();
108+
109+
// tests whether the action returns certain type by using typeof
110+
MyWebApi
111+
.Controller<WebApiController>()
112+
.Calling(c => c.SomeAction())
113+
.ShouldReturn(typeof(ResponseModel));
114+
115+
// tests whether the action returns generic model
116+
MyWebApi
117+
.Controller<WebApiController>()
118+
.Calling(c => c.SomeAction())
119+
.ShouldReturn(typeof(IList<>)); // works with IEnumerable<> (or IList<ResponseModel>) too by using polymorphism
120+
```
121+
122+
#### Ok result
123+
124+
```c#
125+
// tests whether the action returns OkResult
126+
MyWebApi
127+
.Controller<WebApiController>()
128+
.Calling(c => c.SomeAction())
129+
.ShouldReturnOk();
130+
131+
// tests whether the action returns OkResult with no response model
132+
MyWebApi
133+
.Controller<WebApiController>()
134+
.Calling(c => c.SomeAction())
135+
.ShouldReturnOk()
136+
.WithNoResponseModel();
137+
138+
// tests whether the action returns OkResult with specific response model type
139+
MyWebApi
140+
.Controller<WebApiController>()
141+
.Calling(c => c.SomeAction())
142+
.ShouldReturnOk()
143+
.WithResponseModel<ResponseModel>();
144+
145+
// tests whether the action returns OkResult with specific object
146+
MyWebApi
147+
.Controller<WebApiController>()
148+
.Calling(c => c.SomeAction())
149+
.ShouldReturnOk()
150+
.WithResponseModel(someResponseModelObject);
151+
152+
// tests whether the action returns OkResult with specific response model passing certain assertions
153+
MyWebApi
154+
.Controller<WebApiController>()
155+
.Calling(c => c.SomeAction())
156+
.ShouldReturnOk()
157+
.WithResponseModel<ResponseModel>(m =>
158+
{
159+
Assert.AreEqual(1, m.Id);
160+
Assert.AreEqual("Some property value", m.SomeProperty);
161+
});
162+
163+
// tests whether the action returns OkResult with specific response model passing a predicate
164+
MyWebApi
165+
.Controller<WebApiController>()
166+
.Calling(c => c.SomeAction())
167+
.ShouldReturnOk()
168+
.WithResponseModel<ResponseModel>(m => m.Id == 1);
169+
170+
// tests for model state errors for the response model (not very useful in practice)
171+
MyWebApi
172+
.Controller<WebApiController>()
173+
.Calling(c => c.SomeAction())
174+
.ShouldReturnOk()
175+
.WithResponseModel<ResponseModel>()
176+
.ContainingModelStateErrorFor(m => m.SomeProperty).ThatEquals("Error message")
177+
.And()
178+
.ContainingNoModelStateErrorFor(m => m.AnotherProperty);
179+
```
180+
181+
## Any questions, comments or additions?
182+
183+
Leave an issue on the [issues page](https://github.com/ivaylokenov/MyWebApi/issues) or send a [pull request](https://github.com/ivaylokenov/MyWebApi/pulls).

0 commit comments

Comments
 (0)