I mocked a couple of methods of my Data Access layer, but in some methods the value of an SQL output param is set. How can I mock this ?
Method:
var wrappedParameters = new SqlParameter[3];
wrappedParameters[0] = new SqlParameter("#username",username);
wrappedParameters[1] = new SqlParameter("#password",password);
wrappedParameters[2] = new SqlParameter("returnValue",SqlDbType.Int) { Direction =ParameterDirection.ReturnValue };
dal.ExecuteUsingStoredProcedure("GetUser", wrappedParameters);
Mocking (I tried using "OutRef", but that doesn't work):
using (mocks.Record())
{
Expect.Call(dal.ExecuteUsingStoredProcedure("",> null)).Return(true).IgnoreArguments().OutRef(1);
}
But that didnt work. When I excute the SP GetUser the param return value is set, but I have no idea how to mock this
I think you're going about this the wrong way. Your DAL interface should look like this:
/// <summary>
/// Models a service which holds the user information.
/// </summary>
public interface IUserRepository
{
/// <summary>
/// Gets the user with the given name, or <c>null</c> if no user with
/// that name and password exists.
/// </summary>
/// <exception cref="IOException">
/// An I/O problem occurred while accessing the repository.
/// </exception>
User TryGetUser(string name, string password);
}
The DAL abstraction now hides the fact that a stored procedure is used. In fact, the DAL might not even be a database: it could be a text file on disk, a webservice, a mock or anything else.
Mocking the DAL in order to test code which uses the DAL now becomes trivial. I've chosen the view model (aka presentation model)of a login screen as the system under test in these examples:
[Test]
public void Login_sets_user_and_goes_to_main_screen_when_TryGetUser_not_null()
{
var userRepositoryStub = MockRepository.GenerateStub<IUserRepository>();
var user = new User(...);
userRepositoryStub.Stub(x=>x.GetUserByName("foo","bar")).Return(user);
var sessionStub = MockRepository.GenerateStub<ISession>();
var loginScreenViewModel =
new LoginScreenViewModel(sessionStub, userRepositoryStub);
loginScreenViewModel.UserName = "foo";
loginScreenViewModel.Password = "bar";
loginScreenViewModel.Login();
userRepositoryStub.AssertWasCalled(x=>x.TryGetUser("foo","bar"));
sessionStub.AssertWasCalled(x=>x.ShowMainScreen());
Assert.AreEqual(user, session.User);
}
.
[Test]
public void Login_shows_error_when_TryGetUser_returns_null()
{
var userRepositoryStub = MockRepository.GenerateStub<IUserRepository>();
var sessionStub = MockRepository.GenerateStub<ISession>();
var loginScreenViewModel =
new LoginScreenViewModel(sessionStub, userRepositoryStub);
loginScreenViewModel.UserName = "foo";
loginScreenViewModel.Password = "bar";
loginScreenViewModel.Login();
Assert.AreEqual(loginScreenViewModel.Error,
"User 'foo' does not exist or password is incorrect"));
userRepositoryStub.AssertWasCalled(x=>x.TryGetUser("foo","bar"));
sessionStub.AssertWasNotCalled(x=>x.ShowMainScreen());
Assert.IsNull(session.User);
}
I don't think you can mock it, because it is a normal SqlParameter (concrete class), and its not an out/ref parameter in .Net sense. What I would try is to simply set the value of the parameter in the mocked call to dal (I haven't checked the syntax, but I am sure you get the idea):
using (mocks.Record())
{
Expect.Call(dal.ExecuteUsingStoredProcedure("",null)).Return(true).IgnoreArguments().Do(x => wrappedParameters[2].Value = 1; true);
}
I stubbed this using the "WhenCalled" method.
In my code (to be tested) I first create the connection and command and add three parameters, the third of which is my output parameter. I then call "ExecuteCommand" on this....I won't go through the code for this as I think it's fairly standard (ExecuteCommand takes the command object as its parameter).
In my test, I create a stub for my sql data service and program it so that this sets the value of the parameter:
var sqlService = MockRepository.GenerateStub<ISqlDataService>();
sqlService.Stub(s => s.ExecuteCommand(null))
.IgnoreArguments()
.WhenCalled(s => ((SqlCommand)s.Arguments[0]).Parameters[2].Value = expectedValue)
.Return(0);
The answer's probably a bit late for your project, but hope this helps someone...
Griff
Related
Have I written an untestable method? As the library I am using has an important method which is implemented as an extension method, it seems like I am unable to fake it. And thus, unable to test my method.
First, I'll set out a truncated version of the method I want to test.
Then, I'll set out the attempt I have made to fake it using FakeItEasy.
The method uses caching and it is the call to the static method in the caching library LazyCache which I am struggling to fake:
public async Task<BassRuleEditModel> GetBassRuleEditModel(
int facilityId,
int criteriaId,
int bassRuleId,
BassRuleEditDto bassRuleEditDto)
{
var url = _bassRuleService.GetServiceConnectionForFacility(facilityId).Url;
var dto = bassRuleEditDto ?? _bassRuleService.GetBassRuleEditDto(bassRuleId);
var bassRuleEditModel = new BassRuleEditModel
{
...
LocationList = await GetLocations(url),
...
};
...
return bassRuleEditModel;
}
private async Task<IEnumerable<SelectListItem>> GetLocations(string url)
{
var cacheKey = string.Concat(CacheKeys.Location, url);
var selectList = await _appCache.GetOrAddAsync(cacheKey, async () =>
{
return new SelectList(await _tasksAndPrioritiesService.ReturnLocationsAsync(url), NameProperty, NameProperty);
}
, CacheKeys.DefaultCacheLifetime);
return selectList;
}
It is the GetOrAddAsync method which is an extension method.
I just want the fake to return from the cache an empty SelectList.
Note, the AppCache and all dependencies are injected using constructor injection.
The unit test which I have written, where I have tried to fake the AppCache is:
[Fact]
public async Task Un_Named_Test_Does_Stuff()
{
var url = "http://somesite.com";
var referrer = new Uri(url);
var facilityId = GetRandom.Id();
var serviceConnectionDto = new ServiceConnectionDto
{
Url = "http://google.com" // this url does not matter
};
var cacheKey = string.Concat(CacheKeys.Location, serviceConnectionDto.Url);
A.CallTo(() => _bassRuleService.GetServiceConnectionForFacility(facilityId)).Returns(serviceConnectionDto);
A.CallTo(() => _urlHelper.Content("~/ServiceSpec/ListView")).Returns(url);
A.CallTo(() => _appViewService.GetReferrer(url)).Returns(referrer);
A.CallTo(() => _appCache.GetOrAddAsync(cacheKey, A<Func<Task<SelectList>>>.Ignored))
.Returns(Task.FromResult(new SelectList(Enumerable.Empty<SelectListItem>().ToList())));
var editModel = await
_bassRuleService.GetBassRuleEditModel(GetRandom.Int32(),
GetRandom.Int32(),
GetRandom.Int32(),
null
);
var path = editModel.Referrer.AbsolutePath;
editModel.Referrer.AbsolutePath.ShouldBe(referrer.AbsolutePath);
}
I create the fakes in the constructor of the test (using xUnit):
public BassRuleQueryServiceTests()
{
_currentUser = A.Fake<ICurrentUser>();
_bassRuleService = A.Fake<IBassRuleService>();
_tasksAndPrioritiesService = A.Fake<ITasksAndPrioritiesService>();
_appViewService = A.Fake<IAppViewService>();
_urlHelper = A.Fake<IUrlHelper>();
_applicationDateTime = A.Fake<IApplicationDateTime>();
_appCache = new MockCacheService();
}
The error from running the test is:
Message:
FakeItEasy.Configuration.FakeConfigurationException :
The current proxy generator can not intercept the method LazyCache.AppCacheExtenions.GetOrAddAsync1[Microsoft.AspNetCore.Mvc.Rendering.SelectList](LazyCache.IAppCache cache, System.String key,
System.Func1[System.Threading.Tasks.Task`1[Microsoft.AspNetCore.Mvc.Rendering.SelectList]] addItemFactory) for the following reason:
- Extension methods can not be intercepted since they're static.>
I get the fact that faking a static method is not on. I'm looking for solutions.
Do I need to pressure library authors to not use extension methods? (Facetious question)
Cheers
As you have correctly noted, extensions are static methods, and static methods can't be faked.
Extension methods are often just wrappers to simplify operations on the type they extend; it appears to be the case here. The GetOrAddAsync extension method you're calling ends up calling the IAppCache.GetOrAddAsync method. So you should fake that method instead.
A.CallTo(() => _appCache.GetOrAddAsync(cacheKey, A<Func<ICacheEntry, Task<SelectList>>>.Ignored))
.Returns(new SelectList(Enumerable.Empty<SelectListItem>().ToList()));
It's not very convenient, because it means you need to know what the extension method does, but there's no way around it (short of creating an abstraction layer around the library, but LazyCache is already an abstraction around Microsoft.Extensions.Caching.Memory...)
(btw, you don't need Task.FromResult; the Returns method has an overload that accepts a T when you configure a method returning a Task<T>)
Also, if you're going to return an empty sequence anyway, you don't need to configure the method at all. The default behavior of FakeItEasy will be to return a dummy IEnumerable<SelectListItem> which is empty.
As an alternaive to the excellent answer by #Thomas Levesque, two other alternatives would be:
not to mock the cache at all - use the a real CachingService as it runs in memory and so would be perfectly reasonable to include in the tests.
Use the mock instance MockCachingService cache that ships with LazyCache for this purpose.
See https://github.com/alastairtree/LazyCache/wiki/Unit-testing-code-using-LazyCache for an example.
For few test cases I'm trying to follow a DRY principle, where only the interactions are different with same test case conditions. I'm not able to find a way to implement multiple methods in the interaction { } block.
As mentioned in http://spockframework.org/spock/docs/1.3/interaction_based_testing.html#_explicit_interaction_blocks, I'm using interaction { } in the then: block like below:
Java Code:
// legacy code (still running on EJB 1.0 framework, and no dependency injection involved)
// can't alter java code base
public voidGetData() {
DataService ds = new DataService();
ds = ds.findByOffset(5);
Long len = ds.getOffset() // happy path scenario; missing a null check
// other code
}
// other varieties of same code:
public voidGetData2() {
ItemEJB tmpItem = new ItemEJB();
ItemEJB item = tmpItem.findByOffset(5);
if(null != item) {
Long len = item.getOffset();
// other code
}
}
public voidGetData3() {
ItemEJB item = new ItemEJB().findByOffset(5);
if(null != item) {
Long len = item.getOffset();
// other code
}
}
Spock Test:
def "test scene1"() {
given: "a task"
// other code ommitted
DataService mockObj = Mock(DataService)
when: "take action"
// code omitted
then: "action response"
interaction {
verifyNoDataScenario() // How to add verifyErrorScenario() interaction to the list?
}
}
private verifyDataScenario() {
1 * mockObj.findByOffset(5) >> mockObj // the findByOffset() returns an object, so mapped to same mock instance
1 * mockObj.getOffset() >> 200
}
private verifyErrorScenario() {
1 * mockObj.findByOffset(5) >> null // the findByOffset() returns null
0 * mockObj.getOffset() >> 200 // this won't be executed, and should ie expected to throw NPE
}
The interaction closure doesn't accept more than one method call. I'm not sure if it's design limitation. I believe more can be done in the closure than just mentioning the method name. I also tried interpolating the mockObj as a variable and use data pipe / data table, but since it's referring the same mock instance, it's not working. I'll post that as a separate question.
I ended up repeating the test case twice just to invoke different interaction methods. Down the line I see more scenarios, and wanted to avoid copy & paste approach. Appreciate any pointers to achieve this.
Update:
Modified shared java code as the earlier DataService name was confusing.
As there's no DI involved, and I didn't find a way to mock method variables, so I mock them using PowerMockito, e.g. PowerMockito.whenNew(DataService.class).withNoArguments().thenReturn(mockObj)
Your application code looks very strange. Is the programming style in your legacy application really that bad? First a DataService object is created with a no-arguments constructor, just to be overwritten in the next step by calling a method on that instance which again returns a DataService object. What kind of programmer creates code like that? Or did you just make up some pseudo code which does not have much in common with your real application? Please explain.
As for your test code, it also does not make sense because you instantiate DataService mockObj as a local variable in your feature method (test method), which means that in your helper method mockObj cannot be accessed. So either you need to pass the object as a parameter to the helper methods or you need to make it a field in your test class.
Last, but not least, your local mock object is never injected into the class under test because, as I said in the first paragraph, the DataService object in getData() is also a local variable. Unless your application code is compeletely fake, there is no way to inject the mock because getData() does not have any method parameter and the DataService object is not a field which could be set via setter method or constructor. Thus, you can create as many mocks as you want, the application will never have any knowledge of them. So your stubbing findByOffset(long offset) (why don't you show the code of that method?) has no effect whatsoever.
Bottom line: Please provide an example reflecting the structure of your real code, both application and test code. The snippets you provide do not make any sense, unfortunately. I am trying to help, but like this I cannot.
Update:
In my comments I mentioned refactoring your legacy code for testability by adding a constructor, setter method or an overloaded getData method with an additional parameter. Here is an example of what I mean:
Dummy helper class:
package de.scrum_master.stackoverflow.q58470315;
public class DataService {
private long offset;
public DataService(long offset) {
this.offset = offset;
}
public DataService() {}
public DataService findByOffset(long offset) {
return new DataService(offset);
}
public long getOffset() {
return offset;
}
#Override
public String toString() {
return "DataService{" +
"offset=" + offset +
'}';
}
}
Subject under test:
Let me add a private DataService member with a setter in order to make the object injectable. I am also adding a check if the ds member has been injected or not. If not, the code will behave like before in production and create a new object by itself.
package de.scrum_master.stackoverflow.q58470315;
public class ToBeTestedWithInteractions {
private DataService ds;
public void setDataService(DataService ds) {
this.ds = ds;
}
// legacy code; can't alter
public void getData() {
if (ds == null)
ds = new DataService();
ds = ds.findByOffset(5);
Long len = ds.getOffset();
}
}
Spock test:
Now let us test both the normal and the error scenario. Actually I think you should break it down into two smaller feature methods, but as you seem to wish to test everything (IMO too much) in one method, you can also do that via two distinct pairs of when-then blocks. You do not need to explicitly declare any interaction blocks in order to do so.
package de.scrum_master.stackoverflow.q58470315
import spock.lang.Specification
class RepeatedInteractionsTest extends Specification {
def "test scene1"() {
given: "subject under test with injected mock"
ToBeTestedWithInteractions subjectUnderTest = new ToBeTestedWithInteractions()
DataService dataService = Mock()
subjectUnderTest.dataService = dataService
when: "getting data"
subjectUnderTest.getData()
then: "no error, normal return values"
noExceptionThrown()
1 * dataService.findByOffset(5) >> dataService
1 * dataService.getOffset() >> 200
when: "getting data"
subjectUnderTest.getData()
then: "NPE, only first method called"
thrown NullPointerException
1 * dataService.findByOffset(5) >> null
0 * dataService.getOffset()
}
}
Please also note that testing for exceptions thrown or not thrown adds value to the test, the interaction testing just checks internal legacy code behaviour, which has little to no value.
Most of the answers I have seen on StackOverflow are without using the DateTime object, and are instead using the date() function. This makes them very dirty solutions (overriding date(), mocking a protected function of the subject under test, etc).
Is there a way to mock DateTime, effectively mocking the current date/time?
As an example, here is the code I'd like to test:
public function __construct(UserInterface $user, EntityManager $manager)
{
$this->user = $user;
$this->manager = $manager;
}
public function create(Tunnel $tunnel, $chain, $response)
{
$history = new CommandHistory();
$history->setTunnel($tunnel)
->setCommand($chain)
->setResponse($response)
->setUser($this->user)
;
$this->manager->persist($history);
$this->manager->flush();
}
Here is where I set the date and time in my CommandHistory class:
class CommandHistory
{
// Property definitions...
public function __construct()
{
$this->time = new \DateTime();
}
}
And here is my unit test:
public function testCreate()
{
$user = new User();
$manager = $this->mockManagerWithUser($user);
$tunnel = $this->tunnel;
$chain = 'Commands`Chain';
$response = 'This is the response!';
$creator = new CommandHistoryCreator($user, $manager);
$creator->create($tunnel, $chain, $response);
}
protected function mockManagerWithUser(UserInterface $user)
{
$manager = \Mockery::mock('Doctrine\ORM\EntityManager');
$manager->shouldReceive('persist')->once()->with(\Mockery::on(function(CommandHistory $argument) use ($user) {
return
$argument->getCommand() === 'Commands`Chain'
&& $argument->getResponse() === 'This is the response!'
&& $argument->getTunnel() === $this->tunnel
&& $argument->getUser() === $user
;
}));
$manager->shouldReceive('flush')->once()->withNoArgs();
return $manager;
}
As you can see, I've created a rather long-winded closure only to exclude the comparison of the field that contains the current time, and I feel like this is hurting the readability of my test.
Also, to preserve ease of use for people who are using this class, I don't want to have to make them pass in the current time to the create() function. I believe adding strange behavior to my classes only to make them testable means I'm doing something wrong.
So the standard approach to solving this relies on accepting that in your current implementation you have a static, implicit, undeclared dependency on an object which provides the current time (wrapped in a the new instance of the DateTime object). If you did this with your own code (rather than a class from the framework/language) you would not be able to test easily either.
The solution is to stop using the implicit undeclared dependency and declare your implicit dependency explictly. I would do this by creating a DateTimeProvider (or DateTimeFactory) interface which has a method GetCurrentDateTime. Pass this into your constructor for your CommandHistoryCreator and pass it into the CommandHistory constructor. The CommandHistory will then ask the provider to get the current date time object rather than creating a new one itself, and can carry on as it is.
This will allow you to provider a mock DateTime in your tests and check that the CommandHistory is persisted with the correct DateTime
I am currently having issues with testing a method which my controller uses which is mocked. it has a return type of an specific enum. I am currently always getting back from this mocked method the default enum value, not the value that I have specified it to return. Am i missing something? I have tried both Moq and JustMock lite with the same results. JustMock lite example below.
Hopefully i haven't made any mistakes in copying the code, I have changed all the names of the objects so apologies for that.
Here is part the unit test:
var returnStatus = ExampleEnum.Invalid;
//Mock the client
var client = Mock.Create<ITestInterface>();
Mock.Arrange(() => client.ValidateSomething(Guid.NewGuid()))
.Returns(returnStatus).MustBeCalled();
var testController = new TestController(client);
var result = testController.DoSomething(Guid.NewGuid().ToString()) as ViewResult;
Here are the relevant bits from the controller:
private ITestInterface _client { get; set; }
public TestController(ITestInterface client)
{
_client = client;
}
Here is part of my controller action:
public ActionResult DoSomething(string id)
{
Guid token;
if(!string.IsNullOrEmpty(id) && Guid.TryParse(id, out token))
{
using (var client = _client)
{
ApplicationUser applicationUser;
var status = client.ValidateSomething(token);
switch (status)
{
The client is mocked correctly but the "status" property getting returned is always ExampleEnum.DefaultValue not the value i have specified to be the result.
I hope i have provided enough information. Any help much appreciated.
You probably did your setup wrong.
Guid.NewGuid() returns a new random GUID, so the GUID you use to setup your mock and the GUID you use to call the DoSomething method will never be the same.
You should do something like:
var guid = Guid.NewGuid()
...
Mock.Arrange(() => client.ValidateSomething(guid))
.Returns(returnStatus).MustBeCalled();
...
var result = testController.DoSomething(guid.ToString()) as ViewResult;
using the same GUID for the mock and for the call to DoSomething.
I don't know about JustMock, but with Moq you could also simply use It.IsAny to match all GUIDs:
client.Setup(c => c.ValidateSomething(It.IsAny<Guid>())).Returns(returnStatus);
I am using Moq in my unit test project. Most unit test examples I've seen online end with someMock.VerifyAll(); I wonder if it is OK to assert after VerifyAll(). So for example,
//Arrange
var student = new Student{Id = 0, Name="John Doe", IsRegistered = false};
var studentRepository = new Mock<IStudentRepository>();
var studentService= new StudentService(studentRepository.Object);
//Act
studentService.Register(student); //<-- student.IsRegistered = true now.
//Verify and assert
studentRepository.VerifyAll();
Assert.IsTrue(student.IsRegistered);
Any thought? Thank you.
No you should not use both together in most cases(there are always exceptions). The reason for that is you should be testing only one thing in your test for maintainability, readability and few other reasons. So it should be either Verify(VerifyAll) or Assert in your test and you name your tests accordingly.
Look at Roy Osherove's article about it:
http://osherove.com/blog/2005/4/3/a-unit-test-should-test-only-one-thing.html
VerifyAll is used to make sure certain methods are called and how many times. You use mocks for that.
Assert is used for verifying the result returned from the method you are testing. You use Stubs for that.
Martin fowler has a great article explaining the difference between mocks and stubs. If you understand it you will know the difference better.
http://martinfowler.com/articles/mocksArentStubs.html
UPDATE: example of mock vs stub using Moq as requested in the comment below. I have used Verify but you can use VerifyAll as well.
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
...
[TestClass]
public class UnitTest1
{
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method calls
/// Repository GetName method once when Id is greater than Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_GetNameCalledOnce()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
mockEntityRepository.Verify(
m => m.GetName(It.IsAny<int>()), Times.Once);
}
/// <summary>
/// Test using Mock to Verify that GetNameWithPrefix method
/// doesn't calls Repository GetName method when Id is Zero
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsZero_GetNameNeverCalled()
{
// Arrange
var mockEntityRepository = new Mock<IEntityRepository>();
mockEntityRepository.Setup(m => m.GetName(It.IsAny<int>()));
var entity = new EntityClass(mockEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(0);
// Assert
mockEntityRepository.Verify(
m => m.GetName(It.IsAny<int>()), Times.Never);
}
/// <summary>
/// Test using Stub to Verify that GetNameWithPrefix method
/// returns Name with a Prefix
/// </summary>
[TestMethod]
public void GetNameWithPrefix_IdIsTwelve_ReturnsNameWithPrefix()
{
// Arrange
var stubEntityRepository = new Mock<IEntityRepository>();
stubEntityRepository.Setup(m => m.GetName(It.IsAny<int>()))
.Returns("Stub");
const string EXPECTED_NAME_WITH_PREFIX = "Mr. Stub";
var entity = new EntityClass(stubEntityRepository.Object);
// Act
var name = entity.GetNameWithPrefix(12);
// Assert
Assert.AreEqual(EXPECTED_NAME_WITH_PREFIX, name);
}
}
public class EntityClass
{
private IEntityRepository _entityRepository;
public EntityClass(IEntityRepository entityRepository)
{
this._entityRepository = entityRepository;
}
public string Name { get; set; }
public string GetNameWithPrefix(int id)
{
string name = string.Empty;
if (id > 0)
{
name = this._entityRepository.GetName(id);
}
return "Mr. " + name;
}
}
public interface IEntityRepository
{
string GetName(int id);
}
public class EntityRepository:IEntityRepository
{
public string GetName(int id)
{
// Code to connect to DB and get name based on Id
return "NameFromDb";
}
}
Yes you should call the assert.
VerifyAll() will assert that all SetUp() calls were actually called.
VerifyAll() will not confirm that your student object is registered. Because there are no SetUp() calls in your test case, I think VerifyAll() isn't verifying anything.
I would absolutely expect to see Verify and Assert used side-by-side within a unit test. Asserts are used to validate that properties of your system under test have been set correctly, whereas Verify is used to ensure that any dependencies that your system under test takes in have been called correctly. When using Moq I tend to err on the side of explicitly verifying a setup rather than using the VerifyAll catch-all. That way you can make the intent of the test much clearer.
I'm assuming in the code above that your call to the student repository returns a boolean to state that the student is registered? And you then set that value on the student object? In that case, there is a valuable setup that needs to be added, in which you are effectively saying that when the student repository method is called, it will return true. Then you Assert that student.IsRegistered is true to ensure that you have set the property correctly from the repository return value and you Verify that the repository method is called with the inputs that you are expecting.
There's nothing intrinsically wrong with both asserting and verifying in a mocking test, though assertions that depend on the actual methods being called are likely to fail, because the mock methods do not have the same effects as the real methods.
In your example it is probably fine, as only the repository is mocked, and the change of the student state is presumably done in the service.
Whether both verify and assert should be done in the same test is to a degree a matter of taste. Really the verify is checking that the proper calls to the repository are made, and the assert is checking that the proper change to the entity is made. As these are separate concerns, I'd put them in separate tests, but that may just be me.