I've got a Loopback 4 controller, and I want to unit test it. How do I mock the request parameter? Here's the constructor to my controller:
constructor(#inject(RestBindings.Http.REQUEST) private req: Request,
#service(HttpService) private httpService: HttpService,
#service(LocalRequestService) private localRequestService: LocalRequestService) {}
I tried mocking it with createStubInstance(Request), but that gave me this error:
Argument of type 'StubbedInstanceWithSinonAccessor<Request>' is not assignable to parameter of type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>'.
Type 'StubbedInstanceWithSinonAccessor<Request>' is missing the following properties from type 'Request<ParamsDictionary, any, any, ParsedQs, Record<string, any>>': get, header, accepts, acceptsCharsets, and 68 more.ts(2345)
stubExpressContext can be used.
const context = stubExpressContext();
const controller = new MyController(context.request);
Options can be passed to stubExpressContext.
More..
Related
I am trying to unit test the following method with Moq, but I am running into issues with the accessibility of manipulating some properties on these classes, and even mocking them up for that matter.
public string GetClientIpAddress(HttpRequestMessage request)
{
if (request.Properties.ContainsKey("MS_HttpContext"))
return ((HttpContextWrapper)request.Properties["MS_HttpContext"]).Request.UserHostAddress;
if (request.Properties.ContainsKey(RemoteEndpointMessageProperty.Name))
return ((RemoteEndpointMessageProperty)request.Properties[RemoteEndpointMessageProperty.Name]).Address;
return "IP Address Unavailable";
}
In order to test, I have created an instance of HttpRequestMessage that I am passing in as a parameter. I am then adding a mock of HttpRequest and HttpContext, like so...
// Assign
var mockHttpRequestBase = new Mock<HttpRequest>();
mockHttpRequestBase.Setup(m => m.UserHostAddress).Returns("127.0.0.1");
var mockHttpContext = new Mock<HttpContext>();
mockHttpContext.Setup(m => m.Request).Returns(mockHttpRequestBase.Object);
var httpRequestMessage = new HttpRequestMessage();
httpRequestMessage.Properties.Add(new KeyValuePair<string, object>("MS_HttpContext", mockHttpRequestBase.Object));
var apiCachedController = new ApiCachedController();
// Act
var address = apiCachedController.GetClientIpAddress(httpRequestMessage);
// Assert
Assert.AreEqual(address, "127.0.0.1");
EDIT: Sorry for not being clearer on the specific problem(s) I'm having. HttpRequest is unable to be mocked. I get a NotSupportedException that "type to mock must be an interface or an abstract or non-sealed class." I've tried using HttpRequestBase and HttpContextWrapper in place of HttpRequest and HttpContext, respectively, but I receive an InvalidCastException stating, "Unable to cast object of type 'Castle.Proxies.HttpRequestBaseProxy' to type 'System.Web.HttpContextWrapper'."
Moq can't create a Mock object from a class itself, you've got to create (and use) an interface in your code and then create a Mock from that.
Grails 2.3.10.
How can I configure the available mime types for content type negotiation in a Grails Spock test?
When I try to tell the controller to produce JSON content, it seems to want to return an HTTP 406 error. I send in the Accept header in my test code; but, the parser is not able to match it because HTML is the only MIME type that's configured.
My use case...
I have implemented a controller action using the Grails respond method which can return a JSON response. When I hit the endpoint using a REST API call, I am able to get back JSON output (even if no Accept header is specified).
The controller code:
#Transactional(readOnly = true)
class MyObjectController {
static allowedMethods = [save: 'POST']
static responseFormats = ['json']
def myService
#Transactional
def save(MyObject obj) {
obj.validate()
if (obj.hasErrors()) {
respond obj.getErrors(), [status: BAD_REQUEST]
}
myService.addNewCustomer(obj)
respond obj, [formats: responseFormats]
}
}
And my test code:
#TestFor(MyObjectController)
class MyObjectControllerSpec extends Specification {
def setup() {
}
def cleanup() {
}
void "test save - json success"() {
given:
def myObj = new MyObject()
controller.myService= Mock(MyObjectService)
when:
request.addHeader "Accept", "application/json"
controller.save(individual)
then:
response.status == HttpStatus.CREATED.value()
response.text == "{}" //.text is giving me an empty string
response.json.x == x //.json throws an exception (parsing an empty string)
}
}
I have verified in the debugger that obj has a valid value and that the respond method is invoked on the last line of the controller action.
What I am finding is that inside the Grails ResponseMimeTypesApi class, the DefaultAcceptHeaderParser is getting constructed with only the HTML mime type. Even though the JSON accept header is being read correctly, the DefaultAcceptHeaderParser isn't able to understand it because no mime types are configured.
How do I control the mime types that get sent to ResponseMimeTypesApi in my unit test spec?
Edit
I have also tried setting the response.format property, as suggested in this answer; but, to no avail.
If you assign a value to request.json that will set the content type. You can also set the content type explicitly with something like request.contentType = 'application/json' or request.contentType = JSON_CONTENT_TYPE. For a list of the content type constants available in your unit tests see the "Testing Mime Type Handling" section under http://grails.org/doc/latest/guide/testing.html#unitTestingControllers.
Another resource to look at is the unit tests at https://github.com/grails/grails-core/blob/4f8d1a605cde60a4a00021102959578dae8bc5a8/grails-test-suite-web/src/test/groovy/org/codehaus/groovy/grails/web/binding/json/JsonBindingSpec.groovy and https://github.com/grails/grails-core/blob/801d507cf3fec5866baa14f6d6b0acd05aa5fb56/grails-test-suite-web/src/test/groovy/org/codehaus/groovy/grails/web/binding/xml/XmlBindingSpec.groovy.
I hope that helps.
EDIT:
To clarify...
I mentioned assigning a value to request.json. The value you assign to that of course is JSON, not the content type. Like this...
request.json = '''
{
"name": "Douglas", "age": "42"
}
'''
When you do that, the content type gets set automatically.
I found that I could write the test that I wanted by converting from a unit to an integration test.
Move the test to the /integration path.
Change the test spec to extend IntegrationSpec
#Autowire the class under test instead of using #TestFor:
Set controller.response.format = 'json' and call controller.request.addHeader 'Accept', 'application/json'
I'm having a bit of a tough time trying to understand how to write tests in Scala when implicit parameters are involved.
I have the following (short version) of my code and test:
Implementation (Scala 2.10, Spray and Akka):
import spray.httpx.SprayJsonSupport._
import com.acme.ResultJsonFormat._
case class PerRequestIndexingActor(ctx: RequestContext) extends Actor with ActorLogging {
def receive = LoggingReceive {
case AddToIndexRequestCompleted(result) =>
ctx.complete(result)
context.stop(self)
}
}
object ResultJsonFormat extends DefaultJsonProtocol {
implicit val resultFormat = jsonFormat2(Result)
}
case class Result(code: Int, message: String)
Test (Using ScalaTest and Mockito):
"Per Request Indexing Actor" should {
"send the HTTP Response when AddToIndexRequestCompleted message is received" in {
val request = mock[RequestContext]
val result = mock[Result]
val perRequestIndexingActor = TestActorRef(Props(new PerRequestIndexingActor(request)))
perRequestIndexingActor ! AddToIndexRequestCompleted(result)
verify(request).complete(result)
}
}
This line, verify(request).complete(result) uses an implicit Marshaller to turn Result into JSON.
I can bring a marshaller in to scope by adding implicit val marshaller: Marshaller[Result] = mock[Marshaller[Result]] but when I run the test a different instance of Marshaller is used, so the verification fails.
Even explicitly passing the mock Marshaller to complete fails.
So, can any one advise how to create a mock object for an implicit parameter and make sure that instance is the one used?
This is a perfect situation to use a Matcher from Mockito for the marshaller arg. You should not need to mock out the implicit marshaller. All you really want to do is verify that complete was called with a result matching what you expected and also some instance of the marshaller. First, if you haven't already done it, bring the Mockito matchers into scope with an import like so:
import org.mockito.Matchers._
Then, if you wanted reference matching on the result, you could verify like so:
verify(request).complete(same(result))(any[classOf[Marshaller[Result]]])
Or, if you wanted equals matching on result you could do:
verify(request).complete(eq(result))(any(classOf[Marshaller[Result]]))
The trick with matchers is that once you use one for one arg, you have to use them for all args, so that's why we have to use one for result too.
I have a method called ProcessPayment() that I'm developing via BDD and mspec. I need help with a new challenge. My user story says:
Given a payment processing context,
When payment is processed with valid payment information,
Then it should return a successful gateway response code.
To set up the context, I am stubbing my gateway service using Moq.
_mockGatewayService = Mock<IGatewayService>();
_mockGatewayService.Setup(x => x.Process(Moq.It.IsAny<PaymentInfo>()).Returns(100);
Here's the spec:
public class when_payment_is_processed_with_valid_information {
static WebService _webService;
static int _responseCode;
static Mock<IGatewayService> _mockGatewayService;
static PaymentProcessingRequest _paymentProcessingRequest;
Establish a_payment_processing_context = () => {
_mockGatewayService = Mock<IGatewayService>();
_mockGatewayService
.Setup(x => x.Process(Moq.It.IsAny<PaymentInfo>())
.Returns(100);
_webService = new WebService(_mockGatewayService.Object);
_paymentProcessingRequest = new PaymentProcessingRequest();
};
Because payment_is_processed_with_valid_payment_information = () =>
_responseCode = _webService.ProcessPayment(_paymentProcessingRequest);
It should_return_a_successful_gateway_response_code = () =>
_responseCode.ShouldEqual(100);
It should_hit_the_gateway_to_process_the_payment = () =>
_mockGatewayService.Verify(x => x.Process(Moq.It.IsAny<PaymentInfo>());
}
The method should take a `PaymentProcessingRequest' object (not domain obj), map that obj to a domain obj, and pass the domain obj to the stubbed method on the gateway service. The response from the gateway service is what gets returned by the method. However, because of the way I am stubbing my gateway service method, it doesn't care what gets passed in to it. As a result, it seems I have no way to test whether or not the method maps the request object to the domain object properly.
When can I do here and still adhere to BDD?
To check that the object sent to your IGatewayService is correct, you can use a callback to set a reference to the domain object. You can then write your assertions on properties of that object.
Example:
_mockGatewayService
.Setup(x => x.Process(Moq.It.IsAny<PaymentInfo>())
.Callback<PaymentInfo>(paymentInfo => _paymentInfo = paymentInfo);
So from what I understand,
You want to test the mapping logic in the WebService.ProcessPayment method ; there is a mapping of an input parameter A to an object B, which is used as an input to a GateWayService collaborator.
It should_hit_the_gateway_to_process_the_payment = () =>
_mockGatewayService.Verify(
x => x.Process( Moq.It.Is<PaymentInfo>( info => CheckPaymentInfo(info) ));
Use the Moq It.Is constraint which takes in a Predicate (a test for the argument to satisfy). Implement CheckPaymentInfo to assert against the expected PaymentInfo.
e.g. to check if Add is Passed an even number as an argument,
mock.Setup(foo => foo.Add(It.Is<int>(i => i % 2 == 0))).Returns(true);
How do I test which view was rendered from a controller action if what I get is a T4MVC_ActionResult? Under normal circumstances I should be able to directly use TestHelper's methods, like in the examples:
pooController.Details().AssertViewRendered().ForView("Details")
...but, since through T4MVC I get a T4MVC_ActionResult instead of a ViewResult, the part AssertViewRendered<>().ForView("Details") fails. What alternative do I have if I want to test which view was invoked?
UPDATE:
Here's the test code:
[TestMethod]
public void Theme_Controller_Details_Action_Returns_Details_View()
{
var builder = new TestControllerBuilder();
var mockThemeRepository = new Mock<IThemeRepository>();
var themeController = builder.CreateController<Evalgrid.Website.Controllers.ThemeController>(mockThemeRepository.Object);
builder.InitializeController(themeController);
var result = themeController.Details();
result.AssertViewRendered().ForView("Details");
}
I used the debugger setting a breakpoint after the result line, and its variable type is T4MVC_ActionResult, while themeController is Evalgrid.Website.controllers.ThemeController. Note that I have used the fully qualified name of the controller.
I get this:
Expected result to be of type
ViewResult. It is actually of type
T4MVC_ActionResult.
I don't know what's going on.
Actually, T4MVC should not make a difference here. If you directly instantiate your controller and call an action method, you'll get the same thing back whether you use T4MVC or not. i.e. you won't get a T4MVC_ActionResult.
It's only when you write MVC.Foo.Details() that you'll get a T4MVC_ActionResult. That's because MVC.Foo returns an instance of a derived class which does special thing, and not directly your controller class.
Does that make sense?
Update: I'm confused, as looking at the sources for TestControllerBuilder.CreateController, it has:
public T CreateController<T>(params object[] constructorArgs) where T : Controller
{
var controller = (Controller)Activator.CreateInstance(typeof(T), constructorArgs);
InitializeController(controller);
return controller as T;
}
So it's directly instantiating the type that you pass in, which should just call your normal action.
One question about your code: does your Details action method take any parameters? If so, that would explain the problem, as you're calling it with no params, which would be a T4MVC method added in the partial class.