how to assert values from anonymous type passed from an oknegatiatedresult - unit-testing

I'm currently unit testing controllers, and my controller code goes like this
public IHttpActionResult Submit(){
...
return Ok(new { Success = true, ErrorMessage = "", RedirectUrl = "http://myurl" });
}
in my unit test method goes like this
var result = sut.Submit(); //result is not null
result is not null however I can't get any of the anonymous properties sent by the method I called. no other properties like "value" or "content" available from VS intelisense. any idea how to get those values so I can assert them further?

Related

Is it useless to mock an interface's behavior if it's not to be called in the test

Do i need to mock interfaces that does not call, for instance user name and password field is empty? I'm trying to write test first but confused if mocks should be used.
My login test
private val authRepository: AuthRepository = mockk()
private val userManager: AccountManager = mockk()
private lateinit var authUseCase: AuthUseCase
#BeforeEach
fun setUp() {
clearMocks(authRepository)
clearMocks(userManager)
authUseCase = AuthUseCase(authRepository, userManager)
}
/**
* Scenario: Login check with empty fields:
* * Given I am on the login page
* * When I enter empty username
* And I enter empty password
* And I click on the "Login" button
* * Then I get empty fields error.
*/
#Test
fun `Empty fields result empty fields error`() {
// Given
// When
val expected = authUseCase.login("", "", false)
// Then
verify(exactly = 0) {
authRepository.login(or(any(), ""), or(any(), ""), any())
}
expected assertEquals EMPTY_FIELD_ERROR
}
Do i have to mock interface for the given part of the test or AccountManager even though they are not called since user name and/or fields are empty?
This is the final version of login method i intend to write after tests
class AuthUseCase(
private val authRepository: AuthRepository,
private val accountManager: AccountManager
) {
private var loginAttempt = 1
/*
STEP 1: Throw exception for test to compile and fail
*/
// fun login(
// userName: String,
// password: String,
// rememberMe: Boolean = false
// ): AuthenticationState {
// throw NullPointerException()
// }
/*
STEP3: Check if username or password is empty
*/
// fun login(
// userName: String,
// password: String,
// rememberMe: Boolean = false
// ): AuthenticationState {
//
//
// if (userName.isNullOrBlank() || password.isNullOrBlank()) {
// return EMPTY_FIELD_ERROR
// }else {
// throw NullPointerException()
// }
//
// }
/**
* This is the final and complete version of the method.
*/
fun login(
userName: String,
password: String,
rememberMe: Boolean
): AuthenticationState {
return if (loginAttempt >= MAX_LOGIN_ATTEMPT) {
MAX_NUMBER_OF_ATTEMPTS_ERROR
} else if (userName.isNullOrBlank() || password.isNullOrBlank()) {
EMPTY_FIELD_ERROR
} else if (!checkUserNameIsValid(userName) || !checkIfPasswordIsValid(password)) {
INVALID_FIELD_ERROR
} else {
// Concurrent Authentication via mock that returns AUTHENTICATED, or FAILED_AUTHENTICATION
val authenticationPass =
getAccountResponse(userName, password, rememberMe)
return if (authenticationPass) {
loginAttempt = 0
AUTHENTICATED
} else {
loginAttempt++
FAILED_AUTHENTICATION
}
}
}
private fun getAccountResponse(
userName: String,
password: String,
rememberMe: Boolean
): Boolean {
val authResponse =
authRepository.login(userName, password, rememberMe)
val authenticationPass = authResponse?.authenticated ?: false
authResponse?.token?.let {
accountManager.saveToken(it)
}
return authenticationPass
}
private fun checkUserNameIsValid(field: String): Boolean {
return field.length >15 && field.endsWith("#example.com")
}
private fun checkIfPasswordIsValid(field: String): Boolean {
return field.length in 6..10
}
}
Should i only mock when all other states and passed i get a mock response from repository and interaction with account manager occurs?
What should be given section of the test?
Edit:
I updated given section of this test to
#Test
fun `Empty fields result empty fields error`() {
// Given
every { authRepository.login(or(any(), ""), or(any(), "")) } returns null
// When
val expected = authUseCase.login("", "", false)
// Then
verify(exactly = 0) { authRepository.login(or(any(), ""), or(any(), "")) }
expected assertThatEquals EMPTY_FIELD_ERROR
}
Is there something wrong with this kind of behavior testing?
I would suggest that you don't need the verify in the "Empty fields result empty fields error" test. I would also suggest you write separate tests for each empty field. If you were doing strict TDD you would be testing each condition as you wrote the code. i.e.
'Empty username should error" would be the first test and the first condition tested, then "Empty password should error" the next (after you have done two separate written your second test your code may look like
if (userName.isNullOrBlank()) {
return EMPTY_FIELD_ERROR
}
if (password.isNullOrBlank() {
return EMPTY_FIELD_ERROR
}
Once both the tests above pass you could refactor to
if (userName.isNullOrBlank() || password.isNullOrBlank()) {
EMPTY_FIELD_ERROR
}
Once you start testing the conditional statements for checkUserNameIsValid and checkIfPasswordIsValid, you would need to introduce the authRepository and accountManager to your class (constructor injection) and then you would need to start mocking the calls as you use them. Generally mocking frameworks will fake an object (i.e. the code will run but won't return any meaningful result). You should aim to return actual mock data when you want to test specific behavior i.e. you should be returning a valid object from the authRepository.login when you are testing for a successful login. Generally I stay away from using setup methods in the #BeforeEach and use either a factory method or builder to create my class under test. I am unfamiliar with the kotlin syntax so can at best do some sudo code to demonstrate how your builder or factory functions may look like.
// overloaded factory function
fun create() {
val authRepository: AuthRepository = mockk()
val userManager: AccountManager = mockk()
return AuthUseCase(authRepository, userManager);
}
fun create(authRepository: AuthRepository) {
val userManager: AccountManager = mockk()
return AuthUseCase(authRepository, userManager);
}
fun create(authRepository: AuthRepository, userManager: AccountManager) {
return AuthUseCase(authRepository, userManager);
}
You will need to have a look at how to create a builder in kotlin but the end result you would be looking for is that the builder always starts setting the dependencies for you class under test as mocks that do nothing but allows you to change those mocks.
e.g.
AuthUseCase authUseCase = AuthUseCaseBuilder.Create().WithAuthRepository(myMockAuthRepository).Build();
One final thing. I purposely left out discussing loginAttempt check above as to me it looks like the AuthUseCase is a service class that will be used by multiple users and live for the lifetime of the request in which case you don't want to maintain state within the class (i.e. the loginAttempt variable has the same lifetime as the class). It would be better to record the attempts per username in a database table and the attempt count would need to be reset after each successful login.
Hope this helps.

Postman API Tests

I have a response body like
{
"agreementId": "agreement900",
"Status": "ONHOLD"
}
The value of the status can be one of
['PAID','CANCELLED','COOLINGOFF','ONHOLD','COOLINGOFF','PAID']
I need to write a generic test to verify that the body.Status is always among the specified array.
I tried something like this
var data = ['PAID','CANCELLED','COOLINGOFF','ONHOLD','COOLINGOFF','PAID'];
pm.test("Verify Body value", function () {
let testResult = data.find((each)=>{
pm.expect(each.payoutStatus).to.equal(jsonData.payoutStatus)
});
});
But received the following error: Verify Body value | AssertionError: expected undefined to equal 'ONHOLD'
Deepak, welcome to SO
I am not sure about edge cases nor performance, but this can be a way of achieving it:
var myStatus = pm.response.json().Status;
var myEnum = ['Pig','Chicken','Cow'];
pm.test("Status belongs to the ENUMs", function () {
pm.expect(myEnum).to.include(myStatus);
});

How to mock a coldbox.system.web.context.RequestContext and initialize it?

I have inherited some code at my work. A previous person wrote some ColdFusion code that looks like this:
public struct function GetData(Event,RC,PRC){};
I am trying to write a unit-test for this function but I don't know how to mock this "Event".
This is my code:
mockEvent=createMock(className='coldbox.system.web.context.RequestContext');
ColdBox is throwing an exception that I must initialize mockEvent when I run that code. Does anyone see what I am doing wrong?
Within a test, which extends the Coldbox.system.testing.BaseTestCase, you can easily mock the Coldbox RequestContext object.
You need to set the following variable in your tests : this.loadColdbox=true and then, after you call the super.beforeAll() method from within your test, each time you call setup(), the context is rebuilt.
After that, the RequestContext is available with the method getRequestContext(). Here's an example of mocking the request getHTTPMethod() function (I typically use this method to mock API methods which are configured to respond to different HTTP verbs):
function newEventArgs( method = "GET" ) {
//rebuild the context
setup();
//mock the context
var event = getRequestContext();
prepareMock( event ).$( "getHTTPMethod", arguments.method );
var rc = event.getCollection();
var prc = event.getCollection( private=true );
prc.response = getWirebox().getInstance( "APIResponse" );
return {
"event":event,
"rc":rc,
"prc":prc
};
}
Then you might test a create method like so:
it( "Tests Widgets.create", function(){
var testWidget = {
"name" : "Test Widget"
};
var eventArgs = newEventArgs( "POST" );
structAppend( eventArgs.rc, testWidget, true );
var event = execute(
route="/api/v1/widgets"
eventArgs=eventArgs
);
expect( event.getPrivateValue( "response" ).getStatusCode() ).toBe( 201, "Event response did not return the proper status code." );
});

Unit Test NService.Send from an API Controller

I have an API Controller which publishes a command using NServiceBus. I am using NUnit and NSubstitute for testing. I want to test that certain properties from the model are populated on the command
Here is my controller with a route.
[RoutePrefix("api/fileService")]
public class FileServiceController : ApiController
{
[HttpPost]
[Route("releasefile")]
public async Task<IHttpActionResult> ReleaseFile(FileReleaseAPIModels.ReleaseFileModel model)
{
var currentUser = RequestContext.Principal?.Identity as ClaimsIdentity;
if (model.FileType.Equals("ProductFile"))
{
_logger.Info($"Releasing Product files for date: {model.FileDate.ToShortDateString()} ");
_bus.Send<IReleaseProductFiles>("FileManager.Service", t =>
{
t.FileId = Guid.NewGuid();
t.RequestedDataDate = model.FileDate;
t.RequestingUser = currentUser?.Name;
t.RequestDateTime = DateTime.Now;
});
}
return Ok();
}
}
In my test, I substitute(mock) Ibus and try to validate the call received. Here is the test method:
[Test]
public async Task TestReleaseProductsFile()
{
var bus = Substitute.For<IBus>();
var dbContent = _container.Resolve<IFileManagerDbContext>();
var apiContext = new FileServiceController(bus, dbContent);
//Create a snapshot
var releaseDate = DateTime.Now.Date;
var result = await apiContext.ReleaseFile(new ReleaseFileModel
{
FileDate = releaseDate,
FileType = "ProductFile"
});
Assert.That(result, Is.Not.Null, "Result is null");
Assert.That(result, Is.TypeOf<OkResult>(), "Status code is not ok");
bus.Received(1)
.Send<IReleaseProductFiles>(Arg.Is<string>("FileManager.Service"), Arg.Is<Action<IReleaseProductFiles>>(
action =>
{
action.FileId = Guid.NewGuid();
action.RequestedDataDate = releaseDate;
action.RequestingUser = String.Empty;
action.RequestDateTime = DateTime.Now;
}));
}
This results in error - even though the message is actually sent. Here is the error message:
NSubstitute.Exceptions.ReceivedCallsException : Expected to receive exactly 1 call matching:
Send<IReleaseProductFiles>("Capelogic.Service", Action<IReleaseProductFiles>)
Actually received no matching calls.
Received 1 non-matching call (non-matching arguments indicated with '*' characters):
Send<IReleaseProductFiles>("Capelogic.Service", *Action<IReleaseProductFiles>*)
I am obviously missing something obvious here.
The problem here is with the Action<IReleaseProductFiles> argument to Send -- we can't automatically tell if two different actions are the same. Instead, NSubstitute relies on the references being equivalent. Because both the test and the production code create their own Action instance, these will always be different and NSubstitute will say the calls don't match.
There are a few different options for testing this. These examples relate to Expression<Func<>>, but the same ideas apply to Action<>s.
In this case I'd be tempted to test this indirectly:
[Test]
public async Task TestReleaseProductsFile()
{
var bus = Substitute.For<IBus>();
var returnedProductFiles = Substitute.For<IReleaseProductFiles>();
// Whenever bus.Send is called with "FileManager.Service" arg, invoke
// the given callback with the `returnedProductFiles` object.
// We can then make sure the action updates that object as expected.
bus.Send<IReleaseProductFiles>(
"FileManager.Service",
Arg.Invoke<IReleaseProductFiles>(returnedProductFiles));
// ... remainder of test ...
Assert.That(result, Is.TypeOf<OkResult>(), "Status code is not ok");
Assert.That(returnedProductFiles.FileId, Is.Not.EqualTo(Guid.Empty));
Assert.That(returnedProductFiles.RequestedDataDate, Is.EqualTo(releaseDate));
Assert.That(returnedProductFiles.RequestingUser, Is.EqualTo(String.Empty));
}
I'd recommend having a look through the previously mentioned answer though to see if there is a better fit for your situation.

How do I check if a view's model is correct?

I have an "Index" controller method which returns a view with a Model that is a List<WhatsNew>. I am trying to validate this method in a unit test but it gives me an error as it is expecting a string.
Controller
public ActionResult Index()
{
return View("Index", GetWhatsNew());
}
public List<WhatsNew> GetWhatsNew()
{
WCMSDataContext wcmsContext = new WCMSDataContext();
return (from p in wcmsContext.WhatsNews select p).ToList();
}
Unit Test
[TestMethod]
public void Validate_Index_IList_WhatsNew_AS_Model()
{
AppItemController controller = new AppItemController();
// Act
var result = controller.Index();
// Assert
var model = ((ViewResult)result).Model as List<WhatsNew>;
Assert.AreEqual("Index", model.ToList());
}
Error
Assert.AreEqual failed. Expected:<Index (System.String)>. Actual: <System.Collections.Generic.List`1[WCMS.WhatsNew]
You're comparing a string "Index" with a List<WhatsNew>:
Assert.AreEqual("Index", model.ToList());
What exactly are you expecting to happen here?
You can check the contents of the model:
Assert.AreEqual(someValue, model.Count);
Assert.AreEqual(someOtherValue, model[0].SomeProperty);
You can also check the right page is being returned in the action:
Assert.AreEqual("Index", ((ViewResult)result).ViewName);
At the moment, you seem to be trying to mix the two...
You may want to have a read of something like this as a basic introduction to checking your controllers.